Metadata for all user facing resources

Launchpad blueprint:

https://blueprints.launchpad.net/manila/+spec/metadata-for-share-resources

Manila stores information regarding user-owned tangible technical assets in its database as resources. These assets include file shares, snapshots, access control rules, export locations, share networks and security services among others. Resources are designed to accommodate distinguishable identifiers geared at machines and humans such as ID, Name and Description. However these identifiers may be insufficient or inflexible to end users’ needs such as being able to tag resources to a purpose, or attach labels that drive some automation. This specification proposes a design allowing for user modifiable metadata on all user facing resources.

Problem description

End users interact with manila API to manage their shared file system storage resources. These resources include:

  • Shares

  • Share Export Locations

  • Share Access Rules

  • Share Instances

  • Share Instance Export Locations

  • Share Replicas

  • Share Replica Export Locations

  • Share Snapshots

  • Share Groups

  • Share Group Snapshots

  • Security Services

  • Share Networks

  • Share Network Subnets

All of these resources are identifiable by ID. Some of them even allow setting free form text as Name and/or Description. Free form text is usually hard to construct, breakdown or query, so these fields are mostly useful to humans rather than machines. Many times, users want to be able to store much more auxiliary contextual information.

One way they do that today with shares and access rules is with the help of “metadata”. Metadata is a term used to describe auxiliary information associated with some data. For shares and access rules, users today use metadata as flexible key=value pairs to attach useful optional/additional information. This sort of interaction isn’t possible with the other resources enumerated above.

Metadata isn’t just helpful for user interactions. It could also provide additional representational space for manila itself. For example, the service currently uses database objects to represent export location metadata to relay helpful hints regarding the nature of specific exports to end users, such as the preference and accessibility. This metadata is owned by the service, and end users cannot modify this metadata.

Use Cases

Users may want to use metadata to store contextual information such as:

  • the purpose of the resource, e.g.: “usedfor=fileserver”

  • details regarding the provisioner, e.g.: “createdby=manila-csi”

  • grouping information, e.g.: “department=physics”

  • additional chronological information, e.g.: “last_audited=2020-12-24T00:22:29”

  • mount point tag for clients, e.g.: “fstag=website”

Metadata interactions are expected to be uniform and consistent across resources, which makes it easier for building automation on top of these APIs.

Proposed change

A new metadata controller mixin will be created in manila’s API modules that can be inherited by all the user facing API controllers. This mixin will implement API handler code to:

  • get resource metadata (retrieve either all of it, or specific item)

  • set/unset all resource metadata (create, update or delete metadata items as key=value pairs)

  • set a single resource metadata item (update a specific key=value pair)

  • unset resource metadata item (delete a specific key=value pair)

To distinguish and protect service owned or admin only metadata, there will be an ignore_keys parameter and ignored keys dictionary to prevent these metadata items from being manipulated by the end user.

Alternatives

Alternatives to key-value metadata include Name and Description fields. Resources that don’t currently support name and/or description can be updated to include these fields. This alternative suffers from the inflexibility problems described above and doesn’t cover all the use cases.

We could also just add metadata controllers one at a time, based on user feedback instead of adding metadata controllers for each user facing resource. This also allows us to test each metadata controller in isolation and build incrementally. This is pretty non-controversial, however, each resource API update will have to be made in a new microversion, and doing things one at a time instead of using the inheritence pattern to subsume common code will be costlier. Testing effort is significant, and unavoidable.

Instead of distinguishing “user” owned and “service” owned metadata, we could allow all metadata to be modifiable by end users - even data that’s created by the service. Doing so would simplify user interactions, however, it may harm the service and cause misbehavior.

Another option to including a user_modifiable boolean field to metadata is to introduce granular RBAC. However, this may produce too many RBAC rules that may be unnecessary. If this turns out to be a use case, it is possible to easily build on top of the proposed design.

Data model impact

To store resource metadata, new tables and corresponding ORM models are necessary. These tables will be created during a database upgrade, and nullified and destroyed during a database downgrade.

Existing metadata tables for Shares (“share_metadata”), Export Locations (“share_instance_export_locations_metadata”) will be modified to include a string attribute called deleted. The default value for this attribute is set to False.

Existing metadata tables will also be modified to replace the datatype of the “id” field to a string of length 36 for UUIDs. This will be done to avoid integer overflows and provide scalability.

A general ORM schema for a metadata table will be as follows (where ‘Resource’ is a stand-in for the real resource name):

class ResourceMetadata(BASE, ManilaBase):
  """Represents a metadata key/value pair for a Resource."""
  __tablename__ = 'resource_metadata'
  id = Column(String(36), primary_key=True)
  key = Column(String(255), nullable=False)
  value = Column(String(1023), nullable=False)
  resource_id = Column(String(36), ForeignKey('resources.id'), nullable=False)
  deleted = Column(String(36), default='False')
  resource = orm.relationship(Resource, backref="resource_metadata",
                              foreign_keys=resource_id,
                              primaryjoin='and_('
                              'ResourceMetadata.resource_id == Resource.id,'
                              'ResourceMetadata.deleted == "False)')

Metadata items are not soft deleted when they are unset by the service or by end users. The metadata table is not loaded alongside the resource unless the resource has been queried with metadata, or a detailed view of the resource has been requested.

REST API impact

New API endpoints will be created to index metadata, show metadata item, create metadata, update metadata item, update_all metadata (delete all existing metadata and update with requested metadata), and delete metadata item for each resource. The general structure of these APIs is as follows:

Index Metadata

Retrieve all metadata key=value pairs as JSON:

