From 47bb0a8a6c56f6c9c267a45259183897eb7ae74e Mon Sep 17 00:00:00 2001 From: Akash Gangil Date: Fri, 22 Jul 2016 07:50:15 -0700 Subject: [PATCH] Update CIF creation request body The CIF API got updated in new v3 builds. Here is what changed: 1. The information like ip, mac, vlan, host_cif_id which was intially opaque and just provided as a key value pair inside the context body, now have explicit properties. 2. The context now contains container_host_vif_id, and vlan_tag as explicit properties. It still has a key_values array but no information is needed to be passed in that. That's more reserved for future use cases. 3. The host_vif_id property name is updated to container_host_vif_id. 4. The vlan_id property name is updated to vlan_tag. 5. The IP and mac address of the port are moved out of the context. This was previously being duplicated as we pass that information in address bindings already. Here is the sample request body. { "resource_type": "LogicalPort", "attachment": { "attachment_type": "CIF", "id": "test-2", "context": { "vlan_tag": 122, "container_host_vif_id": "c6f817a0-4e36-421e-98a6-8a2faed880bc", "key_values": [], "resource_type": "CifAttachmentContext" } }, "admin_state": "UP", "logical_switch_id": "", "address_bindings": [ { "ip_address": "192.168.1.110", "mac_address": "aa:bb:cc:dd:ee:ff" } ], } This patch makes the plugin send the expected request body to the backend. Also this patch updates default argument in the create_port and update_port method from parent_name to parent_vif_id DocImpact Change-Id: Icf9109839284df9217877342182df4dce4ca2787 --- vmware_nsx/nsxlib/v3/resources.py | 37 +++----- vmware_nsx/plugins/nsx_v3/plugin.py | 6 +- .../tests/unit/nsx_v3/test_constants.py | 46 ++++++++++ .../tests/unit/nsxlib/v3/test_resources.py | 86 +++++++++++++++---- 4 files changed, 134 insertions(+), 41 deletions(-) diff --git a/vmware_nsx/nsxlib/v3/resources.py b/vmware_nsx/nsxlib/v3/resources.py index d2675b884d..a5804b42ef 100644 --- a/vmware_nsx/nsxlib/v3/resources.py +++ b/vmware_nsx/nsxlib/v3/resources.py @@ -236,40 +236,31 @@ class LogicalPort(AbstractRESTResource): return body - def _prepare_attachment(self, vif_uuid, parent_name, parent_tag, + def _prepare_attachment(self, vif_uuid, parent_vif_id, parent_tag, address_bindings, attachment_type): - # NOTE(arosen): if a parent_name is specified we need to use the - # CIF's attachment. - key_values = None - if parent_name: - attachment_type = nsx_constants.ATTACHMENT_CIF - key_values = [ - {'key': 'VLAN_ID', 'value': parent_tag}, - {'key': 'Host_VIF_ID', 'value': parent_name}, - {'key': 'IP', 'value': address_bindings[0].ip_address}, - {'key': 'MAC', 'value': address_bindings[0].mac_address}] - # NOTE(arosen): The above api body structure might change - # in the future - if attachment_type and vif_uuid: attachment = {'attachment_type': attachment_type, 'id': vif_uuid} - - if key_values: - attachment['context'] = {'key_values': key_values} - attachment['context']['resource_type'] = \ - nsx_constants.CIF_RESOURCE_TYPE + if parent_vif_id: + context = {'vlan_tag': parent_tag, + 'container_host_vif_id': parent_vif_id, + 'resource_type': nsx_constants.CIF_RESOURCE_TYPE} + attachment['context'] = context return attachment def create(self, lswitch_id, vif_uuid, tags=None, attachment_type=nsx_constants.ATTACHMENT_VIF, admin_state=True, name=None, address_bindings=None, - parent_name=None, parent_tag=None, + parent_vif_id=None, parent_tag=None, switch_profile_ids=None): tags = tags or [] body = {'logical_switch_id': lswitch_id} - attachment = self._prepare_attachment(vif_uuid, parent_name, + # NOTE(arosen): If parent_vif_id is specified we need to use + # CIF attachment type. + if parent_vif_id: + attachment_type = nsx_constants.ATTACHMENT_CIF + attachment = self._prepare_attachment(vif_uuid, parent_vif_id, parent_tag, address_bindings, attachment_type) body.update(self._build_body_attrs( @@ -294,12 +285,12 @@ class LogicalPort(AbstractRESTResource): address_bindings=None, switch_profile_ids=None, tags_update=None, attachment_type=nsx_constants.ATTACHMENT_VIF, - parent_name=None, parent_tag=None): + parent_vif_id=None, parent_tag=None): lport = self.get(lport_id) tags = lport.get('tags', []) if tags_update: tags = utils.update_v3_tags(tags, tags_update) - attachment = self._prepare_attachment(vif_uuid, parent_name, + attachment = self._prepare_attachment(vif_uuid, parent_vif_id, parent_tag, address_bindings, attachment_type) lport.update(self._build_body_attrs( diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index fac60753dd..72b5df3259 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -1245,7 +1245,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, admin_state=port_data['admin_state_up'], address_bindings=address_bindings, attachment_type=attachment_type, - parent_name=parent_name, parent_tag=tag, + parent_vif_id=parent_name, parent_tag=tag, switch_profile_ids=profiles) except nsx_exc.ManagerError as inst: # we may fail if the QoS is not supported for this port @@ -1754,7 +1754,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, updated_device_id) vif_uuid = updated_port['id'] - parent_name, tag = self._get_data_from_binding_profile( + parent_vif_id, tag = self._get_data_from_binding_profile( context, updated_port) attachment_type = nsx_constants.ATTACHMENT_VIF if (not updated_device_owner or @@ -1803,7 +1803,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, address_bindings=address_bindings, switch_profile_ids=switch_profile_ids, tags_update=tags_update, - parent_name=parent_name, + parent_vif_id=parent_vif_id, parent_tag=tag) except nsx_exc.ManagerError as inst: # we may fail if the QoS is not supported for this port diff --git a/vmware_nsx/tests/unit/nsx_v3/test_constants.py b/vmware_nsx/tests/unit/nsx_v3/test_constants.py index 910147d01c..5befdc36ca 100644 --- a/vmware_nsx/tests/unit/nsx_v3/test_constants.py +++ b/vmware_nsx/tests/unit/nsx_v3/test_constants.py @@ -56,6 +56,52 @@ FAKE_PORT = { ] } +FAKE_CONTAINER_PORT = { + "id": FAKE_PORT_UUID, + "display_name": FAKE_NAME, + "resource_type": "LogicalPort", + "address_bindings": [ + { + "ip_address": "192.168.1.110", + "mac_address": "aa:bb:cc:dd:ee:ff" + } + ], + "logical_switch_id": FAKE_SWITCH_UUID, + "admin_state": "UP", + "attachment": { + "id": "9ca8d413-f7bf-4276-b4c9-62f42516bdb2", + "attachment_type": "CIF", + "context": { + "vlan_tag": 122, + "container_host_vif_id": "c6f817a0-4e36-421e-98a6-8a2faed880bc", + "key_values": [], + "resource_type": "CifAttachmentContext", + } + }, + "switching_profile_ids": [ + { + "value": "64814784-7896-3901-9741-badeff705639", + "key": "IpDiscoverySwitchingProfile" + }, + { + "value": "fad98876-d7ff-11e4-b9d6-1681e6b88ec1", + "key": "SpoofGuardSwitchingProfile" + }, + { + "value": "93b4b7e8-f116-415d-a50c-3364611b5d09", + "key": "PortMirroringSwitchingProfile" + }, + { + "value": "fbc4fb17-83d9-4b53-a286-ccdf04301888", + "key": "SwitchSecuritySwitchingProfile" + }, + { + "value": "f313290b-eba8-4262-bd93-fab5026e9495", + "key": "QosSwitchingProfile" + } + ] +} + FAKE_QOS_PROFILE = { "resource_type": "QosSwitchingProfile", "id": uuidutils.generate_uuid(), diff --git a/vmware_nsx/tests/unit/nsxlib/v3/test_resources.py b/vmware_nsx/tests/unit/nsxlib/v3/test_resources.py index 4e865402b5..67bc5f1416 100644 --- a/vmware_nsx/tests/unit/nsxlib/v3/test_resources.py +++ b/vmware_nsx/tests/unit/nsxlib/v3/test_resources.py @@ -235,28 +235,36 @@ class LogicalPortTestCase(nsxlib_testcase.NsxClientTestCase): return self.mocked_resource( resources.LogicalPort, session_response=session_response) + def _get_profile_dicts(self, fake_port): + fake_profile_dicts = [] + for profile_id in fake_port['switching_profile_ids']: + fake_profile_dicts.append({'resource_type': profile_id['key'], + 'id': profile_id['value']}) + return fake_profile_dicts + + def _get_pktcls_bindings(self): + fake_pkt_classifiers = [] + fake_binding_repr = [] + for i in range(0, 3): + ip = "9.10.11.%s" % i + mac = "00:0c:29:35:4a:%sc" % i + fake_pkt_classifiers.append(resources.PacketAddressClassifier( + ip, mac, None)) + fake_binding_repr.append({ + 'ip_address': ip, + 'mac_address': mac + }) + return fake_pkt_classifiers, fake_binding_repr + def test_create_logical_port(self): """ Test creating a port returns the correct response and 200 status """ fake_port = test_constants_v3.FAKE_PORT.copy() - profile_dicts = [] - for profile_id in fake_port['switching_profile_ids']: - profile_dicts.append({'resource_type': profile_id['key'], - 'id': profile_id['value']}) + profile_dicts = self._get_profile_dicts(fake_port) - pkt_classifiers = [] - binding_repr = [] - for i in range(0, 3): - ip = "9.10.11.%s" % i - mac = "00:0c:29:35:4a:%sc" % i - pkt_classifiers.append(resources.PacketAddressClassifier( - ip, mac, None)) - binding_repr.append({ - 'ip_address': ip, - 'mac_address': mac - }) + pkt_classifiers, binding_repr = self._get_pktcls_bindings() fake_port['address_bindings'] = binding_repr @@ -286,6 +294,54 @@ class LogicalPortTestCase(nsxlib_testcase.NsxClientTestCase): 'https://1.2.3.4/api/v1/logical-ports', data=jsonutils.dumps(resp_body, sort_keys=True)) + def test_create_logical_port_with_attachtype_cif(self): + """ + Test creating a port returns the correct response and 200 status + """ + fake_port = test_constants_v3.FAKE_CONTAINER_PORT.copy() + + profile_dicts = self._get_profile_dicts(fake_port) + + pkt_classifiers, binding_repr = self._get_pktcls_bindings() + + fake_port['address_bindings'] = binding_repr + + mocked_resource = self._mocked_lport() + switch_profile = resources.SwitchingProfile + fake_port_ctx = fake_port['attachment']['context'] + + fake_container_host_vif_id = fake_port_ctx['container_host_vif_id'] + + mocked_resource.create( + fake_port['logical_switch_id'], + fake_port['attachment']['id'], + parent_vif_id=fake_container_host_vif_id, + parent_tag=fake_port_ctx['vlan_tag'], + address_bindings=pkt_classifiers, + switch_profile_ids=switch_profile.build_switch_profile_ids( + mock.Mock(), *profile_dicts)) + + resp_body = { + 'logical_switch_id': fake_port['logical_switch_id'], + 'switching_profile_ids': fake_port['switching_profile_ids'], + 'attachment': { + 'attachment_type': 'CIF', + 'id': fake_port['attachment']['id'], + 'context': { + 'vlan_tag': fake_port_ctx['vlan_tag'], + 'container_host_vif_id': fake_container_host_vif_id, + 'resource_type': 'CifAttachmentContext' + } + }, + 'admin_state': 'UP', + 'address_bindings': fake_port['address_bindings'] + } + + test_client.assert_json_call( + 'post', mocked_resource, + 'https://1.2.3.4/api/v1/logical-ports', + data=jsonutils.dumps(resp_body, sort_keys=True)) + def test_create_logical_port_admin_down(self): """ Test creating port with admin_state down