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
This commit is contained in:
Anna Khmelnitsky 2017-04-28 18:45:16 -07:00 committed by Adit Sarfaty
parent 373506a8d4
commit cdaad65004
8 changed files with 350 additions and 245 deletions

View File

@ -18,14 +18,14 @@ from vmware_nsxlib.v3 import client
from vmware_nsxlib.v3 import policy_constants from vmware_nsxlib.v3 import policy_constants
from vmware_nsxlib.v3 import policy_defs as policy 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): class TestPolicyApi(nsxlib_testcase.NsxClientTestCase):
def setUp(self): def setUp(self):
self.client = self.new_mocked_client(client.NSX3Client, self.client = self.new_mocked_client(client.NSX3Client,
url_prefix='api/v1/') url_prefix='policy/api/v1/')
self.policy_api = policy.NsxPolicyApi(self.client) self.policy_api = policy.NsxPolicyApi(self.client)
super(TestPolicyApi, self).setUp() super(TestPolicyApi, self).setUp()
@ -43,8 +43,8 @@ class TestPolicyDomain(TestPolicyApi):
'archaea', 'archaea',
'prokaryotic cells', 'prokaryotic cells',
'typically characterized by membrane lipids') 'typically characterized by membrane lipids')
self.policy_api.create(domain_def) self.policy_api.create_or_update(domain_def)
self.assert_json_call('PUT', self.client, self.assert_json_call('POST', self.client,
'infra/domains/archaea', 'infra/domains/archaea',
data=domain_def.get_obj_dict()) data=domain_def.get_obj_dict())
@ -73,8 +73,8 @@ class TestPolicyGroup(TestPolicyApi):
'eukarya', 'eukarya',
'cats', 'cats',
'felis catus') 'felis catus')
self.policy_api.create(group_def) self.policy_api.create_or_update(group_def)
self.assert_json_call('PUT', self.client, self.assert_json_call('POST', self.client,
'infra/domains/eukarya/groups/cats', 'infra/domains/eukarya/groups/cats',
data=group_def.get_obj_dict()) data=group_def.get_obj_dict())
@ -89,7 +89,7 @@ class TestPolicyGroup(TestPolicyApi):
self.policy_api.create_with_parent(domain_def, group_def) self.policy_api.create_with_parent(domain_def, group_def)
data = domain_def.get_obj_dict() data = domain_def.get_obj_dict()
data['groups'] = [group_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', 'infra/domains/eukarya',
data=data) data=data)
@ -110,14 +110,12 @@ class TestPolicyGroup(TestPolicyApi):
expected_group = {'id': 'dogs', expected_group = {'id': 'dogs',
'display_name': None, 'display_name': None,
'description': None, 'description': None,
'expression': [expected_condition], 'expression': [expected_condition]}
'_revision': 0}
expected_data = {'id': 'eukarya', expected_data = {'id': 'eukarya',
'display_name': None, 'display_name': None,
'description': None, 'description': None,
'groups': [expected_group], 'groups': [expected_group]}
'_revision': 0} self.assert_json_call('POST', self.client,
self.assert_json_call('PUT', self.client,
'infra/domains/eukarya', 'infra/domains/eukarya',
data=expected_data) data=expected_data)
@ -134,7 +132,7 @@ class TestPolicyGroup(TestPolicyApi):
self.policy_api.create_with_parent(domain_def, group_def) self.policy_api.create_with_parent(domain_def, group_def)
data = domain_def.get_obj_dict() data = domain_def.get_obj_dict()
data['groups'] = [group_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', 'infra/domains/eukarya',
data=data) data=data)
@ -149,8 +147,8 @@ class TestPolicyService(TestPolicyApi):
def test_create(self): def test_create(self):
service_def = policy.ServiceDef('roomservice') service_def = policy.ServiceDef('roomservice')
self.policy_api.create(service_def) self.policy_api.create_or_update(service_def)
self.assert_json_call('PUT', self.client, self.assert_json_call('POST', self.client,
'infra/services/roomservice', 'infra/services/roomservice',
data=service_def.get_obj_dict()) data=service_def.get_obj_dict())
@ -167,14 +165,12 @@ class TestPolicyService(TestPolicyApi):
'display_name': 'room http', 'display_name': 'room http',
'description': None, 'description': None,
'l4_protocol': 'TCP', 'l4_protocol': 'TCP',
'destination_ports': [80, 8080], 'destination_ports': [80, 8080]}
'_revision': 0}
expected_data = {'id': 'roomservice', expected_data = {'id': 'roomservice',
'display_name': None, 'display_name': None,
'description': None, 'description': None,
'service_entries': [expected_entry], 'service_entries': [expected_entry]}
'_revision': 0} self.assert_json_call('POST', self.client,
self.assert_json_call('PUT', self.client,
'infra/services/roomservice', 'infra/services/roomservice',
data=expected_data) data=expected_data)
@ -183,8 +179,8 @@ class TestPolicyCommunicationProfile(TestPolicyApi):
def test_create(self): def test_create(self):
profile_def = policy.CommunicationProfileDef('rental') profile_def = policy.CommunicationProfileDef('rental')
self.policy_api.create(profile_def) self.policy_api.create_or_update(profile_def)
self.assert_json_call('PUT', self.client, self.assert_json_call('POST', self.client,
'infra/communication-profiles/rental', 'infra/communication-profiles/rental',
data=profile_def.get_obj_dict()) data=profile_def.get_obj_dict())
@ -201,14 +197,12 @@ class TestPolicyCommunicationProfile(TestPolicyApi):
'display_name': None, 'display_name': None,
'description': 'includes roomservice', 'description': 'includes roomservice',
'services': ["roomservice"], 'services': ["roomservice"],
'action': 'ALLOW', 'action': 'ALLOW'}
'_revision': 0}
expected_data = {'id': 'rental', expected_data = {'id': 'rental',
'display_name': None, 'display_name': None,
'description': None, 'description': None,
'communication_profile_entries': [expected_entry], 'communication_profile_entries': [expected_entry]}
'_revision': 0} self.assert_json_call('POST', self.client,
self.assert_json_call('PUT', self.client,
'infra/communication-profiles/rental', 'infra/communication-profiles/rental',
data=expected_data) data=expected_data)
@ -233,8 +227,7 @@ class TestPolicyCommunicationMap(TestPolicyApi):
dest_groups=["group3"], dest_groups=["group3"],
profile_id="profile2") profile_id="profile2")
self.expected_data1 = {'_revision': 0, self.expected_data1 = {'id': 'cm1',
'id': 'cm1',
'display_name': None, 'display_name': None,
'description': None, 'description': None,
'sequence_number': 12, 'sequence_number': 12,
@ -246,8 +239,7 @@ class TestPolicyCommunicationMap(TestPolicyApi):
'communication_profile_path': 'communication_profile_path':
'/infra/communication-profiles/profile1'} '/infra/communication-profiles/profile1'}
self.expected_data2 = {'_revision': 0, self.expected_data2 = {'id': 'cm2',
'id': 'cm2',
'display_name': None, 'display_name': None,
'description': None, 'description': None,
'sequence_number': 13, 'sequence_number': 13,
@ -265,7 +257,7 @@ class TestPolicyCommunicationMap(TestPolicyApi):
self.policy_api.create_with_parent(map_def, self.entry1) self.policy_api.create_with_parent(map_def, self.entry1)
expected_data = map_def.get_obj_dict() expected_data = map_def.get_obj_dict()
expected_data['communication_entries'] = [self.expected_data1] 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', 'infra/domains/d1/communication-map',
data=expected_data) data=expected_data)
@ -277,14 +269,14 @@ class TestPolicyCommunicationMap(TestPolicyApi):
expected_data = map_def.get_obj_dict() expected_data = map_def.get_obj_dict()
expected_data['communication_entries'] = [self.expected_data1, expected_data['communication_entries'] = [self.expected_data1,
self.expected_data2] self.expected_data2]
self.assert_json_call('PUT', self.client, self.assert_json_call('POST', self.client,
'infra/domains/d1/communication-map', 'infra/domains/d1/communication-map',
data=expected_data) data=expected_data)
def test_update_entry(self): 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/' 'infra/domains/d1/communication-map/'
'communication-entries/cm1', 'communication-entries/cm1',
data=self.expected_data1) data=self.expected_data1)
@ -305,9 +297,9 @@ class TestPolicyEnforcementPoint(TestPolicyApi):
username='admin', username='admin',
password='a') password='a')
self.policy_api.create(ep_def) self.policy_api.create_or_update(ep_def)
ep_path = policy.EnforcementPointDef('ep1').get_resource_path() ep_path = policy.EnforcementPointDef('ep1').get_resource_path()
self.assert_json_call('PUT', self.client, self.assert_json_call('POST', self.client,
ep_path, ep_path,
data=ep_def.get_obj_dict()) data=ep_def.get_obj_dict())
@ -317,15 +309,14 @@ class TestPolicyDeploymentMap(TestPolicyApi):
def test_create(self): def test_create(self):
map_def = policy.DeploymentMapDef('dm1', domain_id='d1', ep_id='ep1') 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() ep_path = policy.EnforcementPointDef('ep1').get_resource_full_path()
expected_data = {'_revision': 0, expected_data = {'id': 'dm1',
'id': 'dm1',
'display_name': None, 'display_name': None,
'description': None, 'description': None,
'domain_path': '/infra/domains/d1', 'domain_path': '/infra/domains/d1',
'enforcement_point_paths': [ep_path]} 'enforcement_point_paths': [ep_path]}
self.assert_json_call('PUT', self.client, self.assert_json_call('POST', self.client,
'infra/domaindeploymentmap/dm1', 'infra/domaindeploymentmap/dm1',
data=expected_data) data=expected_data)

