From cdaad65004a53860c60b35a1b76d9380d9bd4d3f Mon Sep 17 00:00:00 2001 From: Anna Khmelnitsky Date: Fri, 28 Apr 2017 18:45:16 -0700 Subject: [PATCH] Update policy resources apis 1. create when missing For NSX Policy resources, modify "update" method behavior to create the resource if it does not exist. This will save roundtrips. 2. update the path to use policy/api/vi 3. use POST instead of PUT 4. Update apis do not have to use get before update 5. Add enforcement point thumbprint Co-Authored-by: Adit Sarfaty Change-Id: Ic44308d1df8944dfbcfa2a613ed8e1847eee3715 --- .../tests/unit/v3/test_policy_api.py | 73 +++--- .../tests/unit/v3/test_policy_resources.py | 211 +++++++++++------- vmware_nsxlib/v3/__init__.py | 18 +- vmware_nsxlib/v3/client.py | 14 +- vmware_nsxlib/v3/cluster.py | 4 +- vmware_nsxlib/v3/config.py | 3 +- vmware_nsxlib/v3/policy_defs.py | 104 +++++---- vmware_nsxlib/v3/policy_resources.py | 168 +++++++------- 8 files changed, 350 insertions(+), 245 deletions(-) diff --git a/vmware_nsxlib/tests/unit/v3/test_policy_api.py b/vmware_nsxlib/tests/unit/v3/test_policy_api.py index 35cd5b8a..a881974d 100644 --- a/vmware_nsxlib/tests/unit/v3/test_policy_api.py +++ b/vmware_nsxlib/tests/unit/v3/test_policy_api.py @@ -18,14 +18,14 @@ from vmware_nsxlib.v3 import client from vmware_nsxlib.v3 import policy_constants from vmware_nsxlib.v3 import policy_defs as policy -BASE_POLICY_URI = "https://1.2.3.4/api/v1/" +BASE_POLICY_URI = "https://1.2.3.4/policy/api/v1/" class TestPolicyApi(nsxlib_testcase.NsxClientTestCase): def setUp(self): self.client = self.new_mocked_client(client.NSX3Client, - url_prefix='api/v1/') + url_prefix='policy/api/v1/') self.policy_api = policy.NsxPolicyApi(self.client) super(TestPolicyApi, self).setUp() @@ -43,8 +43,8 @@ class TestPolicyDomain(TestPolicyApi): 'archaea', 'prokaryotic cells', 'typically characterized by membrane lipids') - self.policy_api.create(domain_def) - self.assert_json_call('PUT', self.client, + self.policy_api.create_or_update(domain_def) + self.assert_json_call('POST', self.client, 'infra/domains/archaea', data=domain_def.get_obj_dict()) @@ -73,8 +73,8 @@ class TestPolicyGroup(TestPolicyApi): 'eukarya', 'cats', 'felis catus') - self.policy_api.create(group_def) - self.assert_json_call('PUT', self.client, + self.policy_api.create_or_update(group_def) + self.assert_json_call('POST', self.client, 'infra/domains/eukarya/groups/cats', data=group_def.get_obj_dict()) @@ -89,7 +89,7 @@ class TestPolicyGroup(TestPolicyApi): self.policy_api.create_with_parent(domain_def, group_def) data = domain_def.get_obj_dict() data['groups'] = [group_def.get_obj_dict()] - self.assert_json_call('PUT', self.client, + self.assert_json_call('POST', self.client, 'infra/domains/eukarya', data=data) @@ -110,14 +110,12 @@ class TestPolicyGroup(TestPolicyApi): expected_group = {'id': 'dogs', 'display_name': None, 'description': None, - 'expression': [expected_condition], - '_revision': 0} + 'expression': [expected_condition]} expected_data = {'id': 'eukarya', 'display_name': None, 'description': None, - 'groups': [expected_group], - '_revision': 0} - self.assert_json_call('PUT', self.client, + 'groups': [expected_group]} + self.assert_json_call('POST', self.client, 'infra/domains/eukarya', data=expected_data) @@ -134,7 +132,7 @@ class TestPolicyGroup(TestPolicyApi): self.policy_api.create_with_parent(domain_def, group_def) data = domain_def.get_obj_dict() data['groups'] = [group_def.get_obj_dict()] - self.assert_json_call('PUT', self.client, + self.assert_json_call('POST', self.client, 'infra/domains/eukarya', data=data) @@ -149,8 +147,8 @@ class TestPolicyService(TestPolicyApi): def test_create(self): service_def = policy.ServiceDef('roomservice') - self.policy_api.create(service_def) - self.assert_json_call('PUT', self.client, + self.policy_api.create_or_update(service_def) + self.assert_json_call('POST', self.client, 'infra/services/roomservice', data=service_def.get_obj_dict()) @@ -167,14 +165,12 @@ class TestPolicyService(TestPolicyApi): 'display_name': 'room http', 'description': None, 'l4_protocol': 'TCP', - 'destination_ports': [80, 8080], - '_revision': 0} + 'destination_ports': [80, 8080]} expected_data = {'id': 'roomservice', 'display_name': None, 'description': None, - 'service_entries': [expected_entry], - '_revision': 0} - self.assert_json_call('PUT', self.client, + 'service_entries': [expected_entry]} + self.assert_json_call('POST', self.client, 'infra/services/roomservice', data=expected_data) @@ -183,8 +179,8 @@ class TestPolicyCommunicationProfile(TestPolicyApi): def test_create(self): profile_def = policy.CommunicationProfileDef('rental') - self.policy_api.create(profile_def) - self.assert_json_call('PUT', self.client, + self.policy_api.create_or_update(profile_def) + self.assert_json_call('POST', self.client, 'infra/communication-profiles/rental', data=profile_def.get_obj_dict()) @@ -201,14 +197,12 @@ class TestPolicyCommunicationProfile(TestPolicyApi): 'display_name': None, 'description': 'includes roomservice', 'services': ["roomservice"], - 'action': 'ALLOW', - '_revision': 0} + 'action': 'ALLOW'} expected_data = {'id': 'rental', 'display_name': None, 'description': None, - 'communication_profile_entries': [expected_entry], - '_revision': 0} - self.assert_json_call('PUT', self.client, + 'communication_profile_entries': [expected_entry]} + self.assert_json_call('POST', self.client, 'infra/communication-profiles/rental', data=expected_data) @@ -233,8 +227,7 @@ class TestPolicyCommunicationMap(TestPolicyApi): dest_groups=["group3"], profile_id="profile2") - self.expected_data1 = {'_revision': 0, - 'id': 'cm1', + self.expected_data1 = {'id': 'cm1', 'display_name': None, 'description': None, 'sequence_number': 12, @@ -246,8 +239,7 @@ class TestPolicyCommunicationMap(TestPolicyApi): 'communication_profile_path': '/infra/communication-profiles/profile1'} - self.expected_data2 = {'_revision': 0, - 'id': 'cm2', + self.expected_data2 = {'id': 'cm2', 'display_name': None, 'description': None, 'sequence_number': 13, @@ -265,7 +257,7 @@ class TestPolicyCommunicationMap(TestPolicyApi): self.policy_api.create_with_parent(map_def, self.entry1) expected_data = map_def.get_obj_dict() expected_data['communication_entries'] = [self.expected_data1] - self.assert_json_call('PUT', self.client, + self.assert_json_call('POST', self.client, 'infra/domains/d1/communication-map', data=expected_data) @@ -277,14 +269,14 @@ class TestPolicyCommunicationMap(TestPolicyApi): expected_data = map_def.get_obj_dict() expected_data['communication_entries'] = [self.expected_data1, self.expected_data2] - self.assert_json_call('PUT', self.client, + self.assert_json_call('POST', self.client, 'infra/domains/d1/communication-map', data=expected_data) def test_update_entry(self): - self.policy_api.create(self.entry1) + self.policy_api.create_or_update(self.entry1) - self.assert_json_call('PUT', self.client, + self.assert_json_call('POST', self.client, 'infra/domains/d1/communication-map/' 'communication-entries/cm1', data=self.expected_data1) @@ -305,9 +297,9 @@ class TestPolicyEnforcementPoint(TestPolicyApi): username='admin', password='a') - self.policy_api.create(ep_def) + self.policy_api.create_or_update(ep_def) ep_path = policy.EnforcementPointDef('ep1').get_resource_path() - self.assert_json_call('PUT', self.client, + self.assert_json_call('POST', self.client, ep_path, data=ep_def.get_obj_dict()) @@ -317,15 +309,14 @@ class TestPolicyDeploymentMap(TestPolicyApi): def test_create(self): map_def = policy.DeploymentMapDef('dm1', domain_id='d1', ep_id='ep1') - self.policy_api.create(map_def) + self.policy_api.create_or_update(map_def) ep_path = policy.EnforcementPointDef('ep1').get_resource_full_path() - expected_data = {'_revision': 0, - 'id': 'dm1', + expected_data = {'id': 'dm1', 'display_name': None, 'description': None, 'domain_path': '/infra/domains/d1', 'enforcement_point_paths': [ep_path]} - self.assert_json_call('PUT', self.client, + self.assert_json_call('POST', self.client, 'infra/domaindeploymentmap/dm1', data=expected_data) diff --git a/vmware_nsxlib/tests/unit/v3/test_policy_resources.py b/vmware_nsxlib/tests/unit/v3/test_policy_resources.py index e4f1ea25..d0148ba8 100644 --- a/vmware_nsxlib/tests/unit/v3/test_policy_resources.py +++ b/vmware_nsxlib/tests/unit/v3/test_policy_resources.py @@ -82,11 +82,12 @@ class TestPolicyDomain(NsxPolicyLibTestCase): name = 'd1' description = 'desc' id = '111' - with mock.patch.object(self.policy_api, "create") as api_call: - self.resourceApi.create(name, - domain_id=id, - description=description, - tenant=TEST_TENANT) + with mock.patch.object(self.policy_api, + "create_or_update") as api_call: + self.resourceApi.create_or_overwrite(name, + domain_id=id, + description=description, + tenant=TEST_TENANT) expected_def = policy_defs.DomainDef(domain_id=id, name=name, description=description, @@ -96,9 +97,10 @@ class TestPolicyDomain(NsxPolicyLibTestCase): def test_create_without_id(self): name = 'd1' description = 'desc' - with mock.patch.object(self.policy_api, "create") as api_call: - self.resourceApi.create(name, description=description, - tenant=TEST_TENANT) + with mock.patch.object(self.policy_api, + "create_or_update") as api_call: + self.resourceApi.create_or_overwrite(name, description=description, + tenant=TEST_TENANT) expected_def = policy_defs.DomainDef(domain_id=mock.ANY, name=name, description=description, @@ -141,9 +143,8 @@ class TestPolicyDomain(NsxPolicyLibTestCase): id = '111' name = 'new name' description = 'new desc' - with mock.patch.object(self.policy_api, "get", - return_value={}) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call: + with mock.patch.object(self.policy_api, + "create_or_update") as update_call: self.resourceApi.update(id, name=name, description=description, @@ -152,7 +153,6 @@ class TestPolicyDomain(NsxPolicyLibTestCase): tenant=TEST_TENANT) expected_dict = {'display_name': name, 'description': description} - self.assert_called_with_def(get_call, expected_def) self.assert_called_with_def_and_dict( update_call, expected_def, expected_dict) @@ -168,12 +168,13 @@ class TestPolicyGroup(NsxPolicyLibTestCase): name = 'g1' description = 'desc' id = '222' - with mock.patch.object(self.policy_api, "create") as api_call: - self.resourceApi.create(name, - domain_id, - group_id=id, - description=description, - tenant=TEST_TENANT) + with mock.patch.object(self.policy_api, + "create_or_update") as api_call: + self.resourceApi.create_or_overwrite(name, + domain_id, + group_id=id, + description=description, + tenant=TEST_TENANT) expected_def = policy_defs.GroupDef(domain_id=domain_id, group_id=id, name=name, @@ -186,9 +187,11 @@ class TestPolicyGroup(NsxPolicyLibTestCase): domain_id = '111' name = 'g1' description = 'desc' - with mock.patch.object(self.policy_api, "create") as api_call: - self.resourceApi.create(name, domain_id, description=description, - tenant=TEST_TENANT) + with mock.patch.object(self.policy_api, + "create_or_update") as api_call: + self.resourceApi.create_or_overwrite(name, domain_id, + description=description, + tenant=TEST_TENANT) expected_def = policy_defs.GroupDef(domain_id=domain_id, group_id=mock.ANY, name=name, @@ -205,8 +208,9 @@ class TestPolicyGroup(NsxPolicyLibTestCase): cond_op = policy_constants.CONDITION_OP_EQUALS cond_member_type = policy_constants.CONDITION_MEMBER_NET cond_key = policy_constants.CONDITION_KEY_TAG - with mock.patch.object(self.policy_api, "create") as api_call: - self.resourceApi.create( + with mock.patch.object(self.policy_api, + "create_or_update") as api_call: + self.resourceApi.create_or_overwrite( name, domain_id, description=description, cond_val=cond_val, cond_op=cond_op, @@ -270,9 +274,8 @@ class TestPolicyGroup(NsxPolicyLibTestCase): id = '222' name = 'new name' description = 'new desc' - with mock.patch.object(self.policy_api, "get", - return_value={}) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call: + with mock.patch.object(self.policy_api, + "create_or_update") as update_call: self.resourceApi.update(domain_id, id, name=name, description=description, @@ -282,7 +285,6 @@ class TestPolicyGroup(NsxPolicyLibTestCase): tenant=TEST_TENANT) expected_dict = {'display_name': name, 'description': description} - self.assert_called_with_def(get_call, expected_def) self.assert_called_with_def_and_dict( update_call, expected_def, expected_dict) @@ -292,7 +294,8 @@ class TestPolicyGroup(NsxPolicyLibTestCase): cond_val = '123' with mock.patch.object(self.policy_api, "get", return_value={}) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call: + mock.patch.object(self.policy_api, + "create_or_update") as update_call: self.resourceApi.update_condition(domain_id, id, cond_val=cond_val, tenant=TEST_TENANT) @@ -319,7 +322,8 @@ class TestPolicyGroup(NsxPolicyLibTestCase): 'operator': policy_constants.CONDITION_OP_EQUALS} with mock.patch.object(self.policy_api, "get", return_value={'expression': [old_cond]}) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call: + mock.patch.object(self.policy_api, + "create_or_update") as update_call: self.resourceApi.update_condition(domain_id, id, cond_val=None, tenant=TEST_TENANT) @@ -345,9 +349,11 @@ class TestPolicyService(NsxPolicyLibTestCase): dest_ports = [81, 82] with mock.patch.object(self.policy_api, "create_with_parent") as api_call: - self.resourceApi.create(name, description=description, - protocol=protocol, dest_ports=dest_ports, - tenant=TEST_TENANT) + self.resourceApi.create_or_overwrite(name, + description=description, + protocol=protocol, + dest_ports=dest_ports, + tenant=TEST_TENANT) exp_srv_def = policy_defs.ServiceDef(service_id=mock.ANY, name=name, description=description, @@ -400,7 +406,8 @@ class TestPolicyService(NsxPolicyLibTestCase): description = 'new desc' with mock.patch.object(self.policy_api, "get", return_value={}) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call: + mock.patch.object(self.policy_api, + "create_or_update") as update_call: self.resourceApi.update(id, name=name, description=description, @@ -424,7 +431,8 @@ class TestPolicyService(NsxPolicyLibTestCase): with mock.patch.object( self.policy_api, "get", return_value={'service_entries': [service_entry]}) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call: + mock.patch.object(self.policy_api, + "create_or_update") as update_call: self.resourceApi.update(id, protocol=protocol, dest_ports=dest_ports, @@ -457,7 +465,8 @@ class TestPolicyService(NsxPolicyLibTestCase): with mock.patch.object( self.policy_api, "get", return_value={'service_entries': [service_entry]}) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call,\ + mock.patch.object(self.policy_api, + "create_or_update") as update_call,\ mock.patch.object(self.policy_api, "list", return_value={'results': []}): self.resourceApi.update(id, @@ -474,7 +483,7 @@ class TestPolicyService(NsxPolicyLibTestCase): # update will be called for the service and entry (2 calls) expected_dict = {'display_name': name, 'description': description, - 'service_entries': [service_entry]} + 'service_entries': []} self.assert_called_with_def_and_dict( update_call, expected_def, expected_dict) @@ -505,9 +514,10 @@ class TestPolicyCommunicationProfile(NsxPolicyLibTestCase): action = 'DENY' with mock.patch.object(self.policy_api, "create_with_parent") as api_call: - self.resourceApi.create(name, description=description, - services=[service_id], action=action, - tenant=TEST_TENANT) + self.resourceApi.create_or_overwrite(name, description=description, + services=[service_id], + action=action, + tenant=TEST_TENANT) exp_srv_def = policy_defs.CommunicationProfileDef( profile_id=mock.ANY, name=name, @@ -563,7 +573,8 @@ class TestPolicyCommunicationProfile(NsxPolicyLibTestCase): description = 'new desc' with mock.patch.object(self.policy_api, "get", return_value={}) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call: + mock.patch.object(self.policy_api, + "create_or_update") as update_call: self.resourceApi.update(id, name=name, description=description, @@ -587,7 +598,8 @@ class TestPolicyCommunicationProfile(NsxPolicyLibTestCase): with mock.patch.object( self.policy_api, "get", return_value=entries_dict) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call: + mock.patch.object(self.policy_api, + "create_or_update") as update_call: self.resourceApi.update(id, services=[service_id], action=action, @@ -620,7 +632,8 @@ class TestPolicyCommunicationProfile(NsxPolicyLibTestCase): with mock.patch.object( self.policy_api, "get", return_value=entries_dict) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call: + mock.patch.object(self.policy_api, + "create_or_update") as update_call: self.resourceApi.update(id, name=name, description=description, @@ -635,7 +648,7 @@ class TestPolicyCommunicationProfile(NsxPolicyLibTestCase): # update will be called for the service and entry (2 calls) expected_dict = {'display_name': name, 'description': description, - 'communication_profile_entries': [profile_entry]} + 'communication_profile_entries': []} self.assert_called_with_def_and_dict( update_call, expected_def, expected_dict) @@ -668,15 +681,17 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase): seq_num = 7 profile_id = 'c1' list_return_value = {'results': [{'sequence_number': 1}]} - with mock.patch.object(self.policy_api, "create") as api_call,\ + with mock.patch.object(self.policy_api, + "create_or_update") as api_call,\ mock.patch.object(self.policy_api, "list", return_value=list_return_value): - self.resourceApi.create(name, domain_id, description=description, - sequence_number=seq_num, - profile_id=profile_id, - source_groups=[source_group], - dest_groups=[dest_group], - tenant=TEST_TENANT) + self.resourceApi.create_or_overwrite(name, domain_id, + description=description, + sequence_number=seq_num, + profile_id=profile_id, + source_groups=[source_group], + dest_groups=[dest_group], + tenant=TEST_TENANT) expected_def = policy_defs.CommunicationMapEntryDef( domain_id=domain_id, map_id=mock.ANY, @@ -689,6 +704,36 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase): tenant=TEST_TENANT) self.assert_called_with_def(api_call, expected_def) + def test_create_first_seqnum(self): + domain_id = '111' + name = 'cm1' + description = 'desc' + source_group = 'g1' + dest_group = 'g2' + profile_id = 'c1' + with mock.patch.object(self.policy_api, + "create_or_update") as api_call, \ + mock.patch.object(self.resourceApi, "list", return_value=[]): + self.resourceApi.create_or_overwrite(name, domain_id, + description=description, + profile_id=profile_id, + source_groups=[source_group], + dest_groups=[dest_group], + tenant=TEST_TENANT) + + expected_def = policy_defs.CommunicationMapEntryDef( + domain_id=domain_id, + map_id=mock.ANY, + name=name, + description=description, + sequence_number=1, + profile_id=profile_id, + source_groups=[source_group], + dest_groups=[dest_group], + tenant=TEST_TENANT) + + self.assert_called_with_def(api_call, expected_def) + def test_create_without_seqnum(self): domain_id = '111' name = 'cm1' @@ -698,12 +743,14 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase): profile_id = 'c1' with mock.patch.object(self.policy_api, "create_with_parent") as api_call, \ - mock.patch.object(self.resourceApi, "list", return_value=[]): - self.resourceApi.create(name, domain_id, description=description, - profile_id=profile_id, - source_groups=[source_group], - dest_groups=[dest_group], - tenant=TEST_TENANT) + mock.patch.object(self.resourceApi, "_get_last_seq_num", + return_value=-1): + self.resourceApi.create_or_overwrite(name, domain_id, + description=description, + profile_id=profile_id, + source_groups=[source_group], + dest_groups=[dest_group], + tenant=TEST_TENANT) expected_map_def = policy_defs.CommunicationMapDef( domain_id=domain_id, @@ -755,16 +802,18 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase): obj = self.resourceApi.get_by_name(domain_id, name, tenant=TEST_TENANT) self.assertIsNotNone(obj) - expected_def = policy_defs.CommunicationMapDef(domain_id, - tenant=TEST_TENANT) + expected_def = policy_defs.CommunicationMapEntryDef( + domain_id, + tenant=TEST_TENANT) self.assert_called_with_def(api_call, expected_def) def test_list(self): domain_id = '111' with mock.patch.object(self.policy_api, "list") as api_call: self.resourceApi.list(domain_id, tenant=TEST_TENANT) - expected_def = policy_defs.CommunicationMapDef(domain_id=domain_id, - tenant=TEST_TENANT) + expected_def = policy_defs.CommunicationMapEntryDef( + domain_id=domain_id, + tenant=TEST_TENANT) self.assert_called_with_def(api_call, expected_def) def test_update(self): @@ -777,7 +826,8 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase): profile_id = 'nc1' with mock.patch.object(self.policy_api, "get", return_value={}) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call: + mock.patch.object(self.policy_api, + "create_or_update") as update_call: self.resourceApi.update(domain_id, id, name=name, description=description, @@ -817,12 +867,15 @@ class TestPolicyEnforcementPoint(NsxPolicyLibTestCase): ip_address = '1.1.1.1' username = 'admin' password = 'zzz' - with mock.patch.object(self.policy_api, "create") as api_call: - self.resourceApi.create(name, description=description, - ip_address=ip_address, - username=username, - password=password, - tenant=TEST_TENANT) + with mock.patch.object(self.policy_api, + "create_or_update") as api_call: + self.resourceApi.create_or_overwrite( + name, description=description, + ip_address=ip_address, + username=username, + password=password, + tenant=TEST_TENANT) + expected_def = policy_defs.EnforcementPointDef( ep_id=mock.ANY, name=name, @@ -870,9 +923,8 @@ class TestPolicyEnforcementPoint(NsxPolicyLibTestCase): name = 'new name' username = 'admin' password = 'zzz' - with mock.patch.object(self.policy_api, "get", - return_value={}) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call: + with mock.patch.object(self.policy_api, + "create_or_update") as update_call: self.resourceApi.update(id, name=name, username=username, @@ -883,7 +935,6 @@ class TestPolicyEnforcementPoint(NsxPolicyLibTestCase): expected_dict = {'display_name': name, 'username': username, 'password': password} - self.assert_called_with_def(get_call, expected_def) self.assert_called_with_def_and_dict( update_call, expected_def, expected_dict) @@ -899,11 +950,13 @@ class TestPolicyDeploymentMap(NsxPolicyLibTestCase): description = 'desc' domain_id = 'domain1' ep_id = 'ep1' - with mock.patch.object(self.policy_api, "create") as api_call: - self.resourceApi.create(name, description=description, - ep_id=ep_id, - domain_id=domain_id, - tenant=TEST_TENANT) + with mock.patch.object(self.policy_api, + "create_or_update") as api_call: + self.resourceApi.create_or_overwrite(name, + description=description, + ep_id=ep_id, + domain_id=domain_id, + tenant=TEST_TENANT) expected_def = policy_defs.DeploymentMapDef( map_id=mock.ANY, name=name, @@ -950,9 +1003,8 @@ class TestPolicyDeploymentMap(NsxPolicyLibTestCase): name = 'new name' domain_id = 'domain2' ep_id = 'ep2' - with mock.patch.object(self.policy_api, "get", - return_value={}) as get_call,\ - mock.patch.object(self.policy_api, "update") as update_call: + with mock.patch.object(self.policy_api, + "create_or_update") as update_call: self.resourceApi.update(id, name=name, ep_id=ep_id, @@ -966,6 +1018,5 @@ class TestPolicyDeploymentMap(NsxPolicyLibTestCase): expected_dict = {'display_name': name, 'enforcement_point_paths': [ep_path], 'domain_path': domain_path} - self.assert_called_with_def(get_call, expected_def) self.assert_called_with_def_and_dict( update_call, expected_def, expected_dict) diff --git a/vmware_nsxlib/v3/__init__.py b/vmware_nsxlib/v3/__init__.py index faf06d71..2b0b4865 100644 --- a/vmware_nsxlib/v3/__init__.py +++ b/vmware_nsxlib/v3/__init__.py @@ -49,7 +49,8 @@ class NsxLibBase(object): self.client = client.NSX3Client( self.cluster, nsx_api_managers=self.nsxlib_config.nsx_api_managers, - max_attempts=self.nsxlib_config.max_attempts) + max_attempts=self.nsxlib_config.max_attempts, + url_path_base=self.client_url_prefix) self.general_apis = utils.NsxLibApiBase( self.client, self.nsxlib_config) @@ -63,7 +64,12 @@ class NsxLibBase(object): def set_config(self, nsxlib_config): """Set config user provided and extend it according to application""" self.nsxlib_config = nsxlib_config - self.nsxlib_config.extend(keepalive_section=self.keepalive_section) + self.nsxlib_config.extend(keepalive_section=self.keepalive_section, + url_base=self.client_url_prefix) + + @abc.abstractproperty + def client_url_prefix(self): + pass @abc.abstractproperty def keepalive_section(self): @@ -202,6 +208,10 @@ class NsxLib(NsxLibBase): return False + @property + def client_url_prefix(self): + return client.NSX3Client.NSX_V1_API_PREFIX + class NsxPolicyLib(NsxLibBase): @@ -231,3 +241,7 @@ class NsxPolicyLib(NsxLibBase): return True return False + + @property + def client_url_prefix(self): + return client.NSX3Client.NSX_POLICY_V1_API_PREFIX diff --git a/vmware_nsxlib/v3/client.py b/vmware_nsxlib/v3/client.py index 69122fa1..51339559 100644 --- a/vmware_nsxlib/v3/client.py +++ b/vmware_nsxlib/v3/client.py @@ -226,13 +226,15 @@ class JSONRESTClient(RESTClient): class NSX3Client(JSONRESTClient): - _NSX_V1_API_PREFIX = 'api/v1/' + NSX_V1_API_PREFIX = 'api/v1/' + NSX_POLICY_V1_API_PREFIX = 'policy/api/v1/' def __init__(self, connection, url_prefix=None, default_headers=None, nsx_api_managers=None, max_attempts=utils.DEFAULT_MAX_ATTEMPTS, - client_obj=None): + client_obj=None, + url_path_base=NSX_V1_API_PREFIX): # If the client obj is defined - copy configuration from it if client_obj: @@ -242,12 +244,12 @@ class NSX3Client(JSONRESTClient): self.nsx_api_managers = nsx_api_managers or [] self.max_attempts = max_attempts - url_prefix = url_prefix or NSX3Client._NSX_V1_API_PREFIX - if url_prefix and NSX3Client._NSX_V1_API_PREFIX not in url_prefix: + url_prefix = url_prefix or url_path_base + if url_prefix and url_path_base not in url_prefix: if url_prefix.startswith('http'): - url_prefix += '/' + NSX3Client._NSX_V1_API_PREFIX + url_prefix += '/' + url_path_base else: - url_prefix = "%s/%s" % (NSX3Client._NSX_V1_API_PREFIX, + url_prefix = "%s/%s" % (url_path_base, url_prefix or '') self.max_attempts = max_attempts diff --git a/vmware_nsxlib/v3/cluster.py b/vmware_nsxlib/v3/cluster.py index 4cdc0463..1aac871d 100644 --- a/vmware_nsxlib/v3/cluster.py +++ b/vmware_nsxlib/v3/cluster.py @@ -150,7 +150,9 @@ class NSXRequestsHTTPProvider(AbstractHTTPProvider): return "%s-%s" % (requests.__title__, requests.__version__) def validate_connection(self, cluster_api, endpoint, conn): - client = nsx_client.NSX3Client(conn, url_prefix=endpoint.provider.url) + client = nsx_client.NSX3Client( + conn, url_prefix=endpoint.provider.url, + url_path_base=cluster_api.nsxlib_config.url_base) keepalive_section = cluster_api.nsxlib_config.keepalive_section result = client.get(keepalive_section, silent=True) # If keeplive section returns a list, it is assumed to be non-empty diff --git a/vmware_nsxlib/v3/config.py b/vmware_nsxlib/v3/config.py index 9fa1d417..47a754ec 100644 --- a/vmware_nsxlib/v3/config.py +++ b/vmware_nsxlib/v3/config.py @@ -118,9 +118,10 @@ class NsxLibConfig(object): 'dhcp_profile_uuid is not used by the nsxlib, and will ' 'be removed from its configuration in the future.') - def extend(self, keepalive_section): + def extend(self, keepalive_section, url_base=None): """Called by library code to initialize application-specific data""" self.keepalive_section = keepalive_section + self.url_base = url_base def _attribute_by_index(self, scalar_or_list, index): if isinstance(scalar_or_list, list): diff --git a/vmware_nsxlib/v3/policy_defs.py b/vmware_nsxlib/v3/policy_defs.py index b5e4482f..d1379b78 100644 --- a/vmware_nsxlib/v3/policy_defs.py +++ b/vmware_nsxlib/v3/policy_defs.py @@ -36,8 +36,7 @@ class ResourceDef(object): self.body = {} def get_obj_dict(self): - body = {'_revision': 0, - 'display_name': self.name, + body = {'display_name': self.name, 'description': self.description} if self.id: body['id'] = self.id @@ -67,9 +66,20 @@ class ResourceDef(object): def sub_entries_path(): pass - def update_attributes_in_body(self, body, **kwargs): - self.body = body + def _get_body_from_kwargs(self, **kwargs): + if 'body' in kwargs: + body = kwargs['body'] + else: + body = {} + return body + + def update_attributes_in_body(self, **kwargs): + self.body = self._get_body_from_kwargs(**kwargs) + if 'body' in kwargs: + del kwargs['body'] for key, value in six.iteritems(kwargs): + if key == 'body': + continue if value is not None: if key == 'name': self.body['display_name'] = value @@ -164,13 +174,16 @@ class GroupDef(ResourceDef): for condition in self.conditions] return body - def update_attributes_in_body(self, body, **kwargs): + def update_attributes_in_body(self, **kwargs): + body = self._get_body_from_kwargs(**kwargs) + if 'body' in kwargs: + del kwargs['body'] # Fix params that need special conversions if kwargs.get('conditions') is not None: body['expression'] = [cond.get_obj_dict() for cond in kwargs['conditions']] del kwargs['conditions'] - super(GroupDef, self).update_attributes_in_body(body, **kwargs) + super(GroupDef, self).update_attributes_in_body(body=body, **kwargs) class ServiceDef(ResourceDef): @@ -231,8 +244,12 @@ class L4ServiceEntryDef(ResourceDef): body['destination_ports'] = self.dest_ports return body - def update_attributes_in_body(self, body, **kwargs): + def update_attributes_in_body(self, **kwargs): # Fix params that need special conversions + body = self._get_body_from_kwargs(**kwargs) + if 'body' in kwargs: + del kwargs['body'] + if kwargs.get('protocol') is not None: body['l4_protocol'] = kwargs['protocol'].upper() del kwargs['protocol'] @@ -240,7 +257,7 @@ class L4ServiceEntryDef(ResourceDef): body['destination_ports'] = kwargs['dest_ports'] del kwargs['dest_ports'] super(L4ServiceEntryDef, self).update_attributes_in_body( - body, **kwargs) + body=body, **kwargs) class CommunicationProfileDef(ResourceDef): @@ -270,9 +287,9 @@ class CommunicationProfileDef(ResourceDef): entryDef = CommunicationProfileEntryDef() return entryDef.get_last_section_dict_key - def update_attributes_in_body(self, body, **kwargs): + def update_attributes_in_body(self, **kwargs): super(CommunicationProfileDef, self).update_attributes_in_body( - body, **kwargs) + **kwargs) # make sure entries are there entries_path = self.sub_entries_path() if entries_path not in self.body: @@ -307,12 +324,15 @@ class CommunicationProfileEntryDef(ResourceDef): body['action'] = self.action return body - def update_attributes_in_body(self, body, **kwargs): + def update_attributes_in_body(self, **kwargs): + body = self._get_body_from_kwargs(**kwargs) + if 'body' in kwargs: + del kwargs['body'] if kwargs.get('action') is not None: body['action'] = kwargs['action'].upper() del kwargs['action'] super(CommunicationProfileEntryDef, self).update_attributes_in_body( - body, **kwargs) + body=body, **kwargs) class CommunicationMapDef(ResourceDef): @@ -380,7 +400,10 @@ class CommunicationMapEntryDef(ResourceDef): body['communication_profile_path'] = self.profile_path return body - def update_attributes_in_body(self, body, **kwargs): + def update_attributes_in_body(self, **kwargs): + body = self._get_body_from_kwargs(**kwargs) + if 'body' in kwargs: + del kwargs['body'] # Fix params that need special conversions if kwargs.get('profile_id') is not None: profile_path = self.get_profile_path(kwargs['profile_id']) @@ -400,7 +423,7 @@ class CommunicationMapEntryDef(ResourceDef): del kwargs['source_groups'] super(CommunicationMapEntryDef, self).update_attributes_in_body( - body, **kwargs) + body=body, **kwargs) class EnforcementPointDef(ResourceDef): @@ -411,6 +434,7 @@ class EnforcementPointDef(ResourceDef): ip_address=None, username=None, password=None, + thumbprint=None, ep_type='NSXT', tenant=policy_constants.POLICY_INFRA_TENANT): super(EnforcementPointDef, self).__init__() @@ -422,6 +446,7 @@ class EnforcementPointDef(ResourceDef): self.username = username self.password = password self.ip_address = ip_address + self.thumbprint = thumbprint self.parent_ids = (tenant) @property @@ -433,8 +458,7 @@ class EnforcementPointDef(ResourceDef): body = super(EnforcementPointDef, self).get_obj_dict() body['id'] = self.id body['connection_info'] = [{'fqdn': 'abc', - 'thumbprint': - policy_constants.DEFAULT_THUMBPRINT, + 'thumbprint': self.thumbprint, 'username': self.username, 'password': self.password, 'ip_address': self.ip_address, @@ -443,24 +467,21 @@ class EnforcementPointDef(ResourceDef): body['resource_type'] = 'EnforcementPoint' return body - def update_attributes_in_body(self, body, **kwargs): + def update_attributes_in_body(self, **kwargs): + body = self._get_body_from_kwargs(**kwargs) + if 'body' in kwargs: + del kwargs['body'] # Fix params that need special conversions if body.get('connection_info'): body['connection_info'][0]['resource_type'] = 'NSXTConnectionInfo' - if kwargs.get('username') is not None: - body['connection_info'][0]['username'] = kwargs['username'] - del kwargs['username'] - if kwargs.get('password') is not None: - body['connection_info'][0]['password'] = kwargs['password'] - del kwargs['password'] - - if kwargs.get('ip_address') is not None: - body['connection_info'][0]['ip_address'] = kwargs['ip_address'] - del kwargs['ip_address'] + for attr in ('username', 'password', 'ip_address', 'thumbprint'): + if kwargs.get(attr) is not None: + body['connection_info'][0][attr] = kwargs[attr] + del kwargs[attr] super(EnforcementPointDef, self).update_attributes_in_body( - body, **kwargs) + body=body, **kwargs) # Currently assumes one deployment point per id @@ -497,7 +518,10 @@ class DeploymentMapDef(ResourceDef): body['enforcement_point_paths'] = [self.ep_path] return body - def update_attributes_in_body(self, body, **kwargs): + def update_attributes_in_body(self, **kwargs): + body = self._get_body_from_kwargs(**kwargs) + if 'body' in kwargs: + del kwargs['body'] # Fix params that need special conversions if kwargs.get('domain_id') is not None: domain_id = kwargs.get('domain_id') @@ -514,7 +538,7 @@ class DeploymentMapDef(ResourceDef): del kwargs['ep_id'] super(DeploymentMapDef, self).update_attributes_in_body( - body, **kwargs) + body=body, **kwargs) class NsxPolicyApi(object): @@ -522,9 +546,18 @@ class NsxPolicyApi(object): def __init__(self, client): self.client = client - def create(self, resource_def): + def create_or_update(self, resource_def): + """Create or update a policy object. + + This api will update an existing object, or create a new one if it + doesn't exist. + The policy API supports POST for update too + """ path = resource_def.get_resource_path() - return self.client.update(path, resource_def.get_obj_dict()) + body = resource_def.body + if not body: + body = resource_def.get_obj_dict() + return self.client.create(path, body) def create_with_parent(self, parent_def, resource_def): path = parent_def.get_resource_path() @@ -535,7 +568,7 @@ class NsxPolicyApi(object): else: child_dict_key = resource_def.get_last_section_dict_key body[child_dict_key] = [resource_def.get_obj_dict()] - return self.client.update(path, body) + return self.client.create(path, body) def delete(self, resource_def): path = resource_def.get_resource_path() @@ -548,8 +581,3 @@ class NsxPolicyApi(object): def list(self, resource_def): path = resource_def.get_section_path() return self.client.list(path) - - def update(self, resource_def): - path = resource_def.get_resource_path() - body = resource_def.body - return self.client.update(path, body) diff --git a/vmware_nsxlib/v3/policy_resources.py b/vmware_nsxlib/v3/policy_resources.py index 7f1443aa..5510db46 100644 --- a/vmware_nsxlib/v3/policy_resources.py +++ b/vmware_nsxlib/v3/policy_resources.py @@ -54,7 +54,7 @@ class NsxPolicyResourceBase(object): pass @abc.abstractmethod - def create(self, *args, **kwargs): + def create_or_overwrite(self, *args, **kwargs): pass @abc.abstractmethod @@ -78,14 +78,14 @@ class NsxPolicyResourceBase(object): class NsxPolicyDomainApi(NsxPolicyResourceBase): """NSX Policy Domain.""" - def create(self, name, domain_id=None, description=None, - tenant=policy_constants.POLICY_INFRA_TENANT): + def create_or_overwrite(self, name, domain_id=None, description=None, + tenant=policy_constants.POLICY_INFRA_TENANT): domain_id = self._init_obj_uuid(domain_id) domain_def = policy_defs.DomainDef(domain_id=domain_id, name=name, description=description, tenant=tenant) - return self.policy_api.create(domain_def) + return self.policy_api.create_or_update(domain_def) def delete(self, domain_id, tenant=policy_constants.POLICY_INFRA_TENANT): domain_def = policy_defs.DomainDef(domain_id, tenant=tenant) @@ -103,24 +103,22 @@ class NsxPolicyDomainApi(NsxPolicyResourceBase): tenant=policy_constants.POLICY_INFRA_TENANT): domain_def = policy_defs.DomainDef(domain_id=domain_id, tenant=tenant) - # Get the current data, and update it with the new values - domain = self.get(domain_id, tenant=tenant) - domain_def.update_attributes_in_body(domain, - name=name, + domain_def.update_attributes_in_body(name=name, description=description) # update the backend - return self.policy_api.update(domain_def) + return self.policy_api.create_or_update(domain_def) class NsxPolicyGroupApi(NsxPolicyResourceBase): """NSX Policy Group (under a Domain) with a single condition.""" - def create(self, name, domain_id, group_id=None, - description=None, - cond_val=None, - cond_key=policy_constants.CONDITION_KEY_TAG, - cond_op=policy_constants.CONDITION_OP_EQUALS, - cond_member_type=policy_constants.CONDITION_MEMBER_PORT, - tenant=policy_constants.POLICY_INFRA_TENANT): + def create_or_overwrite( + self, name, domain_id, group_id=None, + description=None, + cond_val=None, + cond_key=policy_constants.CONDITION_KEY_TAG, + cond_op=policy_constants.CONDITION_OP_EQUALS, + cond_member_type=policy_constants.CONDITION_MEMBER_PORT, + tenant=policy_constants.POLICY_INFRA_TENANT): """Create a group with/without a condition. Empty condition value will result a group with no condition. @@ -142,7 +140,7 @@ class NsxPolicyGroupApi(NsxPolicyResourceBase): description=description, conditions=conditions, tenant=tenant) - return self.policy_api.create(group_def) + return self.policy_api.create_or_update(group_def) def delete(self, domain_id, group_id, tenant=policy_constants.POLICY_INFRA_TENANT): @@ -180,12 +178,10 @@ class NsxPolicyGroupApi(NsxPolicyResourceBase): group_def = policy_defs.GroupDef(domain_id=domain_id, group_id=group_id, tenant=tenant) - # Get the current data, and update it with the new values - group = self.get(domain_id, group_id, tenant=tenant) - group_def.update_attributes_in_body(group, name=name, + group_def.update_attributes_in_body(name=name, description=description) # update the backend - return self.policy_api.update(group_def) + return self.policy_api.create_or_update(group_def) def update_condition( self, domain_id, group_id, @@ -212,10 +208,11 @@ class NsxPolicyGroupApi(NsxPolicyResourceBase): else: conditions = [] # Get the current data, and update it with the new values + # We need to do that here because of the conditions data group = self.get(domain_id, group_id, tenant=tenant) - group_def.update_attributes_in_body(group, conditions=conditions) + group_def.update_attributes_in_body(body=group, conditions=conditions) # update the backend - return self.policy_api.update(group_def) + return self.policy_api.create_or_update(group_def) class NsxPolicyL4ServiceApi(NsxPolicyResourceBase): @@ -225,9 +222,9 @@ class NsxPolicyL4ServiceApi(NsxPolicyResourceBase): and multiple service entries per service. At this point this is not supported here. """ - def create(self, name, service_id=None, description=None, - protocol=policy_constants.TCP, dest_ports=None, - tenant=policy_constants.POLICY_INFRA_TENANT): + def create_or_overwrite(self, name, service_id=None, description=None, + protocol=policy_constants.TCP, dest_ports=None, + tenant=policy_constants.POLICY_INFRA_TENANT): service_id = self._init_obj_uuid(service_id) service_def = policy_defs.ServiceDef(service_id=service_id, name=name, @@ -266,16 +263,17 @@ class NsxPolicyL4ServiceApi(NsxPolicyResourceBase): name=None, description=None, protocol=None, dest_ports=None, tenant=policy_constants.POLICY_INFRA_TENANT): + # TODO(asarfaty): This action fails on backend entry_id = srv_entry['id'] entry_def = policy_defs.L4ServiceEntryDef(service_id=service_id, service_entry_id=entry_id, tenant=tenant) - entry_def.update_attributes_in_body(srv_entry, name=name, + entry_def.update_attributes_in_body(body=srv_entry, name=name, description=description, protocol=protocol, dest_ports=dest_ports) - self.policy_api.update(entry_def) + self.policy_api.create_or_update(entry_def) def update(self, service_id, name=None, description=None, protocol=None, dest_ports=None, @@ -288,11 +286,11 @@ class NsxPolicyL4ServiceApi(NsxPolicyResourceBase): # update the service itself service_def = policy_defs.ServiceDef(service_id=service_id, tenant=tenant) - service_def.update_attributes_in_body(service, name=name, + service_def.update_attributes_in_body(name=name, description=description) # update the backend - updated_service = self.policy_api.update(service_def) + updated_service = self.policy_api.create_or_update(service_def) else: updated_service = service @@ -320,9 +318,10 @@ class NsxPolicyCommunicationProfileApi(NsxPolicyResourceBase): profile. At this point this is not supported here. """ - def create(self, name, profile_id=None, description=None, - services=None, action=policy_constants.ACTION_ALLOW, - tenant=policy_constants.POLICY_INFRA_TENANT): + def create_or_overwrite(self, name, profile_id=None, description=None, + services=None, + action=policy_constants.ACTION_ALLOW, + tenant=policy_constants.POLICY_INFRA_TENANT): """Create a Communication profile with a single entry. Services should be a list of service ids @@ -371,12 +370,13 @@ class NsxPolicyCommunicationProfileApi(NsxPolicyResourceBase): profile_id=profile_id, profile_entry_id=entry_id, tenant=tenant) - entry_def.update_attributes_in_body(profile_entry, name=name, + entry_def.update_attributes_in_body(body=profile_entry, + name=name, description=description, services=services, action=action) - self.policy_api.update(entry_def) + self.policy_api.create_or_update(entry_def) def update(self, profile_id, name=None, description=None, services=None, action=None, @@ -388,11 +388,11 @@ class NsxPolicyCommunicationProfileApi(NsxPolicyResourceBase): # update the profile itself profile_def = policy_defs.CommunicationProfileDef( profile_id=profile_id, tenant=tenant) - profile_def.update_attributes_in_body(profile, name=name, + profile_def.update_attributes_in_body(name=name, description=description) # update the backend - updated_profile = self.policy_api.update(profile_def) + updated_profile = self.policy_api.create_or_update(profile_def) else: updated_profile = profile @@ -419,22 +419,28 @@ class NsxPolicyCommunicationMapApi(NsxPolicyResourceBase): def _get_last_seq_num(self, domain_id, tenant=policy_constants.POLICY_INFRA_TENANT): # get the current entries, and choose the next unused sequence number - communication_maps = self.list(domain_id, tenant=tenant) - if not len(communication_maps): + try: + com_entries = self.list(domain_id, tenant=tenant) + except exceptions.ResourceNotFound: + return -1 + if not com_entries: return 0 - - seq_nums = [int(cm['sequence_number']) for cm in communication_maps] + seq_nums = [int(cm['sequence_number']) for cm in com_entries] seq_nums.sort() return seq_nums[-1] - def create(self, name, domain_id, map_id=None, - description=None, sequence_number=None, profile_id=None, - source_groups=None, dest_groups=None, - tenant=policy_constants.POLICY_INFRA_TENANT): - """Create a CommunicationtMap. + def create_or_overwrite(self, name, domain_id, map_id=None, + description=None, sequence_number=None, + profile_id=None, + source_groups=None, dest_groups=None, + tenant=policy_constants.POLICY_INFRA_TENANT): + """Create CommunicationMapEntry. source_groups/dest_groups should be a list of group ids belonging to the domain. + NOTE: In multi-connection environment, it is recommended to execute + this call under lock to prevent race condition where two entries + end up with same sequence number. """ # Validate and convert inputs map_id = self._init_obj_uuid(map_id) @@ -447,7 +453,10 @@ class NsxPolicyCommunicationMapApi(NsxPolicyResourceBase): # get the next available sequence number last_sequence = self._get_last_seq_num(domain_id, tenant=tenant) if not sequence_number: - sequence_number = last_sequence + 1 + if last_sequence < 0: + sequence_number = 1 + else: + sequence_number = last_sequence + 1 entry_def = policy_defs.CommunicationMapEntryDef( domain_id=domain_id, @@ -460,12 +469,14 @@ class NsxPolicyCommunicationMapApi(NsxPolicyResourceBase): profile_id=profile_id, tenant=tenant) - if last_sequence == 0: + if last_sequence < 0: # if communication map is absent, we need to create it map_def = policy_defs.CommunicationMapDef(domain_id, tenant) - return self.policy_api.create_with_parent(map_def, entry_def) + map_result = self.policy_api.create_with_parent(map_def, entry_def) + # return the created entry + return map_result['communication_entries'][0] - return self.policy_api.create(entry_def) + return self.policy_api.create_or_update(entry_def) def delete(self, domain_id, map_id, tenant=policy_constants.POLICY_INFRA_TENANT): @@ -485,14 +496,14 @@ class NsxPolicyCommunicationMapApi(NsxPolicyResourceBase): def get_by_name(self, domain_id, name, tenant=policy_constants.POLICY_INFRA_TENANT): - """Return first communication map matched by name of this domain""" + """Return first communication map entry matched by name""" return super(NsxPolicyCommunicationMapApi, self).get_by_name( name, domain_id, tenant=tenant) def list(self, domain_id, tenant=policy_constants.POLICY_INFRA_TENANT): """List all the map entries of a specific domain.""" - map_def = policy_defs.CommunicationMapDef( + map_def = policy_defs.CommunicationMapEntryDef( domain_id=domain_id, tenant=tenant) return self.policy_api.list(map_def)['results'] @@ -505,10 +516,18 @@ class NsxPolicyCommunicationMapApi(NsxPolicyResourceBase): domain_id=domain_id, map_id=map_id, tenant=tenant) + # Get the current data, and update it with the new values - comm_map = self.get(domain_id, map_id, tenant=tenant) + try: + comm_map = self.get(domain_id, map_id, tenant=tenant) + except exceptions.ResourceNotFound: + return self.create_or_overwrite(name, domain_id, map_id, + description, sequence_number, + profile_id, source_groups, + dest_groups, tenant) + map_def.update_attributes_in_body( - comm_map, + body=comm_map, name=name, description=description, sequence_number=sequence_number, @@ -517,16 +536,16 @@ class NsxPolicyCommunicationMapApi(NsxPolicyResourceBase): dest_groups=dest_groups) # update the backend - return self.policy_api.update(map_def) + return self.policy_api.create_or_update(map_def) class NsxPolicyEnforcementPointApi(NsxPolicyResourceBase): """NSX Policy Enforcement Point.""" - def create(self, name, ep_id=None, description=None, - ip_address=None, username=None, - password=None, - tenant=policy_constants.POLICY_INFRA_TENANT): + def create_or_overwrite(self, name, ep_id=None, description=None, + ip_address=None, username=None, + password=None, thumbprint=None, + tenant=policy_constants.POLICY_INFRA_TENANT): if not ip_address or not username or password is None: err_msg = (_("Cannot create an enforcement point without " "ip_address, username and password")) @@ -539,8 +558,9 @@ class NsxPolicyEnforcementPointApi(NsxPolicyResourceBase): ip_address=ip_address, username=username, password=password, + thumbprint=thumbprint, tenant=tenant) - return self.policy_api.create(ep_def) + return self.policy_api.create_or_update(ep_def) def delete(self, ep_id, tenant=policy_constants.POLICY_INFRA_TENANT): @@ -559,7 +579,8 @@ class NsxPolicyEnforcementPointApi(NsxPolicyResourceBase): return self.policy_api.list(ep_def)['results'] def update(self, ep_id, name=None, description=None, - ip_address=None, username=None, password=None, + ip_address=None, username=None, + password=None, thumbprint=None, tenant=policy_constants.POLICY_INFRA_TENANT): """Update the enforcement point. @@ -572,24 +593,22 @@ class NsxPolicyEnforcementPointApi(NsxPolicyResourceBase): raise exceptions.ManagerError(details=err_msg) ep_def = policy_defs.EnforcementPointDef(ep_id=ep_id, tenant=tenant) - # Get the current data, and update it with the new values - ep = self.get(ep_id, tenant=tenant) - ep_def.update_attributes_in_body(ep, - name=name, + ep_def.update_attributes_in_body(name=name, description=description, ip_address=ip_address, username=username, - password=password) + password=password, + thumbprint=thumbprint) # update the backend - return self.policy_api.update(ep_def) + return self.policy_api.create_or_update(ep_def) class NsxPolicyDeploymentMapApi(NsxPolicyResourceBase): """NSX Policy Deployment Map.""" - def create(self, name, map_id=None, description=None, - ep_id=None, domain_id=None, - tenant=policy_constants.POLICY_INFRA_TENANT): + def create_or_overwrite(self, name, map_id=None, description=None, + ep_id=None, domain_id=None, + tenant=policy_constants.POLICY_INFRA_TENANT): map_id = self._init_obj_uuid(map_id) map_def = policy_defs.DeploymentMapDef( map_id=map_id, @@ -598,7 +617,7 @@ class NsxPolicyDeploymentMapApi(NsxPolicyResourceBase): ep_id=ep_id, domain_id=domain_id, tenant=tenant) - return self.policy_api.create(map_def) + return self.policy_api.create_or_update(map_def) def delete(self, map_id, tenant=policy_constants.POLICY_INFRA_TENANT): @@ -621,12 +640,9 @@ class NsxPolicyDeploymentMapApi(NsxPolicyResourceBase): tenant=policy_constants.POLICY_INFRA_TENANT): map_def = policy_defs.DeploymentMapDef( map_id=map_id, tenant=tenant) - # Get the current data, and update it with the new values - map_obj = self.get(map_id, tenant=tenant) - map_def.update_attributes_in_body(map_obj, - name=name, + map_def.update_attributes_in_body(name=name, description=description, ep_id=ep_id, domain_id=domain_id) # update the backend - return self.policy_api.update(map_def) + return self.policy_api.create_or_update(map_def)