diff --git a/vmware_nsx/api_replay/client.py b/vmware_nsx/api_replay/client.py index 299acb63c2..c6940f825b 100644 --- a/vmware_nsx/api_replay/client.py +++ b/vmware_nsx/api_replay/client.py @@ -101,6 +101,8 @@ class ApiReplayClient(object): source_sec_groups = source_sec_groups['security_groups'] dest_sec_groups = dest_sec_groups['security_groups'] + drop_sg_fields = ['revision'] + for sg in source_sec_groups: dest_sec_group = self.have_id(sg['id'], dest_sec_groups) # If the security group already exists on the the dest_neutron @@ -112,9 +114,10 @@ class ApiReplayClient(object): dest_sec_group['security_group_rules']) is False): try: + body = self.drop_fields(sg_rule, drop_sg_fields) print( self.dest_neutron.create_security_group_rule( - {'security_group_rule': sg_rule})) + {'security_group_rule': body})) except n_exc.Conflict: # NOTE(arosen): when you create a default # security group it is automatically populated @@ -127,8 +130,9 @@ class ApiReplayClient(object): else: sg_rules = sg.pop('security_group_rules') try: + body = self.drop_fields(sg, drop_sg_fields) new_sg = self.dest_neutron.create_security_group( - {'security_group': sg}) + {'security_group': body}) print("Created security-group %s" % new_sg) except Exception as e: # TODO(arosen): improve exception handing here. @@ -136,8 +140,9 @@ class ApiReplayClient(object): for sg_rule in sg_rules: try: + body = self.drop_fields(sg_rule, drop_sg_fields) rule = self.dest_neutron.create_security_group_rule( - {'security_group_rule': sg_rule}) + {'security_group_rule': body}) print("created security group rule %s " % rule['id']) except Exception: # NOTE(arosen): when you create a default @@ -158,7 +163,12 @@ class ApiReplayClient(object): drop_router_fields = ['status', 'routes', 'ha', - 'external_gateway_info'] + 'external_gateway_info', + 'router_type', + 'availability_zone_hints', + 'availability_zones', + 'distributed', + 'revision'] body = self.drop_fields(router, drop_router_fields) new_router = (self.dest_neutron.create_router( {'router': body})) @@ -178,23 +188,24 @@ class ApiReplayClient(object): drop_subnet_fields = ['updated_at', 'created_at', 'network_id', - 'id'] + 'advanced_service_providers', + 'id', 'revision'] - # NOTE: These are fields we drop of when creating a subnet as the - # network api doesn't allow us to specify them. - # TODO(arosen): revisit this to make these fields passable. drop_port_fields = ['updated_at', 'created_at', 'status', 'port_security_enabled', 'binding:vif_details', 'binding:vif_type', - 'binding:host_id', 'qos_policy_id'] + 'binding:host_id', 'qos_policy_id', + 'revision', + 'vnic_index'] drop_network_fields = ['status', 'subnets', 'availability_zones', 'created_at', 'updated_at', 'tags', 'qos_policy_id', 'ipv4_address_scope', - 'ipv6_address_scope', 'mtu'] + 'ipv6_address_scope', 'mtu', + 'revision'] for network in source_networks: body = self.drop_fields(network, drop_network_fields) @@ -210,6 +221,7 @@ class ApiReplayClient(object): {'network': body})['network'] print("Created network: %s " % created_net) + created_subnet = None for subnet_id in network['subnets']: subnet = self.find_subnet_by_id(subnet_id, source_subnets) body = self.drop_fields(subnet, drop_subnet_fields) @@ -263,7 +275,8 @@ class ApiReplayClient(object): if port['device_owner'] == 'network:floatingip': continue - if port['device_owner'] == 'network:router_interface': + if (port['device_owner'] == 'network:router_interface' and + created_subnet is not None): try: # uplink router_interface ports self.dest_neutron.add_interface_router( @@ -285,7 +298,7 @@ class ApiReplayClient(object): def migrate_floatingips(self): """Migrates floatingips from source to dest neutron.""" source_fips = self.source_neutron.list_floatingips()['floatingips'] - drop_fip_fields = ['status', 'router_id', 'id'] + drop_fip_fields = ['status', 'router_id', 'id', 'revision'] for source_fip in source_fips: body = self.drop_fields(source_fip, drop_fip_fields) diff --git a/vmware_nsx/extensions/api_replay.py b/vmware_nsx/extensions/api_replay.py index 8b44916e65..4fec4d443d 100644 --- a/vmware_nsx/extensions/api_replay.py +++ b/vmware_nsx/extensions/api_replay.py @@ -16,33 +16,36 @@ # from neutron.api import extensions +from neutron.api.v2 import attributes +# The attributes map is here for 2 reasons: +# 1) allow posting id for the different objects we are importing +# 2) make sure security-group named 'default' is also copied + +ID_WITH_POST = {'allow_post': True, 'allow_put': False, + 'validate': {'type:uuid': None}, + 'is_visible': True, + 'primary_key': True} + RESOURCE_ATTRIBUTE_MAP = { 'ports': { - 'id': {'allow_post': True, 'allow_put': False, - 'validate': {'type:uuid': None}, - 'is_visible': True}, + 'id': ID_WITH_POST, }, 'networks': { - 'id': {'allow_post': True, 'allow_put': False, - 'validate': {'type:uuid': None}, - 'is_visible': True}, + 'id': ID_WITH_POST, }, 'security_groups': { - 'id': {'allow_post': True, 'allow_put': False, - 'validate': {'type:uuid': None}, - 'is_visible': True}, + 'id': ID_WITH_POST, + 'name': {'allow_post': True, 'allow_put': True, + 'is_visible': True, 'default': '', + 'validate': {'type:string': attributes.NAME_MAX_LEN}}, }, 'security_group_rules': { - 'id': {'allow_post': True, 'allow_put': False, - 'validate': {'type:uuid': None}, - 'is_visible': True}, + 'id': ID_WITH_POST, }, 'routers': { - 'id': {'allow_post': True, 'allow_put': False, - 'validate': {'type:uuid': None}, - 'is_visible': True}, + 'id': ID_WITH_POST, }, } @@ -71,3 +74,8 @@ class Api_replay(extensions.ExtensionDescriptor): return RESOURCE_ATTRIBUTE_MAP else: return {} + + def get_required_extensions(self): + # make sure this extension is called after those, so our change + # will not be overridden + return ["security-group", "router"] diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index 8c02553d6c..ff1b1e8bad 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -2694,19 +2694,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, return super(NsxV3Plugin, self)._ensure_default_security_group( context, tenant_id) - def _stub__validate_name_not_default(self): - # NOTE(arosen): if in replay mode we need stub out this validator to - # all default security groups to be created via the api - if cfg.CONF.api_replay_mode: - def _pass(data, _dummy=None): - pass - validators.add_validator('name_not_default', _pass) - def get_security_groups(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False, default_sg=False): - - self._stub__validate_name_not_default() return super(NsxV3Plugin, self).get_security_groups( context, filters=filters, fields=fields, sorts=sorts, limit=limit, diff --git a/vmware_nsx/tests/unit/nsx_v3/test_api_replay.py b/vmware_nsx/tests/unit/nsx_v3/test_api_replay.py index 0fe4cbc025..dcec99f300 100644 --- a/vmware_nsx/tests/unit/nsx_v3/test_api_replay.py +++ b/vmware_nsx/tests/unit/nsx_v3/test_api_replay.py @@ -15,19 +15,29 @@ from vmware_nsx.tests.unit.nsx_v3 import test_plugin +from neutron import manager +from oslo_config import cfg + -# FIXME(arosen): - these tests pass but seem to break the other tests -# as the attribute map doesn't get reset after each test class. I tried -# backing it up and restoring it here though that doesn't seem to be doing -# the trick either... class TestApiReplay(test_plugin.NsxV3PluginTestCaseMixin): def setUp(self, plugin=None, ext_mgr=None, service_plugins=None): # enables api_replay_mode for these tests + cfg.CONF.set_override('api_replay_mode', True) + super(TestApiReplay, self).setUp() + def tearDown(self): + # disables api_replay_mode for these tests + cfg.CONF.set_override('api_replay_mode', False) + + # remove the extension from the plugin + manager.NeutronManager.get_plugin().supported_extension_aliases.remove( + 'api-replay') + + super(TestApiReplay, self).tearDown() + def test_create_port_specify_id(self): - self.skipTest("...fixme...") specified_network_id = '555e762b-d7a1-4b44-b09b-2a34ada56c9f' specified_port_id = 'e55e762b-d7a1-4b44-b09b-2a34ada56c9f' network_res = self._create_network(self.fmt,