View File

@ -82,8 +82,9 @@ class TestPolicyDomain(NsxPolicyLibTestCase):
name = 'd1' name = 'd1'
description = 'desc' description = 'desc'
id = '111' id = '111'
with mock.patch.object(self.policy_api, "create") as api_call: with mock.patch.object(self.policy_api,
self.resourceApi.create(name, "create_or_update") as api_call:
self.resourceApi.create_or_overwrite(name,
domain_id=id, domain_id=id,
description=description, description=description,
tenant=TEST_TENANT) tenant=TEST_TENANT)
@ -96,8 +97,9 @@ class TestPolicyDomain(NsxPolicyLibTestCase):
def test_create_without_id(self): def test_create_without_id(self):
name = 'd1' name = 'd1'
description = 'desc' description = 'desc'
with mock.patch.object(self.policy_api, "create") as api_call: with mock.patch.object(self.policy_api,
self.resourceApi.create(name, description=description, "create_or_update") as api_call:
self.resourceApi.create_or_overwrite(name, description=description,
tenant=TEST_TENANT) tenant=TEST_TENANT)
expected_def = policy_defs.DomainDef(domain_id=mock.ANY, expected_def = policy_defs.DomainDef(domain_id=mock.ANY,
name=name, name=name,
@ -141,9 +143,8 @@ class TestPolicyDomain(NsxPolicyLibTestCase):
id = '111' id = '111'
name = 'new name' name = 'new name'
description = 'new desc' description = 'new desc'
with mock.patch.object(self.policy_api, "get", with mock.patch.object(self.policy_api,
return_value={}) as get_call,\ "create_or_update") as update_call:
mock.patch.object(self.policy_api, "update") as update_call:
self.resourceApi.update(id, self.resourceApi.update(id,
name=name, name=name,
description=description, description=description,
@ -152,7 +153,6 @@ class TestPolicyDomain(NsxPolicyLibTestCase):
tenant=TEST_TENANT) tenant=TEST_TENANT)
expected_dict = {'display_name': name, expected_dict = {'display_name': name,
'description': description} 'description': description}
self.assert_called_with_def(get_call, expected_def)
self.assert_called_with_def_and_dict( self.assert_called_with_def_and_dict(
update_call, expected_def, expected_dict) update_call, expected_def, expected_dict)
@ -168,8 +168,9 @@ class TestPolicyGroup(NsxPolicyLibTestCase):
name = 'g1' name = 'g1'
description = 'desc' description = 'desc'
id = '222' id = '222'
with mock.patch.object(self.policy_api, "create") as api_call: with mock.patch.object(self.policy_api,
self.resourceApi.create(name, "create_or_update") as api_call:
self.resourceApi.create_or_overwrite(name,
domain_id, domain_id,
group_id=id, group_id=id,
description=description, description=description,
@ -186,8 +187,10 @@ class TestPolicyGroup(NsxPolicyLibTestCase):
domain_id = '111' domain_id = '111'
name = 'g1' name = 'g1'
description = 'desc' description = 'desc'
with mock.patch.object(self.policy_api, "create") as api_call: with mock.patch.object(self.policy_api,
self.resourceApi.create(name, domain_id, description=description, "create_or_update") as api_call:
self.resourceApi.create_or_overwrite(name, domain_id,
description=description,
tenant=TEST_TENANT) tenant=TEST_TENANT)
expected_def = policy_defs.GroupDef(domain_id=domain_id, expected_def = policy_defs.GroupDef(domain_id=domain_id,
group_id=mock.ANY, group_id=mock.ANY,
@ -205,8 +208,9 @@ class TestPolicyGroup(NsxPolicyLibTestCase):
cond_op = policy_constants.CONDITION_OP_EQUALS cond_op = policy_constants.CONDITION_OP_EQUALS
cond_member_type = policy_constants.CONDITION_MEMBER_NET cond_member_type = policy_constants.CONDITION_MEMBER_NET
cond_key = policy_constants.CONDITION_KEY_TAG cond_key = policy_constants.CONDITION_KEY_TAG
with mock.patch.object(self.policy_api, "create") as api_call: with mock.patch.object(self.policy_api,
self.resourceApi.create( "create_or_update") as api_call:
self.resourceApi.create_or_overwrite(
name, domain_id, description=description, name, domain_id, description=description,
cond_val=cond_val, cond_val=cond_val,
cond_op=cond_op, cond_op=cond_op,
@ -270,9 +274,8 @@ class TestPolicyGroup(NsxPolicyLibTestCase):
id = '222' id = '222'
name = 'new name' name = 'new name'
description = 'new desc' description = 'new desc'
with mock.patch.object(self.policy_api, "get", with mock.patch.object(self.policy_api,
return_value={}) as get_call,\ "create_or_update") as update_call:
mock.patch.object(self.policy_api, "update") as update_call:
self.resourceApi.update(domain_id, id, self.resourceApi.update(domain_id, id,
name=name, name=name,
description=description, description=description,
@ -282,7 +285,6 @@ class TestPolicyGroup(NsxPolicyLibTestCase):
tenant=TEST_TENANT) tenant=TEST_TENANT)
expected_dict = {'display_name': name, expected_dict = {'display_name': name,
'description': description} 'description': description}
self.assert_called_with_def(get_call, expected_def)
self.assert_called_with_def_and_dict( self.assert_called_with_def_and_dict(
update_call, expected_def, expected_dict) update_call, expected_def, expected_dict)
@ -292,7 +294,8 @@ class TestPolicyGroup(NsxPolicyLibTestCase):
cond_val = '123' cond_val = '123'
with mock.patch.object(self.policy_api, "get", with mock.patch.object(self.policy_api, "get",
return_value={}) as get_call,\ 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, self.resourceApi.update_condition(domain_id, id,
cond_val=cond_val, cond_val=cond_val,
tenant=TEST_TENANT) tenant=TEST_TENANT)
@ -319,7 +322,8 @@ class TestPolicyGroup(NsxPolicyLibTestCase):
'operator': policy_constants.CONDITION_OP_EQUALS} 'operator': policy_constants.CONDITION_OP_EQUALS}
with mock.patch.object(self.policy_api, "get", with mock.patch.object(self.policy_api, "get",
return_value={'expression': [old_cond]}) as get_call,\ 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, self.resourceApi.update_condition(domain_id, id,
cond_val=None, cond_val=None,
tenant=TEST_TENANT) tenant=TEST_TENANT)
@ -345,8 +349,10 @@ class TestPolicyService(NsxPolicyLibTestCase):
dest_ports = [81, 82] dest_ports = [81, 82]
with mock.patch.object(self.policy_api, with mock.patch.object(self.policy_api,
"create_with_parent") as api_call: "create_with_parent") as api_call:
self.resourceApi.create(name, description=description, self.resourceApi.create_or_overwrite(name,
protocol=protocol, dest_ports=dest_ports, description=description,
protocol=protocol,
dest_ports=dest_ports,
tenant=TEST_TENANT) tenant=TEST_TENANT)
exp_srv_def = policy_defs.ServiceDef(service_id=mock.ANY, exp_srv_def = policy_defs.ServiceDef(service_id=mock.ANY,
name=name, name=name,
@ -400,7 +406,8 @@ class TestPolicyService(NsxPolicyLibTestCase):
description = 'new desc' description = 'new desc'
with mock.patch.object(self.policy_api, "get", with mock.patch.object(self.policy_api, "get",
return_value={}) as get_call,\ 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, self.resourceApi.update(id,
name=name, name=name,
description=description, description=description,
@ -424,7 +431,8 @@ class TestPolicyService(NsxPolicyLibTestCase):
with mock.patch.object( with mock.patch.object(
self.policy_api, "get", self.policy_api, "get",
return_value={'service_entries': [service_entry]}) as get_call,\ 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, self.resourceApi.update(id,
protocol=protocol, protocol=protocol,
dest_ports=dest_ports, dest_ports=dest_ports,
@ -457,7 +465,8 @@ class TestPolicyService(NsxPolicyLibTestCase):
with mock.patch.object( with mock.patch.object(
self.policy_api, "get", self.policy_api, "get",
return_value={'service_entries': [service_entry]}) as get_call,\ 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", mock.patch.object(self.policy_api, "list",
return_value={'results': []}): return_value={'results': []}):
self.resourceApi.update(id, self.resourceApi.update(id,
@ -474,7 +483,7 @@ class TestPolicyService(NsxPolicyLibTestCase):
# update will be called for the service and entry (2 calls) # update will be called for the service and entry (2 calls)
expected_dict = {'display_name': name, expected_dict = {'display_name': name,
'description': description, 'description': description,
'service_entries': [service_entry]} 'service_entries': []}
self.assert_called_with_def_and_dict( self.assert_called_with_def_and_dict(
update_call, expected_def, expected_dict) update_call, expected_def, expected_dict)
@ -505,8 +514,9 @@ class TestPolicyCommunicationProfile(NsxPolicyLibTestCase):
action = 'DENY' action = 'DENY'
with mock.patch.object(self.policy_api, with mock.patch.object(self.policy_api,
"create_with_parent") as api_call: "create_with_parent") as api_call:
self.resourceApi.create(name, description=description, self.resourceApi.create_or_overwrite(name, description=description,
services=[service_id], action=action, services=[service_id],
action=action,
tenant=TEST_TENANT) tenant=TEST_TENANT)
exp_srv_def = policy_defs.CommunicationProfileDef( exp_srv_def = policy_defs.CommunicationProfileDef(
profile_id=mock.ANY, profile_id=mock.ANY,
@ -563,7 +573,8 @@ class TestPolicyCommunicationProfile(NsxPolicyLibTestCase):
description = 'new desc' description = 'new desc'
with mock.patch.object(self.policy_api, "get", with mock.patch.object(self.policy_api, "get",
return_value={}) as get_call,\ 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, self.resourceApi.update(id,
name=name, name=name,
description=description, description=description,
@ -587,7 +598,8 @@ class TestPolicyCommunicationProfile(NsxPolicyLibTestCase):
with mock.patch.object( with mock.patch.object(
self.policy_api, "get", return_value=entries_dict) as get_call,\ 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, self.resourceApi.update(id,
services=[service_id], services=[service_id],
action=action, action=action,
@ -620,7 +632,8 @@ class TestPolicyCommunicationProfile(NsxPolicyLibTestCase):
with mock.patch.object( with mock.patch.object(
self.policy_api, "get", return_value=entries_dict) as get_call,\ 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, self.resourceApi.update(id,
name=name, name=name,
description=description, description=description,
@ -635,7 +648,7 @@ class TestPolicyCommunicationProfile(NsxPolicyLibTestCase):
# update will be called for the service and entry (2 calls) # update will be called for the service and entry (2 calls)
expected_dict = {'display_name': name, expected_dict = {'display_name': name,
'description': description, 'description': description,
'communication_profile_entries': [profile_entry]} 'communication_profile_entries': []}
self.assert_called_with_def_and_dict( self.assert_called_with_def_and_dict(
update_call, expected_def, expected_dict) update_call, expected_def, expected_dict)
@ -668,10 +681,12 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase):
seq_num = 7 seq_num = 7
profile_id = 'c1' profile_id = 'c1'
list_return_value = {'results': [{'sequence_number': 1}]} 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", mock.patch.object(self.policy_api, "list",
return_value=list_return_value): return_value=list_return_value):
self.resourceApi.create(name, domain_id, description=description, self.resourceApi.create_or_overwrite(name, domain_id,
description=description,
sequence_number=seq_num, sequence_number=seq_num,
profile_id=profile_id, profile_id=profile_id,
source_groups=[source_group], source_groups=[source_group],
@ -689,6 +704,36 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase):
tenant=TEST_TENANT) tenant=TEST_TENANT)
self.assert_called_with_def(api_call, expected_def) 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): def test_create_without_seqnum(self):
domain_id = '111' domain_id = '111'
name = 'cm1' name = 'cm1'
@ -698,8 +743,10 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase):
profile_id = 'c1' profile_id = 'c1'
with mock.patch.object(self.policy_api, with mock.patch.object(self.policy_api,
"create_with_parent") as api_call, \ "create_with_parent") as api_call, \
mock.patch.object(self.resourceApi, "list", return_value=[]): mock.patch.object(self.resourceApi, "_get_last_seq_num",
self.resourceApi.create(name, domain_id, description=description, return_value=-1):
self.resourceApi.create_or_overwrite(name, domain_id,
description=description,
profile_id=profile_id, profile_id=profile_id,
source_groups=[source_group], source_groups=[source_group],
dest_groups=[dest_group], dest_groups=[dest_group],
@ -755,7 +802,8 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase):
obj = self.resourceApi.get_by_name(domain_id, name, obj = self.resourceApi.get_by_name(domain_id, name,
tenant=TEST_TENANT) tenant=TEST_TENANT)
self.assertIsNotNone(obj) self.assertIsNotNone(obj)
expected_def = policy_defs.CommunicationMapDef(domain_id, expected_def = policy_defs.CommunicationMapEntryDef(
domain_id,
tenant=TEST_TENANT) tenant=TEST_TENANT)
self.assert_called_with_def(api_call, expected_def) self.assert_called_with_def(api_call, expected_def)
@ -763,7 +811,8 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase):
domain_id = '111' domain_id = '111'
with mock.patch.object(self.policy_api, "list") as api_call: with mock.patch.object(self.policy_api, "list") as api_call:
self.resourceApi.list(domain_id, tenant=TEST_TENANT) self.resourceApi.list(domain_id, tenant=TEST_TENANT)
expected_def = policy_defs.CommunicationMapDef(domain_id=domain_id, expected_def = policy_defs.CommunicationMapEntryDef(
domain_id=domain_id,
tenant=TEST_TENANT) tenant=TEST_TENANT)
self.assert_called_with_def(api_call, expected_def) self.assert_called_with_def(api_call, expected_def)
@ -777,7 +826,8 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase):
profile_id = 'nc1' profile_id = 'nc1'
with mock.patch.object(self.policy_api, "get", with mock.patch.object(self.policy_api, "get",
return_value={}) as get_call,\ 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, self.resourceApi.update(domain_id, id,
name=name, name=name,
description=description, description=description,
@ -817,12 +867,15 @@ class TestPolicyEnforcementPoint(NsxPolicyLibTestCase):
ip_address = '1.1.1.1' ip_address = '1.1.1.1'
username = 'admin' username = 'admin'
password = 'zzz' password = 'zzz'
with mock.patch.object(self.policy_api, "create") as api_call: with mock.patch.object(self.policy_api,
self.resourceApi.create(name, description=description, "create_or_update") as api_call:
self.resourceApi.create_or_overwrite(
name, description=description,
ip_address=ip_address, ip_address=ip_address,
username=username, username=username,
password=password, password=password,
tenant=TEST_TENANT) tenant=TEST_TENANT)
expected_def = policy_defs.EnforcementPointDef( expected_def = policy_defs.EnforcementPointDef(
ep_id=mock.ANY, ep_id=mock.ANY,
name=name, name=name,
@ -870,9 +923,8 @@ class TestPolicyEnforcementPoint(NsxPolicyLibTestCase):
name = 'new name' name = 'new name'
username = 'admin' username = 'admin'
password = 'zzz' password = 'zzz'
with mock.patch.object(self.policy_api, "get", with mock.patch.object(self.policy_api,
return_value={}) as get_call,\ "create_or_update") as update_call:
mock.patch.object(self.policy_api, "update") as update_call:
self.resourceApi.update(id, self.resourceApi.update(id,
name=name, name=name,
username=username, username=username,
@ -883,7 +935,6 @@ class TestPolicyEnforcementPoint(NsxPolicyLibTestCase):
expected_dict = {'display_name': name, expected_dict = {'display_name': name,
'username': username, 'username': username,
'password': password} 'password': password}
self.assert_called_with_def(get_call, expected_def)
self.assert_called_with_def_and_dict( self.assert_called_with_def_and_dict(
update_call, expected_def, expected_dict) update_call, expected_def, expected_dict)
@ -899,8 +950,10 @@ class TestPolicyDeploymentMap(NsxPolicyLibTestCase):
description = 'desc' description = 'desc'
domain_id = 'domain1' domain_id = 'domain1'
ep_id = 'ep1' ep_id = 'ep1'
with mock.patch.object(self.policy_api, "create") as api_call: with mock.patch.object(self.policy_api,
self.resourceApi.create(name, description=description, "create_or_update") as api_call:
self.resourceApi.create_or_overwrite(name,
description=description,
ep_id=ep_id, ep_id=ep_id,
domain_id=domain_id, domain_id=domain_id,
tenant=TEST_TENANT) tenant=TEST_TENANT)
@ -950,9 +1003,8 @@ class TestPolicyDeploymentMap(NsxPolicyLibTestCase):
name = 'new name' name = 'new name'
domain_id = 'domain2' domain_id = 'domain2'
ep_id = 'ep2' ep_id = 'ep2'
with mock.patch.object(self.policy_api, "get", with mock.patch.object(self.policy_api,
return_value={}) as get_call,\ "create_or_update") as update_call:
mock.patch.object(self.policy_api, "update") as update_call:
self.resourceApi.update(id, self.resourceApi.update(id,
name=name, name=name,
ep_id=ep_id, ep_id=ep_id,
@ -966,6 +1018,5 @@ class TestPolicyDeploymentMap(NsxPolicyLibTestCase):
expected_dict = {'display_name': name, expected_dict = {'display_name': name,
'enforcement_point_paths': [ep_path], 'enforcement_point_paths': [ep_path],
'domain_path': domain_path} 'domain_path': domain_path}
self.assert_called_with_def(get_call, expected_def)
self.assert_called_with_def_and_dict( self.assert_called_with_def_and_dict(
update_call, expected_def, expected_dict) update_call, expected_def, expected_dict)

View File

@ -49,7 +49,8 @@ class NsxLibBase(object):
self.client = client.NSX3Client( self.client = client.NSX3Client(
self.cluster, self.cluster,
nsx_api_managers=self.nsxlib_config.nsx_api_managers, 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.general_apis = utils.NsxLibApiBase(
self.client, self.nsxlib_config) self.client, self.nsxlib_config)
@ -63,7 +64,12 @@ class NsxLibBase(object):
def set_config(self, nsxlib_config): def set_config(self, nsxlib_config):
"""Set config user provided and extend it according to application""" """Set config user provided and extend it according to application"""
self.nsxlib_config = nsxlib_config 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 @abc.abstractproperty
def keepalive_section(self): def keepalive_section(self):
@ -202,6 +208,10 @@ class NsxLib(NsxLibBase):
return False return False
@property
def client_url_prefix(self):
return client.NSX3Client.NSX_V1_API_PREFIX
class NsxPolicyLib(NsxLibBase): class NsxPolicyLib(NsxLibBase):
@ -231,3 +241,7 @@ class NsxPolicyLib(NsxLibBase):
return True return True
return False return False
@property
def client_url_prefix(self):
return client.NSX3Client.NSX_POLICY_V1_API_PREFIX

View File

@ -226,13 +226,15 @@ class JSONRESTClient(RESTClient):
class NSX3Client(JSONRESTClient): 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, def __init__(self, connection, url_prefix=None,
default_headers=None, default_headers=None,
nsx_api_managers=None, nsx_api_managers=None,
max_attempts=utils.DEFAULT_MAX_ATTEMPTS, 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 the client obj is defined - copy configuration from it
if client_obj: if client_obj:
@ -242,12 +244,12 @@ class NSX3Client(JSONRESTClient):
self.nsx_api_managers = nsx_api_managers or [] self.nsx_api_managers = nsx_api_managers or []
self.max_attempts = max_attempts self.max_attempts = max_attempts
url_prefix = url_prefix or NSX3Client._NSX_V1_API_PREFIX url_prefix = url_prefix or url_path_base
if url_prefix and NSX3Client._NSX_V1_API_PREFIX not in url_prefix: if url_prefix and url_path_base not in url_prefix:
if url_prefix.startswith('http'): if url_prefix.startswith('http'):
url_prefix += '/' + NSX3Client._NSX_V1_API_PREFIX url_prefix += '/' + url_path_base
else: else:
url_prefix = "%s/%s" % (NSX3Client._NSX_V1_API_PREFIX, url_prefix = "%s/%s" % (url_path_base,
url_prefix or '') url_prefix or '')
self.max_attempts = max_attempts self.max_attempts = max_attempts

View File

@ -150,7 +150,9 @@ class NSXRequestsHTTPProvider(AbstractHTTPProvider):
return "%s-%s" % (requests.__title__, requests.__version__) return "%s-%s" % (requests.__title__, requests.__version__)
def validate_connection(self, cluster_api, endpoint, conn): 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 keepalive_section = cluster_api.nsxlib_config.keepalive_section
result = client.get(keepalive_section, silent=True) result = client.get(keepalive_section, silent=True)
# If keeplive section returns a list, it is assumed to be non-empty # If keeplive section returns a list, it is assumed to be non-empty

View File

@ -118,9 +118,10 @@ class NsxLibConfig(object):
'dhcp_profile_uuid is not used by the nsxlib, and will ' 'dhcp_profile_uuid is not used by the nsxlib, and will '
'be removed from its configuration in the future.') '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""" """Called by library code to initialize application-specific data"""
self.keepalive_section = keepalive_section self.keepalive_section = keepalive_section
self.url_base = url_base
def _attribute_by_index(self, scalar_or_list, index): def _attribute_by_index(self, scalar_or_list, index):
if isinstance(scalar_or_list, list): if isinstance(scalar_or_list, list):

View File

@ -36,8 +36,7 @@ class ResourceDef(object):
self.body = {} self.body = {}
def get_obj_dict(self): def get_obj_dict(self):
body = {'_revision': 0, body = {'display_name': self.name,
'display_name': self.name,
'description': self.description} 'description': self.description}
if self.id: if self.id:
body['id'] = self.id body['id'] = self.id
@ -67,9 +66,20 @@ class ResourceDef(object):
def sub_entries_path(): def sub_entries_path():
pass pass
def update_attributes_in_body(self, body, **kwargs): def _get_body_from_kwargs(self, **kwargs):
self.body = body 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): for key, value in six.iteritems(kwargs):
if key == 'body':
continue
if value is not None: if value is not None:
if key == 'name': if key == 'name':
self.body['display_name'] = value self.body['display_name'] = value
@ -164,13 +174,16 @@ class GroupDef(ResourceDef):
for condition in self.conditions] for condition in self.conditions]
return body 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 # Fix params that need special conversions
if kwargs.get('conditions') is not None: if kwargs.get('conditions') is not None:
body['expression'] = [cond.get_obj_dict() body['expression'] = [cond.get_obj_dict()
for cond in kwargs['conditions']] for cond in kwargs['conditions']]
del 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): class ServiceDef(ResourceDef):
@ -231,8 +244,12 @@ class L4ServiceEntryDef(ResourceDef):
body['destination_ports'] = self.dest_ports body['destination_ports'] = self.dest_ports
return body return body
def update_attributes_in_body(self, body, **kwargs): def update_attributes_in_body(self, **kwargs):
# Fix params that need special conversions # 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: if kwargs.get('protocol') is not None:
body['l4_protocol'] = kwargs['protocol'].upper() body['l4_protocol'] = kwargs['protocol'].upper()
del kwargs['protocol'] del kwargs['protocol']
@ -240,7 +257,7 @@ class L4ServiceEntryDef(ResourceDef):
body['destination_ports'] = kwargs['dest_ports'] body['destination_ports'] = kwargs['dest_ports']
del kwargs['dest_ports'] del kwargs['dest_ports']
super(L4ServiceEntryDef, self).update_attributes_in_body( super(L4ServiceEntryDef, self).update_attributes_in_body(
body, **kwargs) body=body, **kwargs)
class CommunicationProfileDef(ResourceDef): class CommunicationProfileDef(ResourceDef):
@ -270,9 +287,9 @@ class CommunicationProfileDef(ResourceDef):
entryDef = CommunicationProfileEntryDef() entryDef = CommunicationProfileEntryDef()
return entryDef.get_last_section_dict_key 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( super(CommunicationProfileDef, self).update_attributes_in_body(
body, **kwargs) **kwargs)
# make sure entries are there # make sure entries are there
entries_path = self.sub_entries_path() entries_path = self.sub_entries_path()
if entries_path not in self.body: if entries_path not in self.body:
@ -307,12 +324,15 @@ class CommunicationProfileEntryDef(ResourceDef):
body['action'] = self.action body['action'] = self.action
return body 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: if kwargs.get('action') is not None:
body['action'] = kwargs['action'].upper() body['action'] = kwargs['action'].upper()
del kwargs['action'] del kwargs['action']
super(CommunicationProfileEntryDef, self).update_attributes_in_body( super(CommunicationProfileEntryDef, self).update_attributes_in_body(
body, **kwargs) body=body, **kwargs)
class CommunicationMapDef(ResourceDef): class CommunicationMapDef(ResourceDef):
@ -380,7 +400,10 @@ class CommunicationMapEntryDef(ResourceDef):
body['communication_profile_path'] = self.profile_path body['communication_profile_path'] = self.profile_path
return body 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 # Fix params that need special conversions
if kwargs.get('profile_id') is not None: if kwargs.get('profile_id') is not None:
profile_path = self.get_profile_path(kwargs['profile_id']) profile_path = self.get_profile_path(kwargs['profile_id'])
@ -400,7 +423,7 @@ class CommunicationMapEntryDef(ResourceDef):
del kwargs['source_groups'] del kwargs['source_groups']
super(CommunicationMapEntryDef, self).update_attributes_in_body( super(CommunicationMapEntryDef, self).update_attributes_in_body(
body, **kwargs) body=body, **kwargs)
class EnforcementPointDef(ResourceDef): class EnforcementPointDef(ResourceDef):
@ -411,6 +434,7 @@ class EnforcementPointDef(ResourceDef):
ip_address=None, ip_address=None,
username=None, username=None,
password=None, password=None,
thumbprint=None,
ep_type='NSXT', ep_type='NSXT',
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
super(EnforcementPointDef, self).__init__() super(EnforcementPointDef, self).__init__()
@ -422,6 +446,7 @@ class EnforcementPointDef(ResourceDef):
self.username = username self.username = username
self.password = password self.password = password
self.ip_address = ip_address self.ip_address = ip_address
self.thumbprint = thumbprint
self.parent_ids = (tenant) self.parent_ids = (tenant)
@property @property
@ -433,8 +458,7 @@ class EnforcementPointDef(ResourceDef):
body = super(EnforcementPointDef, self).get_obj_dict() body = super(EnforcementPointDef, self).get_obj_dict()
body['id'] = self.id body['id'] = self.id
body['connection_info'] = [{'fqdn': 'abc', body['connection_info'] = [{'fqdn': 'abc',
'thumbprint': 'thumbprint': self.thumbprint,
policy_constants.DEFAULT_THUMBPRINT,
'username': self.username, 'username': self.username,
'password': self.password, 'password': self.password,
'ip_address': self.ip_address, 'ip_address': self.ip_address,
@ -443,24 +467,21 @@ class EnforcementPointDef(ResourceDef):
body['resource_type'] = 'EnforcementPoint' body['resource_type'] = 'EnforcementPoint'
return body 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 # Fix params that need special conversions
if body.get('connection_info'): if body.get('connection_info'):
body['connection_info'][0]['resource_type'] = 'NSXTConnectionInfo' 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: for attr in ('username', 'password', 'ip_address', 'thumbprint'):
body['connection_info'][0]['password'] = kwargs['password'] if kwargs.get(attr) is not None:
del kwargs['password'] body['connection_info'][0][attr] = kwargs[attr]
del kwargs[attr]
if kwargs.get('ip_address') is not None:
body['connection_info'][0]['ip_address'] = kwargs['ip_address']
del kwargs['ip_address']
super(EnforcementPointDef, self).update_attributes_in_body( super(EnforcementPointDef, self).update_attributes_in_body(
body, **kwargs) body=body, **kwargs)
# Currently assumes one deployment point per id # Currently assumes one deployment point per id
@ -497,7 +518,10 @@ class DeploymentMapDef(ResourceDef):
body['enforcement_point_paths'] = [self.ep_path] body['enforcement_point_paths'] = [self.ep_path]
return body 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 # Fix params that need special conversions
if kwargs.get('domain_id') is not None: if kwargs.get('domain_id') is not None:
domain_id = kwargs.get('domain_id') domain_id = kwargs.get('domain_id')
@ -514,7 +538,7 @@ class DeploymentMapDef(ResourceDef):
del kwargs['ep_id'] del kwargs['ep_id']
super(DeploymentMapDef, self).update_attributes_in_body( super(DeploymentMapDef, self).update_attributes_in_body(
body, **kwargs) body=body, **kwargs)
class NsxPolicyApi(object): class NsxPolicyApi(object):
@ -522,9 +546,18 @@ class NsxPolicyApi(object):
def __init__(self, client): def __init__(self, client):
self.client = 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() 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): def create_with_parent(self, parent_def, resource_def):
path = parent_def.get_resource_path() path = parent_def.get_resource_path()
@ -535,7 +568,7 @@ class NsxPolicyApi(object):
else: else:
child_dict_key = resource_def.get_last_section_dict_key child_dict_key = resource_def.get_last_section_dict_key
body[child_dict_key] = [resource_def.get_obj_dict()] 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): def delete(self, resource_def):
path = resource_def.get_resource_path() path = resource_def.get_resource_path()
@ -548,8 +581,3 @@ class NsxPolicyApi(object):
def list(self, resource_def): def list(self, resource_def):
path = resource_def.get_section_path() path = resource_def.get_section_path()
return self.client.list(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)

View File

@ -54,7 +54,7 @@ class NsxPolicyResourceBase(object):
pass pass
@abc.abstractmethod @abc.abstractmethod
def create(self, *args, **kwargs): def create_or_overwrite(self, *args, **kwargs):
pass pass
@abc.abstractmethod @abc.abstractmethod
@ -78,14 +78,14 @@ class NsxPolicyResourceBase(object):
class NsxPolicyDomainApi(NsxPolicyResourceBase): class NsxPolicyDomainApi(NsxPolicyResourceBase):
"""NSX Policy Domain.""" """NSX Policy Domain."""
def create(self, name, domain_id=None, description=None, def create_or_overwrite(self, name, domain_id=None, description=None,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
domain_id = self._init_obj_uuid(domain_id) domain_id = self._init_obj_uuid(domain_id)
domain_def = policy_defs.DomainDef(domain_id=domain_id, domain_def = policy_defs.DomainDef(domain_id=domain_id,
name=name, name=name,
description=description, description=description,
tenant=tenant) 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): def delete(self, domain_id, tenant=policy_constants.POLICY_INFRA_TENANT):
domain_def = policy_defs.DomainDef(domain_id, tenant=tenant) domain_def = policy_defs.DomainDef(domain_id, tenant=tenant)
@ -103,18 +103,16 @@ class NsxPolicyDomainApi(NsxPolicyResourceBase):
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
domain_def = policy_defs.DomainDef(domain_id=domain_id, domain_def = policy_defs.DomainDef(domain_id=domain_id,
tenant=tenant) tenant=tenant)
# Get the current data, and update it with the new values domain_def.update_attributes_in_body(name=name,
domain = self.get(domain_id, tenant=tenant)
domain_def.update_attributes_in_body(domain,
name=name,
description=description) description=description)
# update the backend # update the backend
return self.policy_api.update(domain_def) return self.policy_api.create_or_update(domain_def)
class NsxPolicyGroupApi(NsxPolicyResourceBase): class NsxPolicyGroupApi(NsxPolicyResourceBase):
"""NSX Policy Group (under a Domain) with a single condition.""" """NSX Policy Group (under a Domain) with a single condition."""
def create(self, name, domain_id, group_id=None, def create_or_overwrite(
self, name, domain_id, group_id=None,
description=None, description=None,
cond_val=None, cond_val=None,
cond_key=policy_constants.CONDITION_KEY_TAG, cond_key=policy_constants.CONDITION_KEY_TAG,
@ -142,7 +140,7 @@ class NsxPolicyGroupApi(NsxPolicyResourceBase):
description=description, description=description,
conditions=conditions, conditions=conditions,
tenant=tenant) 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, def delete(self, domain_id, group_id,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
@ -180,12 +178,10 @@ class NsxPolicyGroupApi(NsxPolicyResourceBase):
group_def = policy_defs.GroupDef(domain_id=domain_id, group_def = policy_defs.GroupDef(domain_id=domain_id,
group_id=group_id, group_id=group_id,
tenant=tenant) tenant=tenant)
# Get the current data, and update it with the new values group_def.update_attributes_in_body(name=name,
group = self.get(domain_id, group_id, tenant=tenant)
group_def.update_attributes_in_body(group, name=name,
description=description) description=description)
# update the backend # update the backend
return self.policy_api.update(group_def) return self.policy_api.create_or_update(group_def)
def update_condition( def update_condition(
self, domain_id, group_id, self, domain_id, group_id,
@ -212,10 +208,11 @@ class NsxPolicyGroupApi(NsxPolicyResourceBase):
else: else:
conditions = [] conditions = []
# Get the current data, and update it with the new values # 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 = 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 # update the backend
return self.policy_api.update(group_def) return self.policy_api.create_or_update(group_def)
class NsxPolicyL4ServiceApi(NsxPolicyResourceBase): class NsxPolicyL4ServiceApi(NsxPolicyResourceBase):
@ -225,7 +222,7 @@ class NsxPolicyL4ServiceApi(NsxPolicyResourceBase):
and multiple service entries per service. and multiple service entries per service.
At this point this is not supported here. At this point this is not supported here.
""" """
def create(self, name, service_id=None, description=None, def create_or_overwrite(self, name, service_id=None, description=None,
protocol=policy_constants.TCP, dest_ports=None, protocol=policy_constants.TCP, dest_ports=None,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
service_id = self._init_obj_uuid(service_id) service_id = self._init_obj_uuid(service_id)
@ -266,16 +263,17 @@ class NsxPolicyL4ServiceApi(NsxPolicyResourceBase):
name=None, description=None, name=None, description=None,
protocol=None, dest_ports=None, protocol=None, dest_ports=None,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
# TODO(asarfaty): This action fails on backend
entry_id = srv_entry['id'] entry_id = srv_entry['id']
entry_def = policy_defs.L4ServiceEntryDef(service_id=service_id, entry_def = policy_defs.L4ServiceEntryDef(service_id=service_id,
service_entry_id=entry_id, service_entry_id=entry_id,
tenant=tenant) 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, description=description,
protocol=protocol, protocol=protocol,
dest_ports=dest_ports) 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, def update(self, service_id, name=None, description=None,
protocol=None, dest_ports=None, protocol=None, dest_ports=None,
@ -288,11 +286,11 @@ class NsxPolicyL4ServiceApi(NsxPolicyResourceBase):
# update the service itself # update the service itself
service_def = policy_defs.ServiceDef(service_id=service_id, service_def = policy_defs.ServiceDef(service_id=service_id,
tenant=tenant) tenant=tenant)
service_def.update_attributes_in_body(service, name=name, service_def.update_attributes_in_body(name=name,
description=description) description=description)
# update the backend # update the backend
updated_service = self.policy_api.update(service_def) updated_service = self.policy_api.create_or_update(service_def)
else: else:
updated_service = service updated_service = service
@ -320,8 +318,9 @@ class NsxPolicyCommunicationProfileApi(NsxPolicyResourceBase):
profile. profile.
At this point this is not supported here. At this point this is not supported here.
""" """
def create(self, name, profile_id=None, description=None, def create_or_overwrite(self, name, profile_id=None, description=None,
services=None, action=policy_constants.ACTION_ALLOW, services=None,
action=policy_constants.ACTION_ALLOW,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
"""Create a Communication profile with a single entry. """Create a Communication profile with a single entry.
@ -371,12 +370,13 @@ class NsxPolicyCommunicationProfileApi(NsxPolicyResourceBase):
profile_id=profile_id, profile_id=profile_id,
profile_entry_id=entry_id, profile_entry_id=entry_id,
tenant=tenant) 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, description=description,
services=services, services=services,
action=action) 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, def update(self, profile_id, name=None, description=None,
services=None, action=None, services=None, action=None,
@ -388,11 +388,11 @@ class NsxPolicyCommunicationProfileApi(NsxPolicyResourceBase):
# update the profile itself # update the profile itself
profile_def = policy_defs.CommunicationProfileDef( profile_def = policy_defs.CommunicationProfileDef(
profile_id=profile_id, tenant=tenant) 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) description=description)
# update the backend # update the backend
updated_profile = self.policy_api.update(profile_def) updated_profile = self.policy_api.create_or_update(profile_def)
else: else:
updated_profile = profile updated_profile = profile
@ -419,22 +419,28 @@ class NsxPolicyCommunicationMapApi(NsxPolicyResourceBase):
def _get_last_seq_num(self, domain_id, def _get_last_seq_num(self, domain_id,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
# get the current entries, and choose the next unused sequence number # get the current entries, and choose the next unused sequence number
communication_maps = self.list(domain_id, tenant=tenant) try:
if not len(communication_maps): com_entries = self.list(domain_id, tenant=tenant)
except exceptions.ResourceNotFound:
return -1
if not com_entries:
return 0 return 0
seq_nums = [int(cm['sequence_number']) for cm in com_entries]
seq_nums = [int(cm['sequence_number']) for cm in communication_maps]
seq_nums.sort() seq_nums.sort()
return seq_nums[-1] return seq_nums[-1]
def create(self, name, domain_id, map_id=None, def create_or_overwrite(self, name, domain_id, map_id=None,
description=None, sequence_number=None, profile_id=None, description=None, sequence_number=None,
profile_id=None,
source_groups=None, dest_groups=None, source_groups=None, dest_groups=None,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
"""Create a CommunicationtMap. """Create CommunicationMapEntry.
source_groups/dest_groups should be a list of group ids belonging source_groups/dest_groups should be a list of group ids belonging
to the domain. 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 # Validate and convert inputs
map_id = self._init_obj_uuid(map_id) map_id = self._init_obj_uuid(map_id)
@ -447,6 +453,9 @@ class NsxPolicyCommunicationMapApi(NsxPolicyResourceBase):
# get the next available sequence number # get the next available sequence number
last_sequence = self._get_last_seq_num(domain_id, tenant=tenant) last_sequence = self._get_last_seq_num(domain_id, tenant=tenant)
if not sequence_number: if not sequence_number:
if last_sequence < 0:
sequence_number = 1
else:
sequence_number = last_sequence + 1 sequence_number = last_sequence + 1
entry_def = policy_defs.CommunicationMapEntryDef( entry_def = policy_defs.CommunicationMapEntryDef(
@ -460,12 +469,14 @@ class NsxPolicyCommunicationMapApi(NsxPolicyResourceBase):
profile_id=profile_id, profile_id=profile_id,
tenant=tenant) tenant=tenant)
if last_sequence == 0: if last_sequence < 0:
# if communication map is absent, we need to create it # if communication map is absent, we need to create it
map_def = policy_defs.CommunicationMapDef(domain_id, tenant) 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, def delete(self, domain_id, map_id,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
@ -485,14 +496,14 @@ class NsxPolicyCommunicationMapApi(NsxPolicyResourceBase):
def get_by_name(self, domain_id, name, def get_by_name(self, domain_id, name,
tenant=policy_constants.POLICY_INFRA_TENANT): 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( return super(NsxPolicyCommunicationMapApi, self).get_by_name(
name, domain_id, tenant=tenant) name, domain_id, tenant=tenant)
def list(self, domain_id, def list(self, domain_id,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
"""List all the map entries of a specific domain.""" """List all the map entries of a specific domain."""
map_def = policy_defs.CommunicationMapDef( map_def = policy_defs.CommunicationMapEntryDef(
domain_id=domain_id, domain_id=domain_id,
tenant=tenant) tenant=tenant)
return self.policy_api.list(map_def)['results'] return self.policy_api.list(map_def)['results']
@ -505,10 +516,18 @@ class NsxPolicyCommunicationMapApi(NsxPolicyResourceBase):
domain_id=domain_id, domain_id=domain_id,
map_id=map_id, map_id=map_id,
tenant=tenant) tenant=tenant)
# Get the current data, and update it with the new values # Get the current data, and update it with the new values
try:
comm_map = self.get(domain_id, map_id, tenant=tenant) 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( map_def.update_attributes_in_body(
comm_map, body=comm_map,
name=name, name=name,
description=description, description=description,
sequence_number=sequence_number, sequence_number=sequence_number,
@ -517,15 +536,15 @@ class NsxPolicyCommunicationMapApi(NsxPolicyResourceBase):
dest_groups=dest_groups) dest_groups=dest_groups)
# update the backend # update the backend
return self.policy_api.update(map_def) return self.policy_api.create_or_update(map_def)
class NsxPolicyEnforcementPointApi(NsxPolicyResourceBase): class NsxPolicyEnforcementPointApi(NsxPolicyResourceBase):
"""NSX Policy Enforcement Point.""" """NSX Policy Enforcement Point."""
def create(self, name, ep_id=None, description=None, def create_or_overwrite(self, name, ep_id=None, description=None,
ip_address=None, username=None, ip_address=None, username=None,
password=None, password=None, thumbprint=None,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
if not ip_address or not username or password is None: if not ip_address or not username or password is None:
err_msg = (_("Cannot create an enforcement point without " err_msg = (_("Cannot create an enforcement point without "
@ -539,8 +558,9 @@ class NsxPolicyEnforcementPointApi(NsxPolicyResourceBase):
ip_address=ip_address, ip_address=ip_address,
username=username, username=username,
password=password, password=password,
thumbprint=thumbprint,
tenant=tenant) tenant=tenant)
return self.policy_api.create(ep_def) return self.policy_api.create_or_update(ep_def)
def delete(self, ep_id, def delete(self, ep_id,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
@ -559,7 +579,8 @@ class NsxPolicyEnforcementPointApi(NsxPolicyResourceBase):
return self.policy_api.list(ep_def)['results'] return self.policy_api.list(ep_def)['results']
def update(self, ep_id, name=None, description=None, 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): tenant=policy_constants.POLICY_INFRA_TENANT):
"""Update the enforcement point. """Update the enforcement point.
@ -572,22 +593,20 @@ class NsxPolicyEnforcementPointApi(NsxPolicyResourceBase):
raise exceptions.ManagerError(details=err_msg) raise exceptions.ManagerError(details=err_msg)
ep_def = policy_defs.EnforcementPointDef(ep_id=ep_id, tenant=tenant) ep_def = policy_defs.EnforcementPointDef(ep_id=ep_id, tenant=tenant)
# Get the current data, and update it with the new values ep_def.update_attributes_in_body(name=name,
ep = self.get(ep_id, tenant=tenant)
ep_def.update_attributes_in_body(ep,
name=name,
description=description, description=description,
ip_address=ip_address, ip_address=ip_address,
username=username, username=username,
password=password) password=password,
thumbprint=thumbprint)
# update the backend # update the backend
return self.policy_api.update(ep_def) return self.policy_api.create_or_update(ep_def)
class NsxPolicyDeploymentMapApi(NsxPolicyResourceBase): class NsxPolicyDeploymentMapApi(NsxPolicyResourceBase):
"""NSX Policy Deployment Map.""" """NSX Policy Deployment Map."""
def create(self, name, map_id=None, description=None, def create_or_overwrite(self, name, map_id=None, description=None,
ep_id=None, domain_id=None, ep_id=None, domain_id=None,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
map_id = self._init_obj_uuid(map_id) map_id = self._init_obj_uuid(map_id)
@ -598,7 +617,7 @@ class NsxPolicyDeploymentMapApi(NsxPolicyResourceBase):
ep_id=ep_id, ep_id=ep_id,
domain_id=domain_id, domain_id=domain_id,
tenant=tenant) tenant=tenant)
return self.policy_api.create(map_def) return self.policy_api.create_or_update(map_def)
def delete(self, map_id, def delete(self, map_id,
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
@ -621,12 +640,9 @@ class NsxPolicyDeploymentMapApi(NsxPolicyResourceBase):
tenant=policy_constants.POLICY_INFRA_TENANT): tenant=policy_constants.POLICY_INFRA_TENANT):
map_def = policy_defs.DeploymentMapDef( map_def = policy_defs.DeploymentMapDef(
map_id=map_id, tenant=tenant) map_id=map_id, tenant=tenant)
# Get the current data, and update it with the new values map_def.update_attributes_in_body(name=name,
map_obj = self.get(map_id, tenant=tenant)
map_def.update_attributes_in_body(map_obj,
name=name,
description=description, description=description,
ep_id=ep_id, ep_id=ep_id,
domain_id=domain_id) domain_id=domain_id)
# update the backend # update the backend
return self.policy_api.update(map_def) return self.policy_api.create_or_update(map_def)