Merge "Implement tagging during bulk port creation"
This commit is contained in:
commit
520f7cb4a0
24
neutron/extensions/tag_ports_during_bulk_creation.py
Normal file
24
neutron/extensions/tag_ports_during_bulk_creation.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Copyright (c) 2019 Verizon Media
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from neutron_lib.api.definitions import \
|
||||||
|
tag_ports_during_bulk_creation as apidef
|
||||||
|
from neutron_lib.api import extensions
|
||||||
|
|
||||||
|
|
||||||
|
class Tag_ports_during_bulk_creation(extensions.APIExtensionDescriptor):
|
||||||
|
"""Extension to tag ports during bulk creation."""
|
||||||
|
|
||||||
|
api_definition = apidef
|
@ -12,7 +12,9 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import abc
|
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 extensions as api_extensions
|
||||||
from neutron_lib.api import faults
|
from neutron_lib.api import faults
|
||||||
from neutron_lib.api import validators
|
from neutron_lib.api import validators
|
||||||
@ -48,6 +50,12 @@ TAG_ATTRIBUTE_MAP = {
|
|||||||
NOT_TAGS_ANY: {'allow_post': False, 'allow_put': False,
|
NOT_TAGS_ANY: {'allow_post': False, 'allow_put': False,
|
||||||
'is_visible': False, 'is_filter': True},
|
'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):
|
class TagResourceNotFound(exceptions.NotFound):
|
||||||
@ -210,6 +218,10 @@ class Tagging(api_extensions.ExtensionDescriptor):
|
|||||||
return {}
|
return {}
|
||||||
EXTENDED_ATTRIBUTES_2_0 = {}
|
EXTENDED_ATTRIBUTES_2_0 = {}
|
||||||
for collection_name in TAG_SUPPORTED_RESOURCES:
|
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
|
EXTENDED_ATTRIBUTES_2_0[collection_name] = TAG_ATTRIBUTE_MAP
|
||||||
return EXTENDED_ATTRIBUTES_2_0
|
return EXTENDED_ATTRIBUTES_2_0
|
||||||
|
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
# Copyright (c) 2019 Verizon Media
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from neutron_lib.api.definitions import \
|
||||||
|
tag_ports_during_bulk_creation as apidef
|
||||||
|
from neutron_lib.plugins import directory
|
||||||
|
from neutron_lib.plugins.ml2 import api
|
||||||
|
from oslo_log import helpers as log_helpers
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from neutron.extensions import tagging
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TagPortsDuringBulkCreationExtensionDriver(api.ExtensionDriver):
|
||||||
|
_supported_extension_alias = apidef.ALIAS
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
LOG.info("TagPortsDuringBulkCreationExtensionDriver "
|
||||||
|
"initialization complete")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extension_alias(self):
|
||||||
|
return self._supported_extension_alias
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tag_plugin(self):
|
||||||
|
if not hasattr(self, '_tag_plugin'):
|
||||||
|
self._tag_plugin = directory.get_plugin(tagging.TAG_PLUGIN_TYPE)
|
||||||
|
return self._tag_plugin
|
||||||
|
|
||||||
|
@property
|
||||||
|
def plugin(self):
|
||||||
|
if not hasattr(self, '_plugin'):
|
||||||
|
self._plugin = directory.get_plugin()
|
||||||
|
return self._plugin
|
||||||
|
|
||||||
|
@log_helpers.log_method_call
|
||||||
|
def process_create_port(self, plugin_context, request_data, db_data):
|
||||||
|
tags = request_data.get('tags')
|
||||||
|
if not (self.tag_plugin and tags):
|
||||||
|
return
|
||||||
|
port_db = self.plugin._get_port(plugin_context, db_data['id'])
|
||||||
|
self.tag_plugin.add_tags(plugin_context, port_db.standard_attr_id,
|
||||||
|
tags)
|
@ -90,11 +90,14 @@ class TagPlugin(tagging.TagPluginBase):
|
|||||||
if tag_db.tag in tags_removed
|
if tag_db.tag in tags_removed
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
for tag in tags_added:
|
self.add_tags(context, res.standard_attr_id, tags_added)
|
||||||
tag_obj.Tag(context, standard_attr_id=res.standard_attr_id,
|
|
||||||
tag=tag).create()
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
def add_tags(self, context, standard_attr_id, tags):
|
||||||
|
for tag in tags:
|
||||||
|
tag_obj.Tag(context, standard_attr_id=standard_attr_id,
|
||||||
|
tag=tag).create()
|
||||||
|
|
||||||
@log_helpers.log_method_call
|
@log_helpers.log_method_call
|
||||||
def update_tag(self, context, resource, resource_id, tag):
|
def update_tag(self, context, resource, resource_id, tag):
|
||||||
res = self._get_resource(context, resource, resource_id)
|
res = self._get_resource(context, resource, resource_id)
|
||||||
|
@ -63,6 +63,7 @@ NETWORK_API_EXTENSIONS+=",standard-attr-timestamp"
|
|||||||
NETWORK_API_EXTENSIONS+=",standard-attr-tag"
|
NETWORK_API_EXTENSIONS+=",standard-attr-tag"
|
||||||
NETWORK_API_EXTENSIONS+=",subnet_allocation"
|
NETWORK_API_EXTENSIONS+=",subnet_allocation"
|
||||||
NETWORK_API_EXTENSIONS+=",subnet-dns-publish-fixed-ip"
|
NETWORK_API_EXTENSIONS+=",subnet-dns-publish-fixed-ip"
|
||||||
|
NETWORK_API_EXTENSIONS+=",tag-ports-during-bulk-creation"
|
||||||
NETWORK_API_EXTENSIONS+=",trunk"
|
NETWORK_API_EXTENSIONS+=",trunk"
|
||||||
NETWORK_API_EXTENSIONS+=",trunk-details"
|
NETWORK_API_EXTENSIONS+=",trunk-details"
|
||||||
NETWORK_API_EXTENSIONS+=",uplink-status-propagation"
|
NETWORK_API_EXTENSIONS+=",uplink-status-propagation"
|
||||||
|
@ -0,0 +1,129 @@
|
|||||||
|
# Copyright (c) 2019 Verizon Media
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 copy
|
||||||
|
|
||||||
|
import mock
|
||||||
|
from neutron_lib.plugins import directory
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from neutron.plugins.ml2.extensions import tag_ports_during_bulk_creation
|
||||||
|
from neutron.tests.unit.plugins.ml2 import test_plugin
|
||||||
|
|
||||||
|
|
||||||
|
TAGS = [
|
||||||
|
['tag-1', 'tag-2', 'tag-3'],
|
||||||
|
['tag-1', 'tag-2'],
|
||||||
|
['tag-1', 'tag-3'],
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TagPortsDuringBulkCreationTestCase(test_plugin.Ml2PluginV2TestCase):
|
||||||
|
_extension_drivers = ['tag_ports_during_bulk_creation']
|
||||||
|
fmt = 'json'
|
||||||
|
|
||||||
|
def get_additional_service_plugins(self):
|
||||||
|
p = super(TagPortsDuringBulkCreationTestCase,
|
||||||
|
self).get_additional_service_plugins()
|
||||||
|
p.update({'tag_name': 'tag'})
|
||||||
|
return p
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
cfg.CONF.set_override('extension_drivers',
|
||||||
|
self._extension_drivers,
|
||||||
|
group='ml2')
|
||||||
|
super(TagPortsDuringBulkCreationTestCase, self).setUp()
|
||||||
|
self.plugin = directory.get_plugin()
|
||||||
|
|
||||||
|
def test_create_ports_bulk_with_tags(self):
|
||||||
|
num_ports = 3
|
||||||
|
tenant_id = 'some_tenant'
|
||||||
|
with self.network(tenant_id=tenant_id) as network_to_use:
|
||||||
|
net_id = network_to_use['network']['id']
|
||||||
|
port = {'port': {'network_id': net_id,
|
||||||
|
'admin_state_up': True,
|
||||||
|
'tenant_id': tenant_id}}
|
||||||
|
ports = [copy.deepcopy(port) for x in range(num_ports)]
|
||||||
|
ports_tags_map = {}
|
||||||
|
for port, tags in zip(ports, TAGS):
|
||||||
|
port['port']['tags'] = tags
|
||||||
|
port['port']['name'] = '-'.join(tags)
|
||||||
|
ports_tags_map[port['port']['name']] = tags
|
||||||
|
req_body = {'ports': ports}
|
||||||
|
ports_req = self.new_create_request('ports', req_body)
|
||||||
|
res = ports_req.get_response(self.api)
|
||||||
|
self.assertEqual(201, res.status_int)
|
||||||
|
created_ports = self.deserialize(self.fmt, res)
|
||||||
|
|
||||||
|
for port in created_ports['ports']:
|
||||||
|
self.assertEqual(ports_tags_map[port['name']], port['tags'])
|
||||||
|
|
||||||
|
def test_create_ports_bulk_no_tags(self):
|
||||||
|
num_ports = 2
|
||||||
|
tenant_id = 'some_tenant'
|
||||||
|
with self.network(tenant_id=tenant_id) as network_to_use:
|
||||||
|
net_id = network_to_use['network']['id']
|
||||||
|
port = {'port': {'name': 'port',
|
||||||
|
'network_id': net_id,
|
||||||
|
'admin_state_up': True,
|
||||||
|
'tenant_id': tenant_id}}
|
||||||
|
ports = [copy.deepcopy(port) for x in range(num_ports)]
|
||||||
|
req_body = {'ports': ports}
|
||||||
|
ports_req = self.new_create_request('ports', req_body)
|
||||||
|
res = ports_req.get_response(self.api)
|
||||||
|
self.assertEqual(201, res.status_int)
|
||||||
|
created_ports = self.deserialize(self.fmt, res)
|
||||||
|
for port in created_ports['ports']:
|
||||||
|
self.assertFalse(port['tags'])
|
||||||
|
|
||||||
|
def test_create_port_with_tags(self):
|
||||||
|
tenant_id = 'some_tenant'
|
||||||
|
with self.network(tenant_id=tenant_id) as network_to_use:
|
||||||
|
net_id = network_to_use['network']['id']
|
||||||
|
req_body = {'port': {'name': 'port',
|
||||||
|
'network_id': net_id,
|
||||||
|
'admin_state_up': True,
|
||||||
|
'tenant_id': tenant_id,
|
||||||
|
'tags': TAGS[0]}}
|
||||||
|
port_req = self.new_create_request('ports', req_body)
|
||||||
|
res = port_req.get_response(self.api)
|
||||||
|
self.assertEqual(201, res.status_int)
|
||||||
|
created_port = self.deserialize(self.fmt, res)
|
||||||
|
self.assertEqual(TAGS[0], created_port['port']['tags'])
|
||||||
|
|
||||||
|
def test_type_args_passed_to_extension(self):
|
||||||
|
num_ports = 2
|
||||||
|
tenant_id = 'some_tenant'
|
||||||
|
extension = tag_ports_during_bulk_creation
|
||||||
|
with mock.patch.object(
|
||||||
|
extension.TagPortsDuringBulkCreationExtensionDriver,
|
||||||
|
'process_create_port') as patched_method:
|
||||||
|
with self.network(tenant_id=tenant_id) as network_to_use:
|
||||||
|
net_id = network_to_use['network']['id']
|
||||||
|
port = {'port': {'network_id': net_id,
|
||||||
|
'admin_state_up': True,
|
||||||
|
'tenant_id': tenant_id}}
|
||||||
|
ports = [copy.deepcopy(port) for x in range(num_ports)]
|
||||||
|
ports[0]['port']['tags'] = TAGS[0]
|
||||||
|
ports[1]['port']['tags'] = TAGS[1]
|
||||||
|
req_body = {'ports': ports}
|
||||||
|
ports_req = self.new_create_request('ports', req_body)
|
||||||
|
res = ports_req.get_response(self.api)
|
||||||
|
self.assertEqual(201, res.status_int)
|
||||||
|
self.assertIsInstance(patched_method.call_args.args[1],
|
||||||
|
dict)
|
||||||
|
self.assertIsInstance(patched_method.call_args.args[2],
|
||||||
|
dict)
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- The ``tag_ports_during_bulk_creation`` ML2 plugin extension has been
|
||||||
|
implemented to support tagging ports during bulk creation. As a side
|
||||||
|
effect, this extension also allows tagging ports during non-bulk creation.
|
@ -107,6 +107,7 @@ neutron.ml2.extension_drivers =
|
|||||||
data_plane_status = neutron.plugins.ml2.extensions.data_plane_status:DataPlaneStatusExtensionDriver
|
data_plane_status = neutron.plugins.ml2.extensions.data_plane_status:DataPlaneStatusExtensionDriver
|
||||||
dns_domain_ports = neutron.plugins.ml2.extensions.dns_integration:DNSDomainPortsExtensionDriver
|
dns_domain_ports = neutron.plugins.ml2.extensions.dns_integration:DNSDomainPortsExtensionDriver
|
||||||
uplink_status_propagation = neutron.plugins.ml2.extensions.uplink_status_propagation:UplinkStatusPropagationExtensionDriver
|
uplink_status_propagation = neutron.plugins.ml2.extensions.uplink_status_propagation:UplinkStatusPropagationExtensionDriver
|
||||||
|
tag_ports_during_bulk_creation = neutron.plugins.ml2.extensions.tag_ports_during_bulk_creation:TagPortsDuringBulkCreationExtensionDriver
|
||||||
subnet_dns_publish_fixed_ip = neutron.plugins.ml2.extensions.subnet_dns_publish_fixed_ip:SubnetDNSPublishFixedIPExtensionDriver
|
subnet_dns_publish_fixed_ip = neutron.plugins.ml2.extensions.subnet_dns_publish_fixed_ip:SubnetDNSPublishFixedIPExtensionDriver
|
||||||
neutron.ipam_drivers =
|
neutron.ipam_drivers =
|
||||||
fake = neutron.tests.unit.ipam.fake_driver:FakeDriver
|
fake = neutron.tests.unit.ipam.fake_driver:FakeDriver
|
||||||
|
Loading…
x
Reference in New Issue
Block a user