35c7999ebd
In TripleO we are moving to a model where heat is not used to manage neutron resources. This work relies on using tags, and we are hitting the 60 characther limit. This change bumps the tag elements to 255 characters. Closes-Bug: #1921713 Change-Id: Ie69526acb94b62fd5d8db1dbddc1f24072df7a5e
261 lines
9.2 KiB
Python
261 lines
9.2 KiB
Python
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import abc
|
|
import copy
|
|
|
|
from neutron_lib.api.definitions import port
|
|
from neutron_lib.api import extensions as api_extensions
|
|
from neutron_lib.api import faults
|
|
from neutron_lib.api import validators
|
|
from neutron_lib import exceptions
|
|
from neutron_lib.plugins import directory
|
|
from neutron_lib import rpc as n_rpc
|
|
from neutron_lib.services import base as service_base
|
|
import webob.exc
|
|
|
|
from neutron._i18n import _
|
|
from neutron.api import extensions
|
|
from neutron.api.v2 import resource as api_resource
|
|
from neutron.db import standard_attr
|
|
|
|
|
|
TAG = 'tag'
|
|
TAGS = TAG + 's'
|
|
TAGS_ANY = TAGS + '-any'
|
|
NOT_TAGS = 'not-' + TAGS
|
|
NOT_TAGS_ANY = NOT_TAGS + '-any'
|
|
MAX_TAG_LEN = 255
|
|
TAG_PLUGIN_TYPE = 'TAG'
|
|
|
|
TAG_SUPPORTED_RESOURCES = standard_attr.get_tag_resource_parent_map()
|
|
TAG_ATTRIBUTE_MAP = {
|
|
TAGS: {'allow_post': False, 'allow_put': False,
|
|
'is_visible': True, 'is_filter': True},
|
|
TAGS_ANY: {'allow_post': False, 'allow_put': False,
|
|
'is_visible': False, 'is_filter': True},
|
|
NOT_TAGS: {'allow_post': False, 'allow_put': False,
|
|
'is_visible': False, 'is_filter': True},
|
|
NOT_TAGS_ANY: {'allow_post': False, 'allow_put': False,
|
|
'is_visible': False, 'is_filter': True},
|
|
}
|
|
TAG_ATTRIBUTE_MAP_PORTS = copy.deepcopy(TAG_ATTRIBUTE_MAP)
|
|
TAG_ATTRIBUTE_MAP_PORTS[TAGS] = {
|
|
'allow_post': True, 'allow_put': False,
|
|
'validate': {'type:list_of_unique_strings': MAX_TAG_LEN},
|
|
'default': [], 'is_visible': True, 'is_filter': True
|
|
}
|
|
|
|
|
|
class TagResourceNotFound(exceptions.NotFound):
|
|
message = _("Resource %(resource)s %(resource_id)s could not be found.")
|
|
|
|
|
|
class TagNotFound(exceptions.NotFound):
|
|
message = _("Tag %(tag)s could not be found.")
|
|
|
|
|
|
def validate_tag(tag):
|
|
msg = validators.validate_string(tag, MAX_TAG_LEN)
|
|
if msg:
|
|
raise exceptions.InvalidInput(error_message=msg)
|
|
|
|
|
|
def validate_tags(body):
|
|
if not isinstance(body, dict) or 'tags' not in body:
|
|
raise exceptions.InvalidInput(error_message=_("Invalid tags body"))
|
|
msg = validators.validate_list_of_unique_strings(body['tags'], MAX_TAG_LEN)
|
|
if msg:
|
|
raise exceptions.InvalidInput(error_message=msg)
|
|
|
|
|
|
def notify_tag_action(context, action, parent, parent_id, tags=None):
|
|
notifier = n_rpc.get_notifier('network')
|
|
tag_event = 'tag.%s' % action
|
|
# TODO(hichihara): Add 'updated_at' into payload
|
|
payload = {'parent_resource': parent,
|
|
'parent_resource_id': parent_id}
|
|
if tags is not None:
|
|
payload['tags'] = tags
|
|
notifier.info(context, tag_event, payload)
|
|
|
|
|
|
class TaggingController(object):
|
|
def __init__(self):
|
|
self.plugin = directory.get_plugin(TAG_PLUGIN_TYPE)
|
|
self.supported_resources = TAG_SUPPORTED_RESOURCES
|
|
|
|
def _get_parent_resource_and_id(self, kwargs):
|
|
for key in kwargs:
|
|
for resource in self.supported_resources:
|
|
if key == self.supported_resources[resource] + '_id':
|
|
return resource, kwargs[key]
|
|
return None, None
|
|
|
|
def index(self, request, **kwargs):
|
|
# GET /v2.0/networks/{network_id}/tags
|
|
parent, parent_id = self._get_parent_resource_and_id(kwargs)
|
|
return self.plugin.get_tags(request.context, parent, parent_id)
|
|
|
|
def show(self, request, id, **kwargs):
|
|
# GET /v2.0/networks/{network_id}/tags/{tag}
|
|
# id == tag
|
|
validate_tag(id)
|
|
parent, parent_id = self._get_parent_resource_and_id(kwargs)
|
|
return self.plugin.get_tag(request.context, parent, parent_id, id)
|
|
|
|
def create(self, request, **kwargs):
|
|
# not supported
|
|
# POST /v2.0/networks/{network_id}/tags
|
|
raise webob.exc.HTTPNotFound("not supported")
|
|
|
|
def update(self, request, id, **kwargs):
|
|
# PUT /v2.0/networks/{network_id}/tags/{tag}
|
|
# id == tag
|
|
validate_tag(id)
|
|
parent, parent_id = self._get_parent_resource_and_id(kwargs)
|
|
notify_tag_action(request.context, 'create.start',
|
|
parent, parent_id, [id])
|
|
result = self.plugin.update_tag(request.context, parent, parent_id, id)
|
|
notify_tag_action(request.context, 'create.end',
|
|
parent, parent_id, [id])
|
|
return result
|
|
|
|
def update_all(self, request, body, **kwargs):
|
|
# PUT /v2.0/networks/{network_id}/tags
|
|
# body: {"tags": ["aaa", "bbb"]}
|
|
validate_tags(body)
|
|
parent, parent_id = self._get_parent_resource_and_id(kwargs)
|
|
notify_tag_action(request.context, 'update.start',
|
|
parent, parent_id, body['tags'])
|
|
result = self.plugin.update_tags(request.context, parent,
|
|
parent_id, body)
|
|
notify_tag_action(request.context, 'update.end',
|
|
parent, parent_id, body['tags'])
|
|
return result
|
|
|
|
def delete(self, request, id, **kwargs):
|
|
# DELETE /v2.0/networks/{network_id}/tags/{tag}
|
|
# id == tag
|
|
validate_tag(id)
|
|
parent, parent_id = self._get_parent_resource_and_id(kwargs)
|
|
notify_tag_action(request.context, 'delete.start',
|
|
parent, parent_id, [id])
|
|
result = self.plugin.delete_tag(request.context, parent, parent_id, id)
|
|
notify_tag_action(request.context, 'delete.end',
|
|
parent, parent_id, [id])
|
|
return result
|
|
|
|
def delete_all(self, request, **kwargs):
|
|
# DELETE /v2.0/networks/{network_id}/tags
|
|
parent, parent_id = self._get_parent_resource_and_id(kwargs)
|
|
notify_tag_action(request.context, 'delete_all.start',
|
|
parent, parent_id)
|
|
result = self.plugin.delete_tags(request.context, parent, parent_id)
|
|
notify_tag_action(request.context, 'delete_all.end',
|
|
parent, parent_id)
|
|
return result
|
|
|
|
|
|
class Tagging(api_extensions.ExtensionDescriptor):
|
|
"""Extension class supporting tags."""
|
|
|
|
@classmethod
|
|
def get_name(cls):
|
|
return ("Tag support for resources with standard attribute: %s"
|
|
% ', '.join(TAG_SUPPORTED_RESOURCES.values()))
|
|
|
|
@classmethod
|
|
def get_alias(cls):
|
|
return "standard-attr-tag"
|
|
|
|
@classmethod
|
|
def get_description(cls):
|
|
return "Enables to set tag on resources with standard attribute."
|
|
|
|
@classmethod
|
|
def get_updated(cls):
|
|
return "2017-01-01T00:00:00-00:00"
|
|
|
|
def get_required_extensions(self):
|
|
return []
|
|
|
|
@classmethod
|
|
def get_resources(cls):
|
|
"""Returns Ext Resources."""
|
|
exts = []
|
|
action_status = {'index': 200, 'show': 204, 'update': 201,
|
|
'update_all': 200, 'delete': 204, 'delete_all': 204}
|
|
controller = api_resource.Resource(TaggingController(),
|
|
faults.FAULT_MAP,
|
|
action_status=action_status)
|
|
collection_methods = {"delete_all": "DELETE",
|
|
"update_all": "PUT"}
|
|
exts = []
|
|
for collection_name, member_name in TAG_SUPPORTED_RESOURCES.items():
|
|
if 'security_group' in collection_name:
|
|
collection_name = collection_name.replace('_', '-')
|
|
parent = {'member_name': member_name,
|
|
'collection_name': collection_name}
|
|
exts.append(extensions.ResourceExtension(
|
|
TAGS, controller, parent,
|
|
collection_methods=collection_methods))
|
|
return exts
|
|
|
|
def get_extended_resources(self, version):
|
|
if version != "2.0":
|
|
return {}
|
|
EXTENDED_ATTRIBUTES_2_0 = {}
|
|
for collection_name in TAG_SUPPORTED_RESOURCES:
|
|
if collection_name == port.COLLECTION_NAME:
|
|
EXTENDED_ATTRIBUTES_2_0[collection_name] = (
|
|
TAG_ATTRIBUTE_MAP_PORTS)
|
|
else:
|
|
EXTENDED_ATTRIBUTES_2_0[collection_name] = TAG_ATTRIBUTE_MAP
|
|
return EXTENDED_ATTRIBUTES_2_0
|
|
|
|
|
|
class TagPluginBase(service_base.ServicePluginBase, metaclass=abc.ABCMeta):
|
|
"""REST API to operate the Tag."""
|
|
|
|
def get_plugin_description(self):
|
|
return "Tag support"
|
|
|
|
@classmethod
|
|
def get_plugin_type(cls):
|
|
return TAG_PLUGIN_TYPE
|
|
|
|
@abc.abstractmethod
|
|
def get_tags(self, context, resource, resource_id):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def get_tag(self, context, resource, resource_id, tag):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def update_tags(self, context, resource, resource_id, body):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def update_tag(self, context, resource, resource_id, tag):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def delete_tags(self, context, resource, resource_id):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def delete_tag(self, context, resource, resource_id, tag):
|
|
pass
|