GET /v2/{resource}/metadata
  • Sample request body: null

  • Success Codes: 200

  • Default API policy role: Project Reader

  • Error Codes: 401 (Unauthorized), 403 (Policy Not Authorized), 404 (Invalid resource)

  • Sample response body:

    {
       "metadata": {
           "project": "my_app",
           "aim": "doc"
       }
    }
    

Show specific metadata item

Retrieve a single metadata key=value pair:

GET /v2/{resource}/metadata/{key}
  • Sample request body: null

  • Success Codes: 200

  • Default API policy role: Project Reader

  • Error Codes: 401 (Unauthorized), 403 (Policy Not Authorized), 404 (Invalid resource)

  • Sample response body:

    {
       "metadata": {
           "project": "my_app",
       }
    }
    

Update all metadata

Replace all metadata with the updated set, can also be used to delete all metadata:

PUT /v2/{resource}/metadata
  • Sample request body:

    {
        "metadata": {
           "aim": "changed_doc",
           "project": "my_app",
           "new_metadata_key": "new_information"
        }
    }
    
  • Success Codes: 200

  • Default API policy role: Project Member

  • Error Codes: 401 (Unauthorized), 403 (Policy Not Authorized), 404 (Invalid resource), 400 (Malformed request)

  • Sample response body:

    {
       "metadata": {
          "aim": "changed_doc",
          "project": "my_app",
          "new_metadata_key": "new_information"
       }
    }
    

Note: Metadata keys that are part of the admin-only dictionary will not be deleted and updated if the user requesting this is not an system or project admin.

Update specific metadata item

Update a specific metadata item, leaving the rest unmodified:

POST /v2/{resource}/metadata/{key}
  • Sample request body:

    {
       "metadata": {
          "aim": "updated_doc",
       }
    }
    
  • Success Codes: 200

  • Default API policy role: Project Member

  • Error Codes: 401 (Unauthorized), 403 (Policy Not Authorized), 404 (Invalid resource), 400 (Malformed request)

  • Sample response body:

    {
       "metadata": {
          "aim": "updated_doc",
          "project": "my_app",
          "new_metadata_key": "new_information"
       }
    }
    

Important

Currently, the POST /v2/{share}/metadata API currently expects a meta object. However, the other metadata APIs expect a metadata object. For the sake of consistency, this error will be fixed in a new API microversion.

Delete specific metadata item

Hard delete a single metadata key=value pair:

DELETE /v2/{resource}/metadata/{key}
  • Sample request body: null

  • Success Codes: 200

  • Default API policy role: Project Member

  • Error Codes: 401 (Unauthorized), 403 (Policy Not Authorized), 404 (Invalid resource)

  • Sample response body: null

Query resources by metadata items

URL encoding can be performed by the client:

GET /v2/{resource}?metadata=%7B%27foo%27%3A%27bar%27%2C%27clemson%27%3A%27tigers%27%7D

or the request can be made in a decoded format as well:

GET /v2/{resource}?metadata={'foo':'bar','clemson':'tigers'}

Driver impact

None. Metadata manipulation is directly performed on the manila database and shared file system back end drivers are not invoked during the creation, modification or deletion of resource metadata.

Security impact

It is advised that metadata operations are rate limited to prevent bad actors or automation from adding a large number of metadata items to a resource.

Notifications impact

None

Other end user impact

Python-manilaclient SDK will include support for the new APIs and we’ll ensure that there are corresponding CLI commands in the new OSC plugin shell. Manila UI’s support for share, export location and access rule metadata is limited. This specification doesn’t seek to address all the UI gaps; but all effort will be made to close the feature parity between the CLI utilities and the UI. Eventually users will be able to perform all metadata interactions via the UI as well.

Performance Impact

API performance is bound to suffer when resource queries include metadata items. Since we’ll be providing metadata along with detail retrievals of resources, the performance of those APIs will also be affected negatively because of the new database joins that will be necessary. Over time, as the number of shares and the metadata tables grow, performance degradation can be severe. This impact will be documented; as best practice, it is recommended that a resource have no more than a handful of metadata items.

Other deployer impact

New APIs introduced may require tweaking of policy files if the default RBAC policy is not acceptable.

Developer impact

When adding any new user facing metadata, the metadata mixin controller can be inherited and extended by developers.

Implementation

Assignee(s)

Primary assignee:

ashrod98 <ashrod98@gmail.com>

Other contributors:

gouthamr

Work Items

  • Add database migration to convert the id field of share, export location and access rule metadata to a string from integer and populate the field with UUIDs

  • Add database migrations to introduce the “deleted” field to share, export location and access rule metadata tables.

  • Add database migrations to create new metadata tables for all other resources

  • Add MetadataControllerMixin, inherit and extend in all resources and bump up the API microversion.

  • Add unit and integration tests

  • Add support for metadata APIs in manilaclient SDK, and OSC CLI and SDK

  • Add support for metadata interactions in the UI

  • Add documentation

A further enhancement to this API would be to provide an interface for administrators to create admin-only metadata values. Currently, admin-only metadata values are delinated in a dictionary in manila/manila/common/constants.py. Such a fixated list is sufficient for the implementation of backend filtering for scheduling according to share affinities. To implement this customization, we could revisit adjusting the data-model to include an admin-only boolean value identifying which keys are adjustable by admins or non-admins. Or we could provide some configuration option in manila/manila/data/. Such an enhancement requires further thought, and can be implemented at a later point.

Dependencies

Not a direct dependency, but this API change incorporates the metadata changes necessary to implement this spec: Affinity and anti-affinity scheduler filter

Testing

Extensive unit tests will be written to test the API and database methods being added. A database “walk migrations” test will be added for all the database changes. Tempest tests will be added to cover the new metadata operations across resources.

Documentation Impact

  • API reference

  • End user guide

  • Release notes

References