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 <asarfaty@vmware.com>
Change-Id: Ic44308d1df8944dfbcfa2a613ed8e1847eee3715
changes/86/461186/10
Anna Khmelnitsky 6 years ago committed by Adit Sarfaty
parent 373506a8d4
commit cdaad65004
  1. 73
      vmware_nsxlib/tests/unit/v3/test_policy_api.py
  2. 211
      vmware_nsxlib/tests/unit/v3/test_policy_resources.py
  3. 18
      vmware_nsxlib/v3/__init__.py
  4. 14
      vmware_nsxlib/v3/client.py
  5. 4
      vmware_nsxlib/v3/cluster.py
  6. 3
      vmware_nsxlib/v3/config.py
  7. 104
      vmware_nsxlib/v3/policy_defs.py
  8. 168
      vmware_nsxlib/v3/policy_resources.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)

@ -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)

@ -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

@ -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

@ -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

@ -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):

@ -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)

@ -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: