NSXv3: Support CH nsgroup membership using dynamic criteria tags
CH release adds new way to associate resources with nsgroups by creating specific tags on the resources. We would like to support this feature in the plugin for better performance. This patch make use of this feature to associate logical-ports with nsgroups (Neutron ports with security-groups), for every LP-NSGroup association, a special tag will be added to the LP. The plugin will use this NSX feature only when supported by the NSX version, and given that the designated boolean config option is set to True. Change-Id: I2a802bc314d98dba9ecc54191fcbd7330f183e12
This commit is contained in:
parent
30a4cf2d5f
commit
ddfb880d5a
@ -175,3 +175,8 @@ class NsxResourceNotFound(n_exc.NotFound):
|
||||
|
||||
class NsxQosPolicyMappingNotFound(n_exc.NotFound):
|
||||
message = _('Unable to find mapping for QoS policy: %(policy)s')
|
||||
|
||||
|
||||
class NumberOfNsgroupCriteriaTagsReached(NsxPluginException):
|
||||
message = _("Port can be associated with at most %(max_num)s "
|
||||
"security-groups.")
|
||||
|
@ -168,22 +168,23 @@ def add_v3_tag(tags, resource_type, tag):
|
||||
return tags
|
||||
|
||||
|
||||
def update_v3_tags(tags, resources):
|
||||
port_tags = dict((t['scope'], t['tag']) for t in tags)
|
||||
resources = resources or []
|
||||
# Update tags
|
||||
for resource in resources:
|
||||
tag = resource['tag'][:MAX_TAG_LEN]
|
||||
resource_type = resource['resource_type']
|
||||
if resource_type in port_tags:
|
||||
if tag:
|
||||
port_tags[resource_type] = tag
|
||||
else:
|
||||
port_tags.pop(resource_type, None)
|
||||
else:
|
||||
port_tags[resource_type] = tag
|
||||
# Create the new set of tags
|
||||
return [{'scope': k, 'tag': v} for k, v in port_tags.items()]
|
||||
def update_v3_tags(current_tags, tags_update):
|
||||
current_scopes = set([tag['scope'] for tag in current_tags])
|
||||
updated_scopes = set([tag['scope'] for tag in tags_update])
|
||||
|
||||
tags = [{'scope': tag['scope'], 'tag': tag['tag']}
|
||||
for tag in (current_tags + tags_update)
|
||||
if tag['scope'] in (current_scopes ^ updated_scopes)]
|
||||
|
||||
modified_scopes = current_scopes & updated_scopes
|
||||
for tag in tags_update:
|
||||
if tag['scope'] in modified_scopes:
|
||||
# If the tag value is empty or None, then remove the tag completely
|
||||
if tag['tag']:
|
||||
tag['tag'] = tag['tag'][:MAX_TAG_LEN]
|
||||
tags.append(tag)
|
||||
|
||||
return tags
|
||||
|
||||
|
||||
def retry_upon_exception_nsxv3(exc, delay=500, max_delay=2000,
|
||||
|
@ -40,6 +40,7 @@ REJECT = 'REJECT'
|
||||
# filtering operators and expressions
|
||||
EQUALS = 'EQUALS'
|
||||
NSGROUP_SIMPLE_EXPRESSION = 'NSGroupSimpleExpression'
|
||||
NSGROUP_TAG_EXPRESSION = 'NSGroupTagExpression'
|
||||
|
||||
# nsgroup members update actions
|
||||
ADD_MEMBERS = 'ADD_MEMBERS'
|
||||
@ -86,11 +87,20 @@ def get_nsservice(resource_type, **properties):
|
||||
return {'service': service}
|
||||
|
||||
|
||||
def create_nsgroup(display_name, description, tags):
|
||||
def get_nsgroup_port_tag_expression(scope, tag):
|
||||
return {'resource_type': NSGROUP_TAG_EXPRESSION,
|
||||
'target_type': LOGICAL_PORT,
|
||||
'scope': scope,
|
||||
'tag': tag}
|
||||
|
||||
|
||||
def create_nsgroup(display_name, description, tags, membership_criteria=None):
|
||||
body = {'display_name': display_name,
|
||||
'description': description,
|
||||
'tags': tags,
|
||||
'members': []}
|
||||
if membership_criteria:
|
||||
body.update({'membership_criteria': [membership_criteria]})
|
||||
return nsxclient.create_resource('ns-groups', body)
|
||||
|
||||
|
||||
@ -100,12 +110,17 @@ def list_nsgroups():
|
||||
|
||||
|
||||
@utils.retry_upon_exception_nsxv3(nsx_exc.StaleRevision)
|
||||
def update_nsgroup(nsgroup_id, display_name=None, description=None):
|
||||
def update_nsgroup(nsgroup_id, display_name=None, description=None,
|
||||
membership_criteria=None, members=None):
|
||||
nsgroup = read_nsgroup(nsgroup_id)
|
||||
if display_name is not None:
|
||||
nsgroup['display_name'] = display_name
|
||||
if description is not None:
|
||||
nsgroup['description'] = description
|
||||
if members is not None:
|
||||
nsgroup['members'] = members
|
||||
if membership_criteria is not None:
|
||||
nsgroup['membership_criteria'] = [membership_criteria]
|
||||
return nsxclient.update_resource('ns-groups/%s' % nsgroup_id, nsgroup)
|
||||
|
||||
|
||||
|
@ -292,13 +292,13 @@ class LogicalPort(AbstractRESTResource):
|
||||
def update(self, lport_id, vif_uuid,
|
||||
name=None, admin_state=None,
|
||||
address_bindings=None, switch_profile_ids=None,
|
||||
resources=None,
|
||||
tags_update=None,
|
||||
attachment_type=nsx_constants.ATTACHMENT_VIF,
|
||||
parent_name=None, parent_tag=None):
|
||||
lport = self.get(lport_id)
|
||||
tags = lport.get('tags', [])
|
||||
if resources:
|
||||
tags = utils.update_v3_tags(tags, resources)
|
||||
if tags_update:
|
||||
tags = utils.update_v3_tags(tags, tags_update)
|
||||
attachment = self._prepare_attachment(vif_uuid, parent_name,
|
||||
parent_tag, address_bindings,
|
||||
attachment_type)
|
||||
|
@ -37,6 +37,9 @@ LOG = log.getLogger(__name__)
|
||||
|
||||
DEFAULT_SECTION = 'OS Default Section for Neutron Security-Groups'
|
||||
DEFAULT_SECTION_TAG_NAME = 'neutron_default_dfw_section'
|
||||
PORT_SG_SCOPE = 'os-security-group'
|
||||
|
||||
MAX_NSGROUPS_CRITERIA_TAGS = 10
|
||||
|
||||
|
||||
def _get_l4_protocol_name(protocol_number):
|
||||
@ -230,6 +233,19 @@ def _get_remote_nsg_mapping(context, sg_rule, nsgroup_id):
|
||||
return remote_nsgroup_id
|
||||
|
||||
|
||||
def get_lport_tags_for_security_groups(secgroups):
|
||||
if len(secgroups) > MAX_NSGROUPS_CRITERIA_TAGS:
|
||||
raise nsx_exc.NumberOfNsgroupCriteriaTagsReached(
|
||||
max_num=MAX_NSGROUPS_CRITERIA_TAGS)
|
||||
tags = []
|
||||
for sg in secgroups:
|
||||
tags = utils.add_v3_tag(tags, PORT_SG_SCOPE, sg)
|
||||
if not tags:
|
||||
# This port shouldn't be associated with any security-group
|
||||
tags = [{'scope': PORT_SG_SCOPE, 'tag': None}]
|
||||
return tags
|
||||
|
||||
|
||||
def update_lport_with_security_groups(context, lport_id, original, updated):
|
||||
added = set(updated) - set(original)
|
||||
removed = set(original) - set(updated)
|
||||
|
@ -1187,6 +1187,13 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
if resource_type:
|
||||
tags = utils.add_v3_tag(tags, resource_type, device_id)
|
||||
|
||||
if utils.is_nsx_version_1_1_0(self._nsx_version):
|
||||
# If port has no security-groups then we don't need to add any
|
||||
# security criteria tag.
|
||||
if port_data[ext_sg.SECURITYGROUPS]:
|
||||
tags += security.get_lport_tags_for_security_groups(
|
||||
port_data[ext_sg.SECURITYGROUPS])
|
||||
|
||||
parent_name, tag = self._get_data_from_binding_profile(
|
||||
context, port_data)
|
||||
address_bindings = (self._build_address_bindings(port_data)
|
||||
@ -1567,17 +1574,19 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
'backend. Exception: %(e)s'),
|
||||
{'id': neutron_db['id'], 'e': e})
|
||||
self._cleanup_port(context, neutron_db['id'], None)
|
||||
try:
|
||||
if sgids:
|
||||
|
||||
if not utils.is_nsx_version_1_1_0(self._nsx_version):
|
||||
try:
|
||||
security.update_lport_with_security_groups(
|
||||
context, lport['id'], [], sgids)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.debug("Couldn't associate port %s with "
|
||||
"one or more security-groups, reverting "
|
||||
"logical-port creation (%s).",
|
||||
port_data['id'], lport['id'])
|
||||
self._cleanup_port(context, neutron_db['id'], lport['id'])
|
||||
context, lport['id'], [], sgids or [])
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.debug("Couldn't associate port %s with "
|
||||
"one or more security-groups, reverting "
|
||||
"logical-port creation (%s).",
|
||||
port_data['id'], lport['id'])
|
||||
self._cleanup_port(
|
||||
context, neutron_db['id'], lport['id'])
|
||||
|
||||
try:
|
||||
net_id = port_data[pbin.VIF_DETAILS]['nsx-logical-switch-id']
|
||||
@ -1634,8 +1643,10 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
_net_id, nsx_port_id = nsx_db.get_nsx_switch_and_port_id(
|
||||
context.session, port_id)
|
||||
self._port_client.delete(nsx_port_id)
|
||||
security.update_lport_with_security_groups(
|
||||
context, nsx_port_id, port.get(ext_sg.SECURITYGROUPS, []), [])
|
||||
if not utils.is_nsx_version_1_1_0(self._nsx_version):
|
||||
security.update_lport_with_security_groups(
|
||||
context, nsx_port_id,
|
||||
port.get(ext_sg.SECURITYGROUPS, []), [])
|
||||
self.disassociate_floatingips(context, port_id)
|
||||
|
||||
# Remove Mac/IP binding from native DHCP server and neutron DB.
|
||||
@ -1723,7 +1734,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
original_device_id = original_port.get('device_id')
|
||||
updated_device_owner = updated_port.get('device_owner')
|
||||
updated_device_id = updated_port.get('device_id')
|
||||
resources = []
|
||||
tags_update = []
|
||||
if original_device_id != updated_device_id:
|
||||
# Determine if we need to update or drop the tag. If the
|
||||
# updated_device_id exists then the tag will be updated. This
|
||||
@ -1737,8 +1748,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
resource_type = self._get_resource_type_for_device_id(
|
||||
original_device_owner, updated_device_id)
|
||||
if resource_type:
|
||||
resources = [{'resource_type': resource_type,
|
||||
'tag': updated_device_id}]
|
||||
tags_update = utils.add_v3_tag(tags_update, resource_type,
|
||||
updated_device_id)
|
||||
|
||||
vif_uuid = updated_port['id']
|
||||
parent_name, tag = self._get_data_from_binding_profile(
|
||||
@ -1752,10 +1763,14 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
|
||||
name = self._get_port_name(context, updated_port)
|
||||
|
||||
security.update_lport_with_security_groups(
|
||||
context, lport_id,
|
||||
original_port.get(ext_sg.SECURITYGROUPS, []),
|
||||
updated_port.get(ext_sg.SECURITYGROUPS, []))
|
||||
if utils.is_nsx_version_1_1_0(self._nsx_version):
|
||||
tags_update += security.get_lport_tags_for_security_groups(
|
||||
updated_port.get(ext_sg.SECURITYGROUPS, []))
|
||||
else:
|
||||
security.update_lport_with_security_groups(
|
||||
context, lport_id,
|
||||
original_port.get(ext_sg.SECURITYGROUPS, []),
|
||||
updated_port.get(ext_sg.SECURITYGROUPS, []))
|
||||
|
||||
# Update the DHCP profile
|
||||
if updated_device_owner == const.DEVICE_OWNER_DHCP:
|
||||
@ -1785,7 +1800,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
admin_state=updated_port.get('admin_state_up'),
|
||||
address_bindings=address_bindings,
|
||||
switch_profile_ids=switch_profile_ids,
|
||||
resources=resources,
|
||||
tags_update=tags_update,
|
||||
parent_name=parent_name,
|
||||
parent_tag=tag)
|
||||
except nsx_exc.ManagerError as inst:
|
||||
@ -2591,10 +2606,16 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
tenant_id = secgroup['tenant_id']
|
||||
self._ensure_default_security_group(context, tenant_id)
|
||||
try:
|
||||
if utils.is_nsx_version_1_1_0(self._nsx_version):
|
||||
tag_expression = (
|
||||
firewall.get_nsgroup_port_tag_expression(
|
||||
security.PORT_SG_SCOPE, secgroup['id']))
|
||||
else:
|
||||
tag_expression = None
|
||||
# NOTE(roeyc): We first create the nsgroup so that once the sg is
|
||||
# saved into db its already backed up by an nsx resource.
|
||||
ns_group = firewall.create_nsgroup(
|
||||
name, secgroup['description'], tags)
|
||||
name, secgroup['description'], tags, tag_expression)
|
||||
# security-group rules are located in a dedicated firewall section.
|
||||
firewall_section = (
|
||||
firewall.create_empty_section(
|
||||
|
@ -14,19 +14,23 @@
|
||||
|
||||
import logging
|
||||
|
||||
from vmware_nsx.common import utils
|
||||
from vmware_nsx.shell.admin.plugins.common import constants
|
||||
from vmware_nsx.shell.admin.plugins.common import formatters
|
||||
from vmware_nsx.shell.admin.plugins.common import utils as admin_utils
|
||||
from vmware_nsx.shell import resources as shell
|
||||
|
||||
from neutron.callbacks import registry
|
||||
from neutron import context as neutron_context
|
||||
from neutron.db import common_db_mixin as common_db
|
||||
from neutron.db import securitygroups_db as sg_db
|
||||
|
||||
from vmware_nsx._i18n import _LI
|
||||
from vmware_nsx.common import utils
|
||||
from vmware_nsx.db import db as nsx_db
|
||||
from vmware_nsx.shell.admin.plugins.common import constants
|
||||
from vmware_nsx.shell.admin.plugins.common import formatters
|
||||
from vmware_nsx.shell.admin.plugins.common import utils as admin_utils
|
||||
from vmware_nsx.shell.admin.plugins.nsxv3.resources import ports
|
||||
from vmware_nsx.shell.admin.plugins.nsxv3.resources import utils as v3_utils
|
||||
from vmware_nsx.shell import resources as shell
|
||||
from vmware_nsx._i18n import _LE, _LI, _LW
|
||||
from vmware_nsx.nsxlib import v3 as nsxlib
|
||||
from vmware_nsx.nsxlib.v3 import dfw_api as firewall
|
||||
from vmware_nsx.nsxlib.v3 import security
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -34,17 +38,34 @@ LOG = logging.getLogger(__name__)
|
||||
class NeutronSecurityGroupApi(sg_db.SecurityGroupDbMixin,
|
||||
common_db.CommonDbMixin):
|
||||
def __init__(self):
|
||||
self.sg_api = super(NeutronSecurityGroupApi, self)
|
||||
self.neutron_admin_context = neutron_context.get_admin_context()
|
||||
super(NeutronSecurityGroupApi, self)
|
||||
self.context = neutron_context.get_admin_context()
|
||||
|
||||
def get_security_groups(self):
|
||||
return self.sg_api.get_security_groups(self.neutron_admin_context)
|
||||
return super(NeutronSecurityGroupApi,
|
||||
self).get_security_groups(self.context)
|
||||
|
||||
def delete_security_group(self, sg_id):
|
||||
self.sg_api.delete_security_group(self.neutron_admin_context,
|
||||
sg_id)
|
||||
return super(NeutronSecurityGroupApi,
|
||||
self).delete_security_group(self.context, sg_id)
|
||||
|
||||
def get_nsgroup_id(self, sg_id):
|
||||
return nsx_db.get_nsx_security_group_id(
|
||||
self.context.session, sg_id)
|
||||
|
||||
def get_port_security_groups(self, port_id):
|
||||
secgroups_bindings = self._get_port_security_group_bindings(
|
||||
self.context, {'port_id': [port_id]})
|
||||
return [b['security_group_id'] for b in secgroups_bindings]
|
||||
|
||||
def get_security_group_ports(self, security_group_id):
|
||||
secgroups_bindings = self._get_port_security_group_bindings(
|
||||
self.context, {'security_group_id': [security_group_id]})
|
||||
return [b['port_id'] for b in secgroups_bindings]
|
||||
|
||||
|
||||
neutron_sg = NeutronSecurityGroupApi()
|
||||
neutron_db = v3_utils.NeutronDbClient()
|
||||
|
||||
|
||||
@admin_utils.output_header
|
||||
@ -129,6 +150,53 @@ def neutron_delete_security_groups(resource, event, trigger, **kwargs):
|
||||
LOG.warning(str(e))
|
||||
|
||||
|
||||
def _update_ports_dynamic_criteria_tags():
|
||||
port_client, _ = ports.get_port_and_profile_clients()
|
||||
for port in neutron_db.get_ports():
|
||||
secgroups = neutron_sg.get_port_security_groups(port['id'])
|
||||
# Nothing to do with ports that are not associated with any sec-group.
|
||||
if not secgroups:
|
||||
continue
|
||||
|
||||
_, lport_id = neutron_db.get_lswitch_and_lport_id(port['id'])
|
||||
lport = port_client.get(lport_id)
|
||||
criteria_tags = security.get_lport_tags_for_security_groups(secgroups)
|
||||
lport['tags'] = utils.update_v3_tags(
|
||||
lport.get('tags', []), criteria_tags)
|
||||
port_client._client.update(lport_id, body=lport)
|
||||
|
||||
|
||||
def _update_security_group_dynamic_criteria():
|
||||
secgroups = neutron_sg.get_security_groups()
|
||||
for sg in secgroups:
|
||||
nsgroup_id = neutron_sg.get_nsgroup_id(sg['id'])
|
||||
membership_criteria = firewall.get_nsgroup_port_tag_expression(
|
||||
security.PORT_SG_SCOPE, sg['id'])
|
||||
try:
|
||||
# We want to add the dynamic criteria and remove all direct members
|
||||
# they will be added by the manager using the new criteria.
|
||||
firewall.update_nsgroup(nsgroup_id,
|
||||
membership_criteria=membership_criteria,
|
||||
members=[])
|
||||
except Exception as e:
|
||||
LOG.warning(_LW("Failed to update membership criteria for nsgroup "
|
||||
"%(nsgroup_id)s, request to backend returned "
|
||||
"with error: %(error)s"),
|
||||
{'nsgroup_id': nsgroup_id, 'error': str(e)})
|
||||
|
||||
|
||||
@admin_utils.output_header
|
||||
def migrate_nsgroups_to_dynamic_criteria(resource, event, trigger, **kwargs):
|
||||
if not utils.is_nsx_version_1_1_0(nsxlib.get_version()):
|
||||
LOG.error(_LE("Dynamic criteria grouping feature isn't supported by "
|
||||
"this NSX version."))
|
||||
return
|
||||
# First, we add the criteria tags for all ports.
|
||||
_update_ports_dynamic_criteria_tags()
|
||||
# Update security-groups with dynamic criteria and remove direct members.
|
||||
_update_security_group_dynamic_criteria()
|
||||
|
||||
|
||||
registry.subscribe(nsx_list_security_groups,
|
||||
constants.SECURITY_GROUPS,
|
||||
shell.Operations.LIST.value)
|
||||
@ -156,3 +224,6 @@ registry.subscribe(neutron_delete_security_groups,
|
||||
registry.subscribe(neutron_delete_security_groups,
|
||||
constants.SECURITY_GROUPS,
|
||||
shell.Operations.NEUTRON_CLEAN.value)
|
||||
registry.subscribe(migrate_nsgroups_to_dynamic_criteria,
|
||||
constants.FIREWALL_NSX_GROUPS,
|
||||
shell.Operations.MIGRATE_TO_DYNAMIC_CRITERIA.value)
|
||||
|
@ -44,6 +44,7 @@ class Operations(enum.Enum):
|
||||
NSX_CLEAN = 'nsx-clean'
|
||||
NSX_UPDATE = 'nsx-update'
|
||||
NSX_UPDATE_SECRET = 'nsx-update-secret'
|
||||
MIGRATE_TO_DYNAMIC_CRITERIA = 'migrate-to-dynamic-criteria'
|
||||
|
||||
|
||||
ops = [op.value for op in Operations]
|
||||
@ -64,6 +65,9 @@ nsxv3_resources = {
|
||||
Operations.NSX_CLEAN.value,
|
||||
Operations.NEUTRON_LIST.value,
|
||||
Operations.NEUTRON_CLEAN.value]),
|
||||
constants.FIREWALL_NSX_GROUPS: Resource(
|
||||
constants.FIREWALL_NSX_GROUPS, [
|
||||
Operations.MIGRATE_TO_DYNAMIC_CRITERIA.value]),
|
||||
constants.NETWORKS: Resource(constants.NETWORKS,
|
||||
[Operations.LIST_MISMATCHES.value]),
|
||||
constants.PORTS: Resource(constants.PORTS,
|
||||
|
@ -35,7 +35,7 @@ NSG_IDS = ['11111111-1111-1111-1111-111111111111',
|
||||
def _mock_create_and_list_nsgroups(test_method):
|
||||
nsgroups = []
|
||||
|
||||
def _create_nsgroup_mock(name, desc, tags):
|
||||
def _create_nsgroup_mock(name, desc, tags, membership_criteria=None):
|
||||
nsgroup = {'id': NSG_IDS[len(nsgroups)],
|
||||
'display_name': name,
|
||||
'desc': desc,
|
||||
@ -56,6 +56,19 @@ def _mock_create_and_list_nsgroups(test_method):
|
||||
|
||||
class TestSecurityGroups(test_nsxv3.NsxV3PluginTestCaseMixin,
|
||||
test_ext_sg.TestSecurityGroups):
|
||||
pass
|
||||
|
||||
|
||||
class TestSecurityGroupsNoDynamicCriteria(test_nsxv3.NsxV3PluginTestCaseMixin,
|
||||
test_ext_sg.TestSecurityGroups):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSecurityGroupsNoDynamicCriteria, self).setUp()
|
||||
mock_nsx_version = mock.patch.object(nsx_plugin.utils,
|
||||
'is_nsx_version_1_1_0',
|
||||
new=lambda v: False)
|
||||
mock_nsx_version.start()
|
||||
self._patchers.append(mock_nsx_version)
|
||||
|
||||
@_mock_create_and_list_nsgroups
|
||||
@mock.patch.object(firewall, 'remove_nsgroup_member')
|
||||
@ -63,7 +76,7 @@ class TestSecurityGroups(test_nsxv3.NsxV3PluginTestCaseMixin,
|
||||
def test_create_port_with_multiple_security_groups(self,
|
||||
add_member_mock,
|
||||
remove_member_mock):
|
||||
super(TestSecurityGroups,
|
||||
super(TestSecurityGroupsNoDynamicCriteria,
|
||||
self).test_create_port_with_multiple_security_groups()
|
||||
|
||||
# The first nsgroup is associated with the default secgroup, which is
|
||||
@ -78,7 +91,7 @@ class TestSecurityGroups(test_nsxv3.NsxV3PluginTestCaseMixin,
|
||||
def test_update_port_with_multiple_security_groups(self,
|
||||
add_member_mock,
|
||||
remove_member_mock):
|
||||
super(TestSecurityGroups,
|
||||
super(TestSecurityGroupsNoDynamicCriteria,
|
||||
self).test_update_port_with_multiple_security_groups()
|
||||
|
||||
calls = [mock.call(NSG_IDS[0], firewall.LOGICAL_PORT, mock.ANY),
|
||||
@ -95,7 +108,7 @@ class TestSecurityGroups(test_nsxv3.NsxV3PluginTestCaseMixin,
|
||||
def test_update_port_remove_security_group_empty_list(self,
|
||||
add_member_mock,
|
||||
remove_member_mock):
|
||||
super(TestSecurityGroups,
|
||||
super(TestSecurityGroupsNoDynamicCriteria,
|
||||
self).test_update_port_remove_security_group_empty_list()
|
||||
|
||||
add_member_mock.assert_called_with(
|
||||
|
@ -752,7 +752,7 @@ class TestNsxV3Utils(NsxV3PluginTestCaseMixin):
|
||||
{'scope': 'os-project-name', 'tag': 'Z' * 40},
|
||||
{'scope': 'os-api-version',
|
||||
'tag': version.version_info.release_string()}]
|
||||
resources = [{'resource_type': 'os-instance-uuid',
|
||||
resources = [{'scope': 'os-instance-uuid',
|
||||
'tag': 'A' * 40}]
|
||||
tags = utils.update_v3_tags(tags, resources)
|
||||
expected = [{'scope': 'os-neutron-net-id', 'tag': 'X' * 40},
|
||||
@ -770,7 +770,7 @@ class TestNsxV3Utils(NsxV3PluginTestCaseMixin):
|
||||
{'scope': 'os-project-name', 'tag': 'Z' * 40},
|
||||
{'scope': 'os-api-version',
|
||||
'tag': version.version_info.release_string()}]
|
||||
resources = [{'resource_type': 'os-neutron-net-id',
|
||||
resources = [{'scope': 'os-neutron-net-id',
|
||||
'tag': ''}]
|
||||
tags = utils.update_v3_tags(tags, resources)
|
||||
expected = [{'scope': 'os-project-id', 'tag': 'Y' * 40},
|
||||
@ -785,7 +785,7 @@ class TestNsxV3Utils(NsxV3PluginTestCaseMixin):
|
||||
{'scope': 'os-project-name', 'tag': 'Z' * 40},
|
||||
{'scope': 'os-api-version',
|
||||
'tag': version.version_info.release_string()}]
|
||||
resources = [{'resource_type': 'os-project-id',
|
||||
resources = [{'scope': 'os-project-id',
|
||||
'tag': 'A' * 40}]
|
||||
tags = utils.update_v3_tags(tags, resources)
|
||||
expected = [{'scope': 'os-neutron-net-id', 'tag': 'X' * 40},
|
||||
@ -795,6 +795,35 @@ class TestNsxV3Utils(NsxV3PluginTestCaseMixin):
|
||||
'tag': version.version_info.release_string()}]
|
||||
self.assertEqual(sorted(expected), sorted(tags))
|
||||
|
||||
def test_update_v3_tags_repetitive_scopes(self):
|
||||
tags = [{'scope': 'os-neutron-net-id', 'tag': 'X' * 40},
|
||||
{'scope': 'os-project-id', 'tag': 'Y' * 40},
|
||||
{'scope': 'os-project-name', 'tag': 'Z' * 40},
|
||||
{'scope': 'os-security-group', 'tag': 'SG1'},
|
||||
{'scope': 'os-security-group', 'tag': 'SG2'}]
|
||||
tags_update = [{'scope': 'os-security-group', 'tag': 'SG3'},
|
||||
{'scope': 'os-security-group', 'tag': 'SG4'}]
|
||||
tags = utils.update_v3_tags(tags, tags_update)
|
||||
expected = [{'scope': 'os-neutron-net-id', 'tag': 'X' * 40},
|
||||
{'scope': 'os-project-id', 'tag': 'Y' * 40},
|
||||
{'scope': 'os-project-name', 'tag': 'Z' * 40},
|
||||
{'scope': 'os-security-group', 'tag': 'SG3'},
|
||||
{'scope': 'os-security-group', 'tag': 'SG4'}]
|
||||
self.assertEqual(sorted(expected), sorted(tags))
|
||||
|
||||
def test_update_v3_tags_repetitive_scopes_remove(self):
|
||||
tags = [{'scope': 'os-neutron-net-id', 'tag': 'X' * 40},
|
||||
{'scope': 'os-project-id', 'tag': 'Y' * 40},
|
||||
{'scope': 'os-project-name', 'tag': 'Z' * 40},
|
||||
{'scope': 'os-security-group', 'tag': 'SG1'},
|
||||
{'scope': 'os-security-group', 'tag': 'SG2'}]
|
||||
tags_update = [{'scope': 'os-security-group', 'tag': None}]
|
||||
tags = utils.update_v3_tags(tags, tags_update)
|
||||
expected = [{'scope': 'os-neutron-net-id', 'tag': 'X' * 40},
|
||||
{'scope': 'os-project-id', 'tag': 'Y' * 40},
|
||||
{'scope': 'os-project-name', 'tag': 'Z' * 40}]
|
||||
self.assertEqual(sorted(expected), sorted(tags))
|
||||
|
||||
|
||||
class NsxNativeDhcpTestCase(NsxV3PluginTestCaseMixin):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user