Browse Source

Cherry pick NSX 3 related patches to Stein

commit: I4f9c3bfd7995bca85a51a6971fc9d784e574301a
commit: Ib8d8a15f23146e09fd6ed839c626724aa4e36952
commit: If1e86a3f0a0049b3022b9645f738440200e337d9
commit: I45b7c50a50b1a69e37b79f684b324bb17496a9df
commit: I59ef2e27f6a2f23a44edcd37da88bdc70fda944d
commit: 27d4662f90
commit: I26ce4b1af9e9b6c19a3245675a3db2c027361acb
commit: I8f8f928d6a7d40a909772b949ea7b49315f69ee3
commit: If51c51fb503dcb5465ad2eda5a3fb0cd24b463b8
commit: Ia34e42a94c10bd3f12ebc658939ed826af53658c
commit: Ibf1c1fc918998f6002e445ad53e32b2c5c54ac1c

Change-Id: Ibf1c1fc918998f6002e445ad53e32b2c5c54ac1c
changes/35/704735/2
Shawn Wang 6 months ago
parent
commit
8985a92978
16 changed files with 990 additions and 76 deletions
  1. +116
    -7
      vmware_nsxlib/tests/unit/v3/policy/test_lb_resources.py
  2. +272
    -2
      vmware_nsxlib/tests/unit/v3/policy/test_resources.py
  3. +188
    -0
      vmware_nsxlib/tests/unit/v3/policy/test_transaction.py
  4. +2
    -1
      vmware_nsxlib/tests/unit/v3/test_constants.py
  5. +11
    -7
      vmware_nsxlib/tests/unit/v3/test_load_balancer.py
  6. +40
    -12
      vmware_nsxlib/tests/unit/v3/test_resources.py
  7. +10
    -4
      vmware_nsxlib/v3/__init__.py
  8. +14
    -2
      vmware_nsxlib/v3/lib.py
  9. +24
    -2
      vmware_nsxlib/v3/load_balancer.py
  10. +2
    -0
      vmware_nsxlib/v3/nsx_constants.py
  11. +33
    -0
      vmware_nsxlib/v3/policy/__init__.py
  12. +89
    -3
      vmware_nsxlib/v3/policy/core_defs.py
  13. +122
    -23
      vmware_nsxlib/v3/policy/core_resources.py
  14. +22
    -0
      vmware_nsxlib/v3/policy/lb_defs.py
  15. +32
    -7
      vmware_nsxlib/v3/policy/lb_resources.py
  16. +13
    -6
      vmware_nsxlib/v3/policy/transaction.py

+ 116
- 7
vmware_nsxlib/tests/unit/v3/policy/test_lb_resources.py View File

@@ -17,6 +17,8 @@
import mock

from vmware_nsxlib.tests.unit.v3.policy import test_resources
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
from vmware_nsxlib.v3.policy import constants
from vmware_nsxlib.v3.policy import lb_defs

TEST_TENANT = 'test'
@@ -603,6 +605,7 @@ class TestPolicyLBService(test_resources.NsxPolicyLibTestCase):
obj_id = '111'
size = 'SMALL'
connectivity_path = 'path'
relax_scale_validation = True
with self.mock_create_update() as api_call:
result = self.resourceApi.create_or_overwrite(
name,
@@ -610,14 +613,17 @@ class TestPolicyLBService(test_resources.NsxPolicyLibTestCase):
description=description,
size=size,
connectivity_path=connectivity_path,
relax_scale_validation=relax_scale_validation,
tenant=TEST_TENANT)
expected_def = (
lb_defs.LBServiceDef(
nsx_version=self.policy_lib.get_version(),
lb_service_id=obj_id,
name=name,
description=description,
size=size,
connectivity_path=connectivity_path,
relax_scale_validation=relax_scale_validation,
tenant=TEST_TENANT))
self.assert_called_with_def(api_call, expected_def)
self.assertEqual(obj_id, result)
@@ -637,6 +643,25 @@ class TestPolicyLBService(test_resources.NsxPolicyLibTestCase):
self.assert_called_with_def(api_call, expected_def)
self.assertIsNotNone(result)

def test_create_with_unsupported_attribute(self):
name = 'd1'
description = 'desc'
relax_scale_validation = True

with self.mock_create_update() as api_call, \
mock.patch.object(self.resourceApi, 'version', '0.0.0'):
result = self.resourceApi.create_or_overwrite(
name, description=description,
relax_scale_validation=relax_scale_validation,
tenant=TEST_TENANT)
expected_def = (
lb_defs.LBServiceDef(lb_service_id=mock.ANY,
name=name,
description=description,
tenant=TEST_TENANT))
self.assert_called_with_def(api_call, expected_def)
self.assertIsNotNone(result)

def test_delete(self):
obj_id = '111'
with mock.patch.object(self.policy_api, "delete") as api_call:
@@ -683,21 +708,26 @@ class TestPolicyLBService(test_resources.NsxPolicyLibTestCase):
description = 'new desc'
size = 'SMALL'
connectivity_path = 'path'
relax_scale_validation = True
with self.mock_get(obj_id, name), \
self.mock_create_update() as update_call:
self.resourceApi.update(obj_id,
name=name,
description=description,
tenant=TEST_TENANT,
size=size,
connectivity_path=connectivity_path)
self.resourceApi.update(
obj_id,
name=name,
description=description,
tenant=TEST_TENANT,
size=size,
connectivity_path=connectivity_path,
relax_scale_validation=relax_scale_validation)
expected_def = lb_defs.LBServiceDef(
nsx_version=self.policy_lib.get_version(),
lb_service_id=obj_id,
name=name,
description=description,
tenant=TEST_TENANT,
size=size,
connectivity_path=connectivity_path)
connectivity_path=connectivity_path,
relax_scale_validation=relax_scale_validation)
self.assert_called_with_def(update_call, expected_def)

def test_get_status(self):
@@ -729,6 +759,62 @@ class TestPolicyLBService(test_resources.NsxPolicyLibTestCase):
tenant=TEST_TENANT)
self.assert_called_with_def(api_call, expected_def)

def test_wait_until_realized_fail(self):
lbs_id = 'test_lbs'
info = {'state': constants.STATE_UNREALIZED,
'realization_specific_identifier': lbs_id,
'entity_type': 'LbServiceDto'}
with mock.patch.object(self.resourceApi, "_get_realization_info",
return_value=info):
self.assertRaises(nsxlib_exc.RealizationTimeoutError,
self.resourceApi.wait_until_realized,
lbs_id, max_attempts=5, sleep=0.1,
tenant=TEST_TENANT)

def test_wait_until_realized_error(self):
lbs_id = 'test_lbs'
error_code = 23500
related_error_code = 23707
error_msg = 'Found errors in the request.'
related_error_msg = 'Exceed maximum number of load balancer.'
info = {'state': constants.STATE_ERROR,
'realization_specific_identifier': lbs_id,
'entity_type': 'LbServiceDto',
'alarms': [{
'message': error_msg,
'error_details': {
'related_errors': [{
'error_code': related_error_code,
'module_name': 'LOAD-BALANCER',
'error_message': related_error_msg
}],
'error_code': error_code,
'module_name': 'LOAD-BALANCER',
'error_message': error_msg
}
}]}
with mock.patch.object(self.resourceApi, "_get_realization_info",
return_value=info):
with self.assertRaises(nsxlib_exc.RealizationErrorStateError) as e:
self.resourceApi.wait_until_realized(
lbs_id, tenant=TEST_TENANT)
error_msg_tail = "%s: %s" % (error_msg, related_error_msg)
self.assertTrue(e.exception.msg.endswith(error_msg_tail))
self.assertEqual(e.exception.error_code, error_code)
self.assertEqual(e.exception.related_error_codes,
[related_error_code])

def test_wait_until_realized_succeed(self):
lbs_id = 'test_lbs'
info = {'state': constants.STATE_REALIZED,
'realization_specific_identifier': lbs_id,
'entity_type': 'LbServiceDto'}
with mock.patch.object(self.resourceApi, "_get_realization_info",
return_value=info):
actual_info = self.resourceApi.wait_until_realized(
lbs_id, max_attempts=5, sleep=0.1, tenant=TEST_TENANT)
self.assertEqual(info, actual_info)


class TestPolicyLBVirtualServer(test_resources.NsxPolicyLibTestCase):

@@ -1015,6 +1101,29 @@ class TestPolicyLBVirtualServer(test_resources.NsxPolicyLibTestCase):
rules=[{'display_name': 'yy'}])
self.assert_called_with_def(update_call, expected_def)

def test_wait_until_realized_fail(self):
vs_id = 'test_vs'
info = {'state': constants.STATE_UNREALIZED,
'realization_specific_identifier': vs_id}
with mock.patch.object(self.resourceApi, "_get_realization_info",
return_value=info):
self.assertRaises(nsxlib_exc.RealizationTimeoutError,
self.resourceApi.wait_until_realized,
vs_id, max_attempts=5, sleep=0.1,
tenant=TEST_TENANT)

def test_wait_until_realized_succeed(self):
vs_id = 'test_vs'
info = {'state': constants.STATE_REALIZED,
'realization_specific_identifier': vs_id,
'entity_type': 'LbVirtualServerDto'}
with mock.patch.object(self.resourceApi, "_get_realization_info",
return_value=info):
actual_info = self.resourceApi.wait_until_realized(
vs_id, entity_type='LbVirtualServerDto', max_attempts=5,
sleep=0.1, tenant=TEST_TENANT)
self.assertEqual(info, actual_info)


class TestPolicyLBPoolApi(test_resources.NsxPolicyLibTestCase):


+ 272
- 2
vmware_nsxlib/tests/unit/v3/policy/test_resources.py View File

@@ -1931,7 +1931,7 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase):
'display_name': 'map_name',
'rules': [
{'id': entry1_id, 'resource_type': 'Rule',
'dsiplay_name': 'name1', 'scope': ['scope1']},
'display_name': 'name1', 'scope': ['scope1']},
{'id': entry2_id, 'resource_type': 'Rule',
'display_name': 'name2', 'scope': ['scope2']},
{'id': entry3_id, 'resource_type': 'Rule',
@@ -1943,7 +1943,7 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase):
'display_name': 'new_map_name',
'rules': [
{'id': entry1_id, 'resource_type': 'Rule',
'dsiplay_name': 'name1', 'scope': ['new_scope1']},
'display_name': 'name1', 'scope': ['new_scope1']},
{'id': entry2_id, 'resource_type': 'Rule',
'display_name': 'name2', 'scope': ['scope2']}]}
map_def = self.mapDef(
@@ -1960,6 +1960,51 @@ class TestPolicyCommunicationMap(NsxPolicyLibTestCase):
update_call.assert_called_once_with(
map_def.get_resource_path(), updated_map)

def test_update_with_entries_for_IGNORE_entries(self):
domain_id = '111'
map_id = '222'
entry1_id = 'entry1'
entry2_id = 'entry2'
entry3_id = 'entry3'
original_map = {
'id': map_id,
'resource_type': self.resource_type,
'category': constants.CATEGORY_APPLICATION,
'display_name': 'map_name',
'rules': [
{'id': entry1_id, 'resource_type': 'Rule',
'display_name': 'name1', 'scope': ['scope1'],
'_created_time': 1},
{'id': entry2_id, 'resource_type': 'Rule',
'display_name': 'name2', 'scope': ['scope2']},
{'id': entry3_id, 'resource_type': 'Rule',
'display_name': 'name3', 'scope': ['scope3']}]}
updated_map = {
'id': map_id,
'resource_type': self.resource_type,
'category': constants.CATEGORY_APPLICATION,
'display_name': 'new_map_name',
'rules': [
{'id': entry1_id, 'resource_type': 'Rule',
'display_name': 'name1', 'scope': ['scope1'],
'_created_time': 1},
{'id': entry2_id, 'resource_type': 'Rule',
'display_name': 'name2', 'scope': ['scope2']},
{'id': entry3_id, 'resource_type': 'Rule',
'display_name': 'name3', 'scope': ['scope3']}]}
map_def = self.mapDef(
domain_id=domain_id,
map_id=map_id,
tenant=TEST_TENANT)
with mock.patch.object(self.policy_api, "get",
return_value=original_map),\
mock.patch.object(self.policy_api.client,
"update") as update_call:
self.resourceApi.update_with_entries(
domain_id, map_id, name='new_map_name', tenant=TEST_TENANT)
update_call.assert_called_once_with(
map_def.get_resource_path(), updated_map)

def test_unset(self):
name = 'hello'
domain_id = 'test'
@@ -3050,6 +3095,7 @@ class TestPolicyTier0NatRule(NsxPolicyLibTestCase):
tier0_id = '111'
nat_rule_id = 'rule1'
action = constants.NAT_ACTION_SNAT
firewall_match = constants.NAT_FIREWALL_MATCH_INTERNAL
cidr1 = '1.1.1.1/32'
cidr2 = '2.2.2.0/24'
enabled = True
@@ -3063,6 +3109,7 @@ class TestPolicyTier0NatRule(NsxPolicyLibTestCase):
action=action,
translated_network=cidr1,
source_network=cidr2,
firewall_match=firewall_match,
tenant=TEST_TENANT,
enabled=enabled)
expected_def = core_defs.Tier0NatRule(
@@ -3074,6 +3121,7 @@ class TestPolicyTier0NatRule(NsxPolicyLibTestCase):
action=action,
translated_network=cidr1,
source_network=cidr2,
firewall_match=firewall_match,
tenant=TEST_TENANT,
enabled=enabled)
self.assert_called_with_def(api_call, expected_def)
@@ -3109,6 +3157,44 @@ class TestPolicyTier0NatRule(NsxPolicyLibTestCase):
self.assert_called_with_def(api_call, expected_def)
self.assertEqual(mock_t0_nat_rule, result)

def test_update(self):
name = 'test'
description = 'desc'
tier0_id = '111'
nat_rule_id = 'rule1'
action = constants.NAT_ACTION_SNAT
firewall_match = constants.NAT_FIREWALL_MATCH_EXTERNAL
cidr1 = '1.1.1.1/32'
cidr2 = '2.2.2.0/24'
enabled = True

with mock.patch.object(self.policy_api,
"create_or_update") as api_call:
self.resourceApi.update(
tier0_id, nat_rule_id,
name=name,
description=description,
action=action,
translated_network=cidr1,
firewall_match=firewall_match,
source_network=cidr2,
tenant=TEST_TENANT,
enabled=enabled)

expected_def = core_defs.Tier0NatRule(
tier0_id=tier0_id,
nat_rule_id=nat_rule_id,
nat_id=self.resourceApi.DEFAULT_NAT_ID,
name=name,
description=description,
action=action,
translated_network=cidr1,
firewall_match=firewall_match,
source_network=cidr2,
tenant=TEST_TENANT,
enabled=enabled)
self.assert_called_with_def(api_call, expected_def)


class TestPolicyTier1NatRule(NsxPolicyLibTestCase):

@@ -3122,6 +3208,7 @@ class TestPolicyTier1NatRule(NsxPolicyLibTestCase):
tier1_id = '111'
nat_rule_id = 'rule1'
action = constants.NAT_ACTION_SNAT
firewall_match = constants.NAT_FIREWALL_MATCH_INTERNAL
cidr1 = '1.1.1.1/32'
cidr2 = '2.2.2.0/24'
enabled = True
@@ -3134,6 +3221,7 @@ class TestPolicyTier1NatRule(NsxPolicyLibTestCase):
description=description,
action=action,
translated_network=cidr1,
firewall_match=firewall_match,
source_network=cidr2,
tenant=TEST_TENANT,
enabled=enabled)
@@ -3146,6 +3234,7 @@ class TestPolicyTier1NatRule(NsxPolicyLibTestCase):
description=description,
action=action,
translated_network=cidr1,
firewall_match=firewall_match,
source_network=cidr2,
tenant=TEST_TENANT,
enabled=enabled)
@@ -3167,6 +3256,44 @@ class TestPolicyTier1NatRule(NsxPolicyLibTestCase):
tenant=TEST_TENANT)
self.assert_called_with_def(api_call, expected_def)

def test_update(self):
name = 'test'
description = 'desc'
tier1_id = '111'
nat_rule_id = 'rule1'
action = constants.NAT_ACTION_SNAT
firewall_match = constants.NAT_FIREWALL_MATCH_INTERNAL
cidr1 = '1.1.1.1/32'
cidr2 = '2.2.2.0/24'
enabled = True

with mock.patch.object(self.policy_api,
"create_or_update") as api_call:
self.resourceApi.update(
tier1_id, nat_rule_id,
name=name,
description=description,
action=action,
translated_network=cidr1,
firewall_match=firewall_match,
source_network=cidr2,
tenant=TEST_TENANT,
enabled=enabled)

expected_def = core_defs.Tier1NatRule(
tier1_id=tier1_id,
nat_rule_id=nat_rule_id,
nat_id=self.resourceApi.DEFAULT_NAT_ID,
name=name,
description=description,
action=action,
translated_network=cidr1,
firewall_match=firewall_match,
source_network=cidr2,
tenant=TEST_TENANT,
enabled=enabled)
self.assert_called_with_def(api_call, expected_def)


class TestPolicyTier1StaticRoute(NsxPolicyLibTestCase):

@@ -3846,6 +3973,17 @@ class TestPolicyIpPool(NsxPolicyLibTestCase):
tenant=TEST_TENANT)
self.assert_called_with_def(api_call, expected_def)

def test_get_realization_info(self):
ip_pool_id = '111'
with mock.patch.object(
self.resourceApi, "_get_realization_info") as api_call:
self.resourceApi.get_realization_info(
ip_pool_id, tenant=TEST_TENANT)
expected_def = core_defs.IpPoolDef(
ip_pool_id=ip_pool_id,
tenant=TEST_TENANT)
self.assert_called_with_def_and_dict(api_call, expected_def, {})

def test_get_static_subnet_realization_info(self):
ip_pool_id = 'ip-pool-id'
ip_subnet_id = 'static-subnet-id'
@@ -3870,6 +4008,53 @@ class TestPolicyIpPool(NsxPolicyLibTestCase):
wait=True, subnet_type=constants.IPPOOL_STATIC_SUBNET)
api_get.assert_called_once()

def test_wait_until_realized_fail(self):
ip_pool_id = 'p1'
info = {'state': constants.STATE_UNREALIZED,
'realization_specific_identifier': ip_pool_id,
'entity_type': 'IpPool'}
with mock.patch.object(self.resourceApi, "_get_realization_info",
return_value=info):
self.assertRaises(nsxlib_exc.RealizationTimeoutError,
self.resourceApi.wait_until_realized,
ip_pool_id, max_attempts=5, sleep=0.1,
tenant=TEST_TENANT)

def test_wait_until_realized_error(self):
ip_alloc_id = 'ip_alloc_1'
error_code = 5109
error_msg = 'Insufficient free IP addresses.'
info = {'state': constants.STATE_ERROR,
'realization_specific_identifier': ip_alloc_id,
'entity_type': 'AllocationIpAddress',
'alarms': [{
'message': error_msg,
'error_details': {
'error_code': error_code,
'module_name': 'id-allocation service',
'error_message': error_msg
}
}]}
with mock.patch.object(self.resourceApi, "_get_realization_info",
return_value=info):
with self.assertRaises(nsxlib_exc.RealizationErrorStateError) as e:
self.resourceApi.wait_until_realized(
ip_alloc_id, tenant=TEST_TENANT)
self.assertTrue(e.exception.msg.endswith(error_msg))
self.assertEqual(e.exception.error_code, error_code)
self.assertEqual(e.exception.related_error_codes, [])

def test_wait_until_realized_succeed(self):
ip_pool_id = 'p1'
info = {'state': constants.STATE_REALIZED,
'realization_specific_identifier': ip_pool_id,
'entity_type': 'IpPool'}
with mock.patch.object(self.resourceApi, "_get_realization_info",
return_value=info):
actual_info = self.resourceApi.wait_until_realized(
ip_pool_id, max_attempts=5, sleep=0.1, tenant=TEST_TENANT)
self.assertEqual(info, actual_info)


class TestPolicySegmentPort(NsxPolicyLibTestCase):

@@ -4747,3 +4932,88 @@ class TestPolicyExcludeList(NsxPolicyLibTestCase):

def test_update(self):
self.skipTest("The action is not supported by this resource")


class TestNsxSearch(NsxPolicyLibTestCase):

def setUp(self):
super(TestNsxSearch, self).setUp()
self.search_path = 'search/query?query=%s'

def test_nsx_search_by_realization(self):
"""Test search of resources with the specified tag."""
with mock.patch.object(self.policy_lib.client, 'url_get') as search:
realized_id = 'xxx'
realized_type = 'RealizedLogicalSwitch'
query = ('resource_type:GenericPolicyRealizedResource AND '
'realization_specific_identifier:%s AND '
'entity_type:%s' % (realized_id, realized_type))
self.policy_lib.search_resource_by_realized_id(
realized_id, realized_type)
search.assert_called_with(self.search_path % query)


class TestPolicyGlobalConfig(NsxPolicyLibTestCase):

def setUp(self, *args, **kwargs):
super(TestPolicyGlobalConfig, self).setUp()
self.resourceApi = self.policy_lib.global_config

def test_create_or_overwrite(self):
self.skipTest("The action is not supported by this resource")

def test_delete(self):
self.skipTest("The action is not supported by this resource")

def test_get(self):
with mock.patch.object(self.policy_api, "get") as api_call:
self.resourceApi.get(tenant=TEST_TENANT)
expected_def = core_defs.GlobalConfigDef(
tenant=TEST_TENANT)
self.assert_called_with_def(api_call, expected_def)

def test_list(self):
self.skipTest("The action is not supported by this resource")

def test_update(self):
self.skipTest("The action is not supported by this resource")

def test_enable_ipv6(self):
current_config = {'l3_forwarding_mode': 'IPV4_ONLY'}
with mock.patch.object(self.policy_api, "get",
return_value=current_config) as api_get,\
mock.patch.object(self.policy_api.client, "update") as api_put:
self.resourceApi.enable_ipv6(tenant=TEST_TENANT)
api_get.assert_called_once()
api_put.assert_called_once_with(
"%s/global-config/" % TEST_TENANT,
{'l3_forwarding_mode': 'IPV4_AND_IPV6'})

def test_enable_ipv6_no_call(self):
current_config = {'l3_forwarding_mode': 'IPV4_AND_IPV6'}
with mock.patch.object(self.policy_api, "get",
return_value=current_config) as api_get,\
mock.patch.object(self.policy_api.client, "update") as api_put:
self.resourceApi.enable_ipv6(tenant=TEST_TENANT)
api_get.assert_called_once()
api_put.assert_not_called()

def test_disable_ipv6(self):
current_config = {'l3_forwarding_mode': 'IPV4_AND_IPV6'}
with mock.patch.object(self.policy_api, "get",
return_value=current_config) as api_get,\
mock.patch.object(self.policy_api.client, "update") as api_put:
self.resourceApi.disable_ipv6(tenant=TEST_TENANT)
api_get.assert_called_once()
api_put.assert_called_once_with(
"%s/global-config/" % TEST_TENANT,
{'l3_forwarding_mode': 'IPV4_ONLY'})

def test_disable_ipv6_no_call(self):
current_config = {'l3_forwarding_mode': 'IPV4_ONLY'}
with mock.patch.object(self.policy_api, "get",
return_value=current_config) as api_get,\
mock.patch.object(self.policy_api.client, "update") as api_put:
self.resourceApi.disable_ipv6(tenant=TEST_TENANT)
api_get.assert_called_once()
api_put.assert_not_called()

+ 188
- 0
vmware_nsxlib/tests/unit/v3/policy/test_transaction.py View File

@@ -14,6 +14,8 @@
# under the License.
#

import copy

import mock

from vmware_nsxlib.tests.unit.v3 import nsxlib_testcase
@@ -248,3 +250,189 @@ class TestPolicyTransaction(policy_testcase.TestPolicyApi):
'Segment': seg2}]}

self.assert_infra_patch_call(expected_body)

def test_creating_security_policy_and_dfw_rules(self):
dfw_rule = {'id': 'rule_id1', 'action': 'ALLOW',
'display_name': 'rule1', 'description': None,
'direction': 'IN_OUT', 'ip_protocol': 'IPV4_IPV6',
'logged': False, 'destination_groups': ['destination_url'],
'source_groups': ['src_url'], 'resource_type': 'Rule',
'scope': None, 'sequence_number': None, 'tag': None,
'services': ['ANY']}
security_policy = {'id': 'security_policy_id1',
'display_name': 'security_policy',
'category': 'Application',
'resource_type': 'SecurityPolicy'}
domain = {'resource_type': 'Domain', 'id': 'domain1'}
domain_id = domain['id']
map_id = security_policy['id']
dfw_rule_entries = [self.policy_lib.comm_map.build_entry(
name=dfw_rule['display_name'],
domain_id=domain_id,
map_id=map_id,
entry_id=dfw_rule['id'],
source_groups=dfw_rule['source_groups'],
dest_groups=dfw_rule['destination_groups']
)]
with trans.NsxPolicyTransaction():
self.policy_lib.comm_map.create_with_entries(
name=security_policy['display_name'],
domain_id=domain_id,
map_id=map_id,
entries=dfw_rule_entries
)

def get_group_path(group_id, domain_id):
return '/infra/domains/' + domain_id + '/groups/' + group_id

dfw_rule['destination_groups'] = [get_group_path(group_id, domain_id)
for group_id in
dfw_rule['destination_groups']]
dfw_rule['source_groups'] = [get_group_path(group_id, domain_id) for
group_id in dfw_rule['source_groups']]
child_rules = [{'resource_type': 'ChildRule', 'Rule': dfw_rule}]
security_policy.update({'children': child_rules})
child_security_policies = [{
'resource_type': 'ChildSecurityPolicy',
'SecurityPolicy': security_policy
}]
domain.update({'children': child_security_policies})
child_domains = [{'resource_type': 'ChildDomain',
'Domain': domain}]
expected_body = {'resource_type': 'Infra',
'children': child_domains}
self.assert_infra_patch_call(expected_body)

@mock.patch('vmware_nsxlib.v3.policy.core_defs.NsxPolicyApi.get')
def test_updating_security_policy_and_dfw_rules(self, mock_get_api):
dfw_rule1 = {'id': 'rule_id1', 'action': 'ALLOW',
'display_name': 'rule1', 'description': None,
'direction': 'IN_OUT', 'ip_protocol': 'IPV4_IPV6',
'logged': False,
'destination_groups': ['destination_url'],
'source_groups': ['src_url'], 'resource_type': 'Rule',
'scope': None, 'sequence_number': None, 'tag': None,
'services': ['ANY'], "_create_time": 1}
dfw_rule2 = {'id': 'rule_id2', 'action': 'DROP',
'display_name': 'rule2', 'description': None,
'direction': 'IN_OUT', 'ip_protocol': 'IPV4_IPV6',
'logged': False,
'destination_groups': ['destination_url'],
'source_groups': ['src_url'], 'resource_type': 'Rule',
'scope': None, 'sequence_number': None, 'tag': None,
'services': ['ANY'], "_create_time": 1}
security_policy = {'id': 'security_policy_id1',
'display_name': 'security_policy',
'category': 'Application',
'resource_type': 'SecurityPolicy'}
domain = {'resource_type': 'Domain', 'id': 'domain1'}
domain_id = domain['id']
map_id = security_policy['id']
new_rule_name = 'new_rule1'
new_direction = 'IN'
dfw_rule_entries = [self.policy_lib.comm_map.build_entry(
name=new_rule_name,
domain_id=domain_id,
map_id=map_id,
entry_id=dfw_rule1['id'],
source_groups=dfw_rule1['source_groups'],
dest_groups=dfw_rule1['destination_groups'],
direction=new_direction
)]

def get_group_path(group_id, domain_id):
return '/infra/domains/' + domain_id + '/groups/' + group_id

for dfw_rule in [dfw_rule1, dfw_rule2]:
dfw_rule['destination_groups'] = [get_group_path(group_id,
domain_id)
for group_id in
dfw_rule['destination_groups']]
dfw_rule['source_groups'] = [get_group_path(group_id, domain_id)
for group_id in
dfw_rule['source_groups']]

security_policy_values = copy.deepcopy(security_policy)
security_policy_values.update({'rules':
copy.deepcopy([dfw_rule1, dfw_rule2])})
mock_get_api.return_value = security_policy_values

with trans.NsxPolicyTransaction():
self.policy_lib.comm_map.update_with_entries(
name=security_policy['display_name'],
domain_id=domain_id,
map_id=map_id,
entries=dfw_rule_entries
)

dfw_rule1['display_name'] = new_rule_name
dfw_rule1['direction'] = new_direction
child_rules = [{'resource_type': 'ChildRule', 'Rule': dfw_rule1},
{'resource_type': 'ChildRule', 'Rule': dfw_rule2,
'marked_for_delete': True}]
security_policy.update({'children': child_rules})
child_security_policies = [{
'resource_type': 'ChildSecurityPolicy',
'SecurityPolicy': security_policy
}]
domain.update({'children': child_security_policies})
child_domains = [{
'resource_type': 'ChildDomain',
'Domain': domain
}]
expected_body = {'resource_type': 'Infra',
'children': child_domains}
self.assert_infra_patch_call(expected_body)

@mock.patch('vmware_nsxlib.v3.policy.core_defs.NsxPolicyApi.get')
def test_updating_security_policy_with_no_entries_set(self, mock_get_api):
dfw_rule1 = {'id': 'rule_id1', 'action': 'ALLOW',
'display_name': 'rule1', 'description': None,
'direction': 'IN_OUT', 'ip_protocol': 'IPV4_IPV6',
'logged': False,
'destination_groups': ['destination_url'],
'source_groups': ['src_url'], 'resource_type': 'Rule',
'scope': None, 'sequence_number': None, 'tag': None,
'services': ['ANY'], "_create_time": 1}
security_policy = {'id': 'security_policy_id1',
'display_name': 'security_policy',
'category': 'Application',
'resource_type': 'SecurityPolicy'}
domain = {'resource_type': 'Domain', 'id': 'domain1'}
domain_id = domain['id']
map_id = security_policy['id']

def get_group_path(group_id, domain_id):
return '/infra/domains/' + domain_id + '/groups/' + group_id

for dfw_rule in [dfw_rule1]:
dfw_rule['destination_groups'] = [get_group_path(group_id,
domain_id)
for group_id in
dfw_rule['destination_groups']]
dfw_rule['source_groups'] = [get_group_path(group_id, domain_id)
for group_id in
dfw_rule['source_groups']]

security_policy.update({'rules': [dfw_rule1]})
mock_get_api.return_value = security_policy

with trans.NsxPolicyTransaction():
self.policy_lib.comm_map.update_with_entries(
name=security_policy['display_name'],
domain_id=domain_id,
map_id=map_id
)

child_security_policies = [{
'resource_type': 'ChildSecurityPolicy',
'SecurityPolicy': security_policy
}]
domain.update({'children': child_security_policies})
child_domains = [{
'resource_type': 'ChildDomain',
'Domain': domain
}]
expected_body = {'resource_type': 'Infra',
'children': child_domains}
self.assert_infra_patch_call(expected_body)

+ 2
- 1
vmware_nsxlib/tests/unit/v3/test_constants.py View File

@@ -334,7 +334,8 @@ FAKE_SERVICE = {
"attachment": {
"target_id": FAKE_ROUTER_UUID,
"target_type": "LogicalRouter"
}
},
"relax_scale_validation": False
}

FAKE_TZ_UUID = uuidutils.generate_uuid()

+ 11
- 7
vmware_nsxlib/tests/unit/v3/test_load_balancer.py View File

@@ -500,15 +500,19 @@ class TestService(nsxlib_testcase.NsxClientTestCase):
'description': fake_service['description'],
'enabled': fake_service['enabled'],
'attachment': fake_service['attachment'],
'relax_scale_validation': fake_service['relax_scale_validation'],
'tags': consts.FAKE_TAGS
}
with mock.patch.object(self.nsxlib.client, 'create') as create:
self.nsxlib.load_balancer.service.create(
body['display_name'], body['description'],
consts.FAKE_TAGS, enabled=body['enabled'],
attachment=body['attachment'])
create.assert_called_with('loadbalancer/services',
body)
with mock.patch.object(self.nsxlib.client, 'create') as create, \
mock.patch.object(self.nsxlib, 'feature_supported') as support:
support.return_value = True
self.nsxlib.load_balancer.service.create(
body['display_name'], body['description'],
consts.FAKE_TAGS, enabled=body['enabled'],
attachment=body['attachment'],
relax_scale_validation=body['relax_scale_validation'])
create.assert_called_with('loadbalancer/services',
body)

def test_list_services(self):
with mock.patch.object(self.nsxlib.client, 'list') as list_call:

+ 40
- 12
vmware_nsxlib/tests/unit/v3/test_resources.py View File

@@ -1565,13 +1565,27 @@ class IpPoolTestCase(BaseTestResource):

class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):

def setUp(self):
super(TestNsxSearch, self).setUp()
self.search_path = 'search?query=%s'
self.mock = mock.patch("vmware_nsxlib.v3.NsxLib.get_version",
return_value=self.get_nsxlib_version())
self.mock.start()

def tearDown(self):
self.mock.stop()

@staticmethod
def get_nsxlib_version():
return '2.5.0'

def test_nsx_search_tags(self):
"""Test search of resources with the specified tag."""
with mock.patch.object(self.nsxlib.client, 'url_get') as search:
user_tags = [{'scope': 'user', 'tag': 'k8s'}]
query = self.nsxlib._build_query(tags=user_tags)
self.nsxlib.search_by_tags(tags=user_tags)
search.assert_called_with('search?query=%s' % query)
search.assert_called_with(self.search_path % query)

def test_nsx_search_tags_scope_only(self):
"""Test search of resources with the specified tag."""
@@ -1579,7 +1593,7 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
user_tags = [{'scope': 'user'}]
query = self.nsxlib._build_query(tags=user_tags)
self.nsxlib.search_by_tags(tags=user_tags)
search.assert_called_with('search?query=%s' % query)
search.assert_called_with(self.search_path % query)

def test_nsx_search_tags_tag_only(self):
"""Test search of resources with the specified tag."""
@@ -1587,7 +1601,7 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
user_tags = [{'tag': 'k8s'}]
query = self.nsxlib._build_query(tags=user_tags)
self.nsxlib.search_by_tags(tags=user_tags)
search.assert_called_with('search?query=%s' % query)
search.assert_called_with(self.search_path % query)

def test_nsx_search_by_resouce_type_and_attributes(self):
with mock.patch.object(self.nsxlib.client, 'url_get') as search:
@@ -1598,7 +1612,7 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
exp_query = 'resource_type:%s AND color:%s' % (
resource_type, attributes['color'])
search.assert_called_with(
'search?query=%s' % exp_query)
self.search_path % exp_query)

def test_nsx_search_by_resouce_type_only(self):
with mock.patch.object(self.nsxlib.client, 'url_get') as search:
@@ -1606,7 +1620,7 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
self.nsxlib.search_resource_by_attributes(resource_type)
exp_query = 'resource_type:%s' % resource_type
search.assert_called_with(
'search?query=%s' % exp_query)
self.search_path % exp_query)

def test_nsx_search_no_resource_type_fails(self):
self.assertRaises(exceptions.NsxSearchInvalidQuery,
@@ -1622,7 +1636,7 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
exp_query = 'resource_type:%s AND color:%s' % (
resource_type, attributes['color'])
search.assert_called_with(
'search?query=%s&cursor=50&page_size=100' % exp_query)
(self.search_path + '&cursor=50&page_size=100') % exp_query)

def test_nsx_search_tags_tag_and_scope(self):
"""Test search of resources with the specified tag."""
@@ -1630,7 +1644,7 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
user_tags = [{'tag': 'k8s'}, {'scope': 'user'}]
query = self.nsxlib._build_query(tags=user_tags)
self.nsxlib.search_by_tags(tags=user_tags)
search.assert_called_with('search?query=%s' % query)
search.assert_called_with(self.search_path % query)

def test_nsx_search_tags_and_resource_type(self):
"""Test search of specified resource with the specified tag."""
@@ -1641,7 +1655,7 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
# Add resource_type to the query
query = "resource_type:%s AND %s" % (res_type, query)
self.nsxlib.search_by_tags(tags=user_tags, resource_type=res_type)
search.assert_called_with('search?query=%s' % query)
search.assert_called_with(self.search_path % query)

def test_nsx_search_tags_and_cursor(self):
"""Test search of resources with the specified tag and cursor."""
@@ -1649,7 +1663,8 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
user_tags = [{'scope': 'user', 'tag': 'k8s'}]
query = self.nsxlib._build_query(tags=user_tags)
self.nsxlib.search_by_tags(tags=user_tags, cursor=50)
search.assert_called_with('search?query=%s&cursor=50' % query)
search.assert_called_with(
(self.search_path + '&cursor=50') % query)

def test_nsx_search_tags_and_page_size(self):
"""Test search of resources with the specified tag and page size."""
@@ -1657,7 +1672,8 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
user_tags = [{'scope': 'user', 'tag': 'k8s'}]
query = self.nsxlib._build_query(tags=user_tags)
self.nsxlib.search_by_tags(tags=user_tags, page_size=100)
search.assert_called_with('search?query=%s&page_size=100' % query)
search.assert_called_with(
(self.search_path + '&page_size=100') % query)

def test_nsx_search_invalid_query_fail(self):
"""Test search query failure for missing tag argument."""
@@ -1687,8 +1703,8 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
query = self.nsxlib._build_query(tags=user_tags)
results = self.nsxlib.search_all_by_tags(tags=user_tags)
search.assert_has_calls([
mock.call('search?query=%s' % query),
mock.call('search?query=%s&cursor=2' % query)])
mock.call(self.search_path % query),
mock.call((self.search_path + '&cursor=2') % query)])
self.assertEqual(3, len(results))

def test_get_id_by_resource_and_tag(self):
@@ -1726,6 +1742,18 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
res_type, scope, tag, alert_multiple=True)


class TestNsxSearchNew(TestNsxSearch):

def setUp(self):

super(TestNsxSearchNew, self).setUp()
self.search_path = 'search/query?query=%s'

@staticmethod
def get_nsxlib_version():
return '3.0.0'


class TransportZone(BaseTestResource):

def setUp(self):

+ 10
- 4
vmware_nsxlib/v3/__init__.py View File

@@ -91,7 +91,7 @@ class NsxLib(lib.NsxLibBase):
self.ip_pool = resources.IpPool(
self.client, self.nsxlib_config, nsxlib=self)
self.load_balancer = load_balancer.LoadBalancer(
self.client, self.nsxlib_config)
self.client, self.nsxlib_config, nsxlib=self)
self.trust_management = trust_management.NsxLibTrustManagement(
self.client, self.nsxlib_config)
self.router = router.RouterLib(
@@ -168,7 +168,13 @@ class NsxLib(lib.NsxLibBase):

def feature_supported(self, feature):
if (version.LooseVersion(self.get_version()) >=
version.LooseVersion(nsx_constants.NSX_VERSION_2_5_0)):
version.LooseVersion(nsx_constants.NSX_VERSION_3_0_0)):
# features available since 3.0.0
if (feature == nsx_constants.FEATURE_RELAX_SCALE_VALIDATION):
return True

if (version.LooseVersion(self.get_version()) >=
version.LooseVersion(nsx_constants.NSX_VERSION_2_5_0)):
# features available since 2.5
if (feature == nsx_constants.FEATURE_CONTAINER_CLUSTER_INVENTORY):
return True
@@ -178,7 +184,7 @@ class NsxLib(lib.NsxLibBase):
return True

if (version.LooseVersion(self.get_version()) >=
version.LooseVersion(nsx_constants.NSX_VERSION_2_4_0)):
version.LooseVersion(nsx_constants.NSX_VERSION_2_4_0)):
# Features available since 2.4
if (feature == nsx_constants.FEATURE_ENS_WITH_SEC):
return True
@@ -188,7 +194,7 @@ class NsxLib(lib.NsxLibBase):
return True

if (version.LooseVersion(self.get_version()) >=
version.LooseVersion(nsx_constants.NSX_VERSION_2_3_0)):
version.LooseVersion(nsx_constants.NSX_VERSION_2_3_0)):
# Features available since 2.3
if (feature == nsx_constants.FEATURE_ROUTER_ALLOCATION_PROFILE):
return True

+ 14
- 2
vmware_nsxlib/v3/lib.py View File

@@ -14,6 +14,7 @@
# under the License.

import abc
from distutils import version

from oslo_log import log
import six
@@ -22,6 +23,7 @@ from vmware_nsxlib._i18n import _
from vmware_nsxlib.v3 import client
from vmware_nsxlib.v3 import cluster
from vmware_nsxlib.v3 import exceptions
from vmware_nsxlib.v3 import nsx_constants
from vmware_nsxlib.v3 import utils

LOG = log.getLogger(__name__)
@@ -80,6 +82,10 @@ class NsxLibBase(object):
def feature_supported(self, feature):
pass

@abc.abstractmethod
def get_version(self):
pass

def build_v3_api_version_tag(self):
return self.general_apis.build_v3_api_version_tag()

@@ -107,6 +113,12 @@ class NsxLibBase(object):
url += "&page_size=%d" % page_size
return url

def _get_search_url(self):
if (version.LooseVersion(self.get_version()) >=
version.LooseVersion(nsx_constants.NSX_VERSION_3_0_0)):
return "search/query?query=%s"
return "search?query=%s"

# TODO(abhiraut): Revisit this method to generate complex boolean
# queries to search resources.
def search_by_tags(self, tags, resource_type=None, cursor=None,
@@ -133,7 +145,7 @@ class NsxLibBase(object):
query += " AND %s" % query_tags
else:
query = query_tags
url = self._add_pagination_parameters("search?query=%s" % query,
url = self._add_pagination_parameters(self._get_search_url() % query,
cursor, page_size)

# Retry the search in case of error
@@ -170,7 +182,7 @@ class NsxLibBase(object):
in attributes.items()])
query = 'resource_type:%s' % resource_type + (
" AND %s" % attributes_query if attributes_query else "")
url = self._add_pagination_parameters("search?query=%s" % query,
url = self._add_pagination_parameters(self._get_search_url() % query,
cursor, page_size)

# Retry the search in case of error

+ 24
- 2
vmware_nsxlib/v3/load_balancer.py View File

@@ -16,6 +16,7 @@
from oslo_log import log as logging

from vmware_nsxlib.v3 import exceptions as nsxlib_exc
from vmware_nsxlib.v3 import nsx_constants
from vmware_nsxlib.v3 import utils

LOG = logging.getLogger(__name__)
@@ -411,6 +412,27 @@ class VirtualServer(LoadBalancerBase):
class Service(LoadBalancerBase):
resource = 'loadbalancer/services'

def _build_args(self, body, display_name=None, description=None,
tags=None, resource_type=None, **kwargs):
if display_name:
body['display_name'] = display_name
if description:
body['description'] = description
if tags:
body['tags'] = tags
if resource_type:
body['resource_type'] = resource_type

if ('relax_scale_validation' in kwargs and
not self.nsxlib.feature_supported(
nsx_constants.FEATURE_RELAX_SCALE_VALIDATION)):
kwargs.pop('relax_scale_validation')
LOG.warning("Ignoring relax_scale_validation for new "
"lb service %s: this feature is not supported.",
display_name)
body.update(kwargs)
return body

def update_service_with_virtual_servers(self, service_id,
virtual_server_ids):
# Using internal method so we can access max_attempts in the decorator
@@ -475,8 +497,8 @@ class Service(LoadBalancerBase):
class LoadBalancer(object):
"""This is the class that have all load balancer resource clients"""

def __init__(self, client, nsxlib_config=None):
self.service = Service(client, nsxlib_config)
def __init__(self, client, nsxlib_config=None, nsxlib=None):
self.service = Service(client, nsxlib_config, nsxlib)
self.virtual_server = VirtualServer(client, nsxlib_config)
self.pool = Pool(client, nsxlib_config)
self.monitor = Monitor(client, nsxlib_config)

+ 2
- 0
vmware_nsxlib/v3/nsx_constants.py View File

@@ -169,11 +169,13 @@ FEATURE_ICMP_STRICT = 'Strict list of supported ICMP types and codes'
FEATURE_ROUTER_ALLOCATION_PROFILE = 'Router Allocation Profile'
FEATURE_ENABLE_STANDBY_RELOCATION = 'Router Enable standby relocation'
FEATURE_PARTIAL_UPDATES = 'Partial Update with PATCH'
FEATURE_RELAX_SCALE_VALIDATION = 'Relax Scale Validation for LbService'

# Features available depending on the Policy Manager backend version
FEATURE_NSX_POLICY = 'NSX Policy'
FEATURE_NSX_POLICY_NETWORKING = 'NSX Policy Networking'
FEATURE_NSX_POLICY_MDPROXY = 'NSX Policy Metadata Proxy'
FEATURE_NSX_POLICY_GLOBAL_CONFIG = 'NSX Policy Global Config'

# FEATURE available depending on Inventory service backend version
FEATURE_CONTAINER_CLUSTER_INVENTORY = 'Container Cluster Inventory'

+ 33
- 0
vmware_nsxlib/v3/policy/__init__.py View File

@@ -20,8 +20,10 @@ from oslo_log import log

from vmware_nsxlib import v3
from vmware_nsxlib.v3 import client
from vmware_nsxlib.v3 import exceptions
from vmware_nsxlib.v3 import lib
from vmware_nsxlib.v3 import nsx_constants
from vmware_nsxlib.v3 import utils as lib_utils

from vmware_nsxlib.v3.policy import core_defs
from vmware_nsxlib.v3.policy import core_resources
@@ -120,6 +122,7 @@ class NsxPolicyLib(lib.NsxLibBase):
self.exclude_list = core_resources.NsxPolicyExcludeListApi(*args)
self.load_balancer = lb_resources.NsxPolicyLoadBalancerApi(*args)
self.ipsec_vpn = ipsec_vpn_resources.NsxPolicyIpsecVpnApi(*args)
self.global_config = core_resources.NsxPolicyGlobalConfig(*args)

@property
def keepalive_section(self):
@@ -161,10 +164,15 @@ class NsxPolicyLib(lib.NsxLibBase):

if (version.LooseVersion(self.get_version()) >=
version.LooseVersion(nsx_constants.NSX_VERSION_3_0_0)):
# features available since 3.0.0
if feature == nsx_constants.FEATURE_PARTIAL_UPDATES:
return True
if feature == nsx_constants.FEATURE_NSX_POLICY_MDPROXY:
return True
if (feature == nsx_constants.FEATURE_RELAX_SCALE_VALIDATION):
return True
if (feature == nsx_constants.FEATURE_NSX_POLICY_GLOBAL_CONFIG):
return True

return (feature == nsx_constants.FEATURE_NSX_POLICY)

@@ -185,3 +193,28 @@ class NsxPolicyLib(lib.NsxLibBase):
"value": "0 */%d * * * *" % interval_min}
body = {"keyValuePairs": [realization_config]}
self.client.patch("system-config", body)

def search_resource_by_realized_id(self, realized_id, realized_type):
"""Search resources by a realized id & type

:returns: a list of resource pathes matching the realized id and type.
"""
if not realized_type or not realized_id:
raise exceptions.NsxSearchInvalidQuery(
reason=_("Resource type or id was not specified"))
query = ('resource_type:GenericPolicyRealizedResource AND '
'realization_specific_identifier:%s AND '
'entity_type:%s' % (realized_id, realized_type))
url = self._get_search_url() % query

# Retry the search on case of error
@lib_utils.retry_upon_exception(exceptions.NsxSearchError,
max_attempts=self.client.max_attempts)
def do_search(url):
return self.client.url_get(url)

results = do_search(url)
pathes = []
for resource in results['results']:
pathes.extend(resource.get('intent_paths', []))
return pathes

+ 89
- 3
vmware_nsxlib/v3/policy/core_defs.py View File

@@ -31,6 +31,7 @@ PROVIDERS_PATH_PATTERN = TENANTS_PATH_PATTERN + "providers/"
TIER0S_PATH_PATTERN = TENANTS_PATH_PATTERN + "tier-0s/"
TIER1S_PATH_PATTERN = TENANTS_PATH_PATTERN + "tier-1s/"
SERVICES_PATH_PATTERN = TENANTS_PATH_PATTERN + "services/"
GLOBAL_CONFIG_PATH_PATTERN = TENANTS_PATH_PATTERN + "global-config/"
ENFORCEMENT_POINT_PATTERN = (TENANTS_PATH_PATTERN +
"sites/default/enforcement-points/")
TRANSPORT_ZONE_PATTERN = ENFORCEMENT_POINT_PATTERN + "%s/transport-zones/"
@@ -65,14 +66,21 @@ TIER1_LOCALE_SERVICES_PATH_PATTERN = (TIER1S_PATH_PATTERN +

@six.add_metaclass(abc.ABCMeta)
class ResourceDef(object):
def __init__(self, **kwargs):
def __init__(self, nsx_version=None, **kwargs):
self.attrs = kwargs

# nsx_version should be passed in on init if the resource has
# version-dependant attributes. Otherwise this is ignored
self.nsx_version = nsx_version

# init default tenant
self.attrs['tenant'] = self.get_tenant()

self.body = {}

# Whether this entry needs to be deleted
self.delete = False

# As of now, for some defs (ex: services) child entry is required,
# meaning parent creation will fail without the child.
# Unfortunately in transactional API policy still fails us, even if
@@ -82,6 +90,12 @@ class ResourceDef(object):
# TODO(annak): remove this if/when policy solves this
self.mandatory_child_def = None

def set_delete(self):
self.delete = True

def get_delete(self):
return self.delete

def get_obj_dict(self):
body = self.body if self.body else {}
if self.resource_type():
@@ -192,6 +206,41 @@ class ResourceDef(object):
for attr in attr_list:
self._set_attr_if_specified(body, attr)

# Helper to set attr in body if user specified it
# and current nsx version supports it
# Body name must match attr name
def _set_attr_if_supported(self, body, attr, value=None):
if self.has_attr(attr) and self._version_dependant_attr_supported(
attr):
value = value if value is not None else self.get_attr(attr)
body[attr] = value

# Helper to set attrs in body if user specified them
# and current nsx version supports it
# Body name must match attr name
def _set_attrs_if_supported(self, body, attr_list):
for attr in attr_list:
self._set_attr_if_supported(body, attr)

def _version_dependant_attr_supported(self, attr):
"""Check if a version dependent attr is supported on current NSX

For each resource def, there could be some attributes which only exist
on NSX after certain versions. This abstract method provides a skeleton
to define version requirements of version-dependent attributes.

By design, Devs should use _set_attr_if_supported() to add any attrs
that are only known to NSX after a certain version. This method works
as a registry for _set_attrs_if_supported() to know the baseline
version of each version dependent attr.

Non-version-dependent attributes should be added to the request body
by using _set_attr_if_specified(). This method defaults to false since
any version dependent attr unknown to this lib should be excluded
for security and safety reasons.
"""
return False

@classmethod
def get_single_entry(cls, obj_body):
"""Return the single sub-entry from the object body.
@@ -790,9 +839,11 @@ class SegmentPortDef(ResourceDef):
if address_bindings:
body['address_bindings'] = [binding.get_obj_dict()
for binding in address_bindings]
if self.has_attr('attachment_type') or self.has_attr('vif_id'):
if (self.has_attr('attachment_type') or self.has_attr('vif_id') or
self.has_attr('hyperbus_mode')):
if (not self.get_attr('attachment_type') and
not self.get_attr('vif_id')):
not self.get_attr('vif_id') and
not self.get_attr('hyperbus_mode')):
# detach operation
body['attachment'] = None
else:
@@ -801,6 +852,8 @@ class SegmentPortDef(ResourceDef):
attachment['type'] = self.get_attr('attachment_type')
if self.get_attr('vif_id'):
attachment['id'] = self.get_attr('vif_id')
if self.get_attr('hyperbus_mode'):
self._set_attr_if_supported(attachment, 'hyperbus_mode')

self._set_attrs_if_specified(attachment,
['context_id',
@@ -1347,6 +1400,17 @@ class SecurityPolicyRuleBaseDef(ResourceDef):
body['services'] = self.get_services_path(service_ids)
return body

@classmethod
def adapt_from_rule_dict(cls, rule_dict, domain_id, map_id):
entry_id = rule_dict.pop('id', None)
name = rule_dict.pop('display_name', None)

rule_def = cls(tenant=constants.POLICY_INFRA_TENANT,
domain_id=domain_id, map_id=map_id, entry_id=entry_id,
name=name)
rule_def.set_obj_dict(rule_dict)
return rule_def


class CommunicationMapEntryDef(SecurityPolicyRuleBaseDef):

@@ -1785,6 +1849,28 @@ class CertificateDef(ResourceDef):
return body


class GlobalConfigDef(ResourceDef):

@property
def path_pattern(self):
return GLOBAL_CONFIG_PATH_PATTERN

@property
def path_ids(self):
# Adding dummy 2nd key to satisfy get_section_path
# This resource has no keys, since it is a single object
return ('tenant', 'dummy')

@staticmethod
def resource_type():
return "GlobalConfig"

def get_obj_dict(self):
body = super(GlobalConfigDef, self).get_obj_dict()
self._set_attrs_if_specified(body, ['l3_forwarding_mode'])
return body


class ExcludeListDef(ResourceDef):

@property

+ 122
- 23
vmware_nsxlib/v3/policy/core_resources.py View File

@@ -126,7 +126,7 @@ class NsxPolicyResourceBase(object):
def _init_def(self, **kwargs):
"""Helper for update function - ignore attrs without explicit value"""
args = self._get_user_args(**kwargs)
return self.entry_def(**args)
return self.entry_def(nsx_version=self.version, **args)

def _init_parent_def(self, **kwargs):
"""Helper for update function - ignore attrs without explicit value"""
@@ -136,7 +136,7 @@ class NsxPolicyResourceBase(object):
def _get_and_update_def(self, **kwargs):
"""Helper for update function - ignore attrs without explicit value"""
args = self._get_user_args(**kwargs)
resource_def = self.entry_def(**args)
resource_def = self.entry_def(nsx_version=self.version, **args)
body = self.policy_api.get(resource_def)
if body:
resource_def.set_obj_dict(body)
@@ -224,17 +224,23 @@ class NsxPolicyResourceBase(object):
realization_info.get('realization_specific_identifier')):
return realization_info['realization_specific_identifier']

def _get_realization_error_message(self, info):
def _get_realization_error_message_and_code(self, info):
error_msg = 'unknown'
error_code = None
related_error_codes = []
if info.get('alarms'):
alarm = info['alarms'][0]
error_msg = alarm.get('message')
if (alarm.get('error_details') and
alarm['error_details'].get('related_errors')):
related = alarm['error_details']['related_errors'][0]
error_msg = '%s: %s' % (error_msg,
related.get('error_message'))
return error_msg
if alarm.get('error_details'):
error_code = alarm['error_details'].get('error_code')
if alarm['error_details'].get('related_errors'):
related = alarm['error_details']['related_errors']
for err_obj in related:
error_msg = '%s: %s' % (error_msg,
err_obj.get('error_message'))
if err_obj.get('error_code'):
related_error_codes.append(err_obj['error_code'])
return error_msg, error_code, related_error_codes

def _wait_until_realized(self, resource_def, entity_type=None,
sleep=None, max_attempts=None):
@@ -255,11 +261,13 @@ class NsxPolicyResourceBase(object):
if info['state'] == constants.STATE_REALIZED:
return info
if info['state'] == constants.STATE_ERROR:
error_msg = self._get_realization_error_message(info)
error_msg, error_code, related_error_codes = \
self._get_realization_error_message_and_code(info)
raise exceptions.RealizationErrorStateError(
resource_type=resource_def.resource_type(),
resource_id=resource_def.get_id(),
error=error_msg)
error=error_msg, error_code=error_code,
related_error_codes=related_error_codes)

try:
return get_info()
@@ -303,11 +311,13 @@ class NsxPolicyResourceBase(object):
if resource_def and test_num % check_status == (check_status - 1):
info = self._get_realization_info(resource_def)
if info and info['state'] == constants.STATE_ERROR:
error_msg = self._get_realization_error_message(info)
error_msg, error_code, related_error_codes = \
self._get_realization_error_message_and_code(info)
raise exceptions.RealizationErrorStateError(
resource_type=resource_def.resource_type(),
resource_id=resource_def.get_id(),
error=error_msg)
error=error_msg, error_code=error_code,
related_error_codes=related_error_codes)
if (info and info['state'] == constants.STATE_REALIZED and
info.get('realization_specific_identifier')):
LOG.warning("Realization ID for %s was not found via "
@@ -1527,6 +1537,7 @@ class NsxPolicyTier0NatRuleApi(NsxPolicyResourceBase):
source_network=IGNORE,
destination_network=IGNORE,
translated_network=IGNORE,
firewall_match=IGNORE,
action=IGNORE,
sequence_number=IGNORE,
log=IGNORE,
@@ -1541,6 +1552,7 @@ class NsxPolicyTier0NatRuleApi(NsxPolicyResourceBase):
source_network=source_network,
destination_network=destination_network,
translated_network=translated_network,
firewall_match=firewall_match,
action=action,
sequence_number=sequence_number,
log=log,
@@ -1615,6 +1627,7 @@ class NsxPolicyTier1NatRuleApi(NsxPolicyResourceBase):
source_network=IGNORE,
destination_network=IGNORE,
translated_network=IGNORE,
firewall_match=IGNORE,
action=IGNORE,
sequence_number=IGNORE,
log=IGNORE,
@@ -1629,6 +1642,7 @@ class NsxPolicyTier1NatRuleApi(NsxPolicyResourceBase):
source_network=source_network,
destination_network=destination_network,
translated_network=translated_network,
firewall_match=firewall_match,
action=action,
sequence_number=sequence_number,
log=log,
@@ -2680,6 +2694,11 @@ class NsxPolicyIpPoolApi(NsxPolicyResourceBase):
tenant=tenant)
return self.policy_api.get(ip_subnet_def)

def get_realization_info(self, ip_pool_id, entity_type=None,
tenant=constants.POLICY_INFRA_TENANT):
ip_pool_def = self.entry_def(ip_pool_id=ip_pool_id, tenant=tenant)
return self._get_realization_info(ip_pool_def, entity_type=entity_type)

def get_ip_subnet_realization_info(
self, ip_pool_id, ip_subnet_id,
entity_type=None,
@@ -2737,6 +2756,14 @@ class NsxPolicyIpPoolApi(NsxPolicyResourceBase):
except IndexError:
return

def wait_until_realized(self, ip_pool_id, entity_type=None,
tenant=constants.POLICY_INFRA_TENANT,
sleep=None, max_attempts=None):
ip_pool_def = self.entry_def(ip_pool_id=ip_pool_id, tenant=tenant)
return self._wait_until_realized(ip_pool_def, entity_type=entity_type,
sleep=sleep,
max_attempts=max_attempts)


class NsxPolicySecurityPolicyBaseApi(NsxPolicyResourceBase):

@@ -2889,7 +2916,7 @@ class NsxPolicySecurityPolicyBaseApi(NsxPolicyResourceBase):
delay=self.nsxlib_config.realization_wait_sec,
max_attempts=self.nsxlib_config.realization_max_attempts)
def _do_create_with_retry():
self.policy_api.create_with_parent(map_def, entries)
self._create_or_store(map_def, entries)

_do_create_with_retry()
return map_id
@@ -3077,34 +3104,63 @@ class NsxPolicySecurityPolicyBaseApi(NsxPolicyResourceBase):
map_sequence_number=map_sequence_number)
map_path = map_def.get_resource_path()

def _overwrite_entries(old_entries, new_entries):
def _overwrite_entries(old_entries, new_entries, transaction):
# Replace old entries with new entries, but copy additional
# attributes from old entries for those kept in new entries.
# attributes from old entries for those kept in new entries
# and marked the unwanted ones in the old entries as deleted
# if it is in the transaction call.
old_rules = {entry["id"]: entry for entry in old_entries}
new_rules = []
replaced_entries = []
for entry in new_entries:
rule_id = entry.get_id()
new_rule = entry.get_obj_dict()
old_rule = old_rules.get(rule_id)
if old_rule:
old_rules.pop(rule_id)
for key, value in old_rule.items():
if key not in new_rule:
new_rule[key] = value
new_rules.append(new_rule)
return new_rules
replaced_entries.append(
self.entry_def.adapt_from_rule_dict(
new_rule, domain_id, map_id))

if transaction:
replaced_entries.extend(
_mark_delete_entries(old_rules.values()))
return replaced_entries

def _mark_delete_entries(delete_rule_dicts):
delete_entries = []
for delete_rule_dict in delete_rule_dicts:
delete_entry = self.entry_def.adapt_from_rule_dict(
delete_rule_dict, domain_id, map_id)
delete_entry.set_delete()
delete_entries.append(delete_entry)
return delete_entries

@utils.retry_upon_exception(
exceptions.StaleRevision,
max_attempts=self.policy_api.client.max_attempts)
def _update():
transaction = trans.NsxPolicyTransaction.get_current()
# Get the current data of communication map & its entries
comm_map = self.policy_api.get(map_def)
replaced_entries = None
ignore_entries = (entries == IGNORE)
if not ignore_entries:
replaced_entries = _overwrite_entries(comm_map['rules'],
entries, transaction)
comm_map.pop('rules')
map_def.set_obj_dict(comm_map)
body = map_def.get_obj_dict()
if entries != IGNORE:
body['rules'] = _overwrite_entries(comm_map['rules'], entries)
# Update the entire map at the NSX
self.policy_api.client.update(map_path, body)
if transaction:
self._create_or_store(map_def, replaced_entries)
else:
body = map_def.get_obj_dict()
if not ignore_entries:
body['rules'] = [rule.get_obj_dict() for rule in
replaced_entries]
self.policy_api.client.update(map_path, body)

_update()

@@ -3926,3 +3982,46 @@ class NsxPolicyExcludeListApi(NsxPolicyResourceBase):
raise exceptions.ManagerError(details=err_msg)

# TODO(asarfaty): Add support for add/remove member


class NsxPolicyGlobalConfig(NsxPolicyResourceBase):

@property
def entry_def(self):
return core_defs.GlobalConfigDef

def create_or_overwrite(self, tenant=constants.POLICY_INFRA_TENANT):
err_msg = (_("This action is not supported"))
raise exceptions.ManagerError(details=err_msg)

def delete(self, tenant=constants.POLICY_INFRA_TENANT):
err_msg = (_("This action is not supported"))
raise exceptions.ManagerError(details=err_msg)

def get(self, tenant=constants.POLICY_INFRA_TENANT, silent=False):
global_config_def = self.entry_def(tenant=tenant)
return self.policy_api.get(global_config_def, silent=silent)

def list(self, tenant=constants.POLICY_INFRA_TENANT):
err_msg = (_("This action is not supported"))
raise exceptions.ManagerError(details=err_msg)

def update(self, members=IGNORE,
tenant=constants.POLICY_INFRA_TENANT):
err_msg = (_("This action is not supported"))
raise exceptions.ManagerError(details=err_msg)

def _set_l3_forwarding_mode(self, mode, tenant):
# Using PUT as PATCH is not supported for this API
config = self.get()
if config['l3_forwarding_mode'] != mode:
config['l3_forwarding_mode'] = mode
config_def = self.entry_def(tenant=tenant)
path = config_def.get_resource_path()
self.policy_api.client.update(path, config)

def enable_ipv6(self, tenant=constants.POLICY_INFRA_TENANT):
return self._set_l3_forwarding_mode('IPV4_AND_IPV6', tenant)

def disable_ipv6(self, tenant=constants.POLICY_INFRA_TENANT):
return self._set_l3_forwarding_mode('IPV4_ONLY', tenant)

+ 22
- 0
vmware_nsxlib/v3/policy/lb_defs.py View File

@@ -14,9 +14,15 @@
# under the License.
#

from distutils import version

from oslo_log import log as logging
from vmware_nsxlib.v3 import nsx_constants
from vmware_nsxlib.v3.policy import constants
from vmware_nsxlib.v3.policy.core_defs import ResourceDef

LOG = logging.getLogger(__name__)

TENANTS_PATH_PATTERN = "%s/"
LB_VIRTUAL_SERVERS_PATH_PATTERN = TENANTS_PATH_PATTERN + "lb-virtual-servers/"
LB_SERVICES_PATH_PATTERN = TENANTS_PATH_PATTERN + "lb-services/"
@@ -379,8 +385,24 @@ class LBServiceDef(ResourceDef):
def get_obj_dict(self):
body = super(LBServiceDef, self).get_obj_dict()
self._set_attrs_if_specified(body, ['size', 'connectivity_path'])
self._set_attrs_if_supported(body, ['relax_scale_validation'])
return body

def _version_dependant_attr_supported(self, attr):
if (version.LooseVersion(self.nsx_version) >=
version.LooseVersion(nsx_constants.NSX_VERSION_3_0_0)):
if attr == 'relax_scale_validation':
return True
else:
LOG.warning(
"Ignoring %s for %s %s: this feature is not supported."
"Current NSX version: %s. Minimum supported version: %s",
attr, self.resource_type, self.attrs.get('name', ''),
self.nsx_version, nsx_constants.NSX_VERSION_3_0_0)
return False

return False


class LBServiceStatisticsDef(ResourceDef):


+ 32
- 7
vmware_nsxlib/v3/policy/lb_resources.py View File

@@ -558,6 +558,7 @@ class NsxPolicyLoadBalancerPoolApi(NsxPolicyResourceBase):

class NsxPolicyLoadBalancerServiceApi(NsxPolicyResourceBase):
"""NSX Policy LBService."""

@property
def entry_def(self):
return lb_defs.LBServiceDef
@@ -567,6 +568,7 @@ class NsxPolicyLoadBalancerServiceApi(NsxPolicyResourceBase):
tags=IGNORE,
size=IGNORE,
connectivity_path=IGNORE,
relax_scale_validation=IGNORE,
tenant=constants.POLICY_INFRA_TENANT):
lb_service_id = self._init_obj_uuid(lb_service_id)
lb_service_def = self._init_def(
@@ -576,6 +578,7 @@ class NsxPolicyLoadBalancerServiceApi(NsxPolicyResourceBase):
tags=tags,
size=size,
connectivity_path=connectivity_path,
relax_scale_validation=relax_scale_validation,
tenant=tenant)

self._create_or_store(lb_service_def)
@@ -599,14 +602,18 @@ class NsxPolicyLoadBalancerServiceApi(NsxPolicyResourceBase):
def update(self, lb_service_id, name=IGNORE,
description=IGNORE, tags=IGNORE,
size=IGNORE, connectivity_path=IGNORE,
relax_scale_validation=IGNORE,
tenant=constants.POLICY_INFRA_TENANT):
self._update(lb_service_id=lb_service_id,
name=name,
description=description,
tags=tags,
size=size,
connectivity_path=connectivity_path,
tenant=tenant)

self._update(
lb_service_id=lb_service_id,
name=name,
description=description,
tags=tags,
size=size,
connectivity_path=connectivity_path,
relax_scale_validation=relax_scale_validation,
tenant=tenant)

def get_statistics(self, lb_service_id,
tenant=constants.POLICY_INFRA_TENANT):
@@ -646,6 +653,15 @@ class NsxPolicyLoadBalancerServiceApi(NsxPolicyResourceBase):
tenant=tenant)
return profile_def.get_resource_full_path()

def wait_until_realized(self, lb_service_id, entity_type='LbServiceDto',
tenant=constants.POLICY_INFRA_TENANT,
sleep=None, max_attempts=None):
lb_service_def = self.entry_def(
lb_service_id=lb_service_id, tenant=tenant)
return self._wait_until_realized(
lb_service_def, entity_type=entity_type,
sleep=sleep, max_attempts=max_attempts)


class NsxPolicyLoadBalancerVirtualServerAPI(NsxPolicyResourceBase):
"""NSX Policy LoadBalancerVirtualServers"""
@@ -884,6 +900,15 @@ class NsxPolicyLoadBalancerVirtualServerAPI(NsxPolicyResourceBase):
tenant=tenant)
return profile_def.get_resource_full_path()

def wait_until_realized(self, virtual_server_id, entity_type=None,
tenant=constants.POLICY_INFRA_TENANT,
sleep=None, max_attempts=None):
lbvs_def = self.entry_def(
virtual_server_id=virtual_server_id, tenant=tenant)
return self._wait_until_realized(
lbvs_def, entity_type=entity_type,
sleep=sleep, max_attempts=max_attempts)


@six.add_metaclass(abc.ABCMeta)
class NsxPolicyLBMonitorProfileBase(NsxPolicyResourceBase):

+ 13
- 6
vmware_nsxlib/v3/policy/transaction.py View File

@@ -67,7 +67,10 @@ class NsxPolicyTransaction(object):

self.client = client
# TODO(annak): raise exception for different tenants
self.defs.append(resource_def)
if isinstance(resource_def, list):
self.defs.extend(resource_def)
else:
self.defs.append(resource_def)

def _sort_defs(self):
sorted_defs = []
@@ -96,9 +99,12 @@ class NsxPolicyTransaction(object):

self.defs = sorted_defs

def _build_wrapper_dict(self, resource_class, node):
return {'resource_type': 'Child%s' % resource_class,
resource_class: node}
def _build_wrapper_dict(self, resource_class, node, delete=False):
wrapper_dict = {'resource_type': 'Child%s' % resource_class,
resource_class: node}
if delete:
wrapper_dict.update({'marked_for_delete': True})
return wrapper_dict

def _find_parent_in_dict(self, d, resource_def, level=1):

@@ -179,8 +185,9 @@ class NsxPolicyTransaction(object):
child_dict_key = child_def.get_last_section_dict_key
node[child_dict_key] = [child_def.get_obj_dict()]
parent_dict['children'].append(
self._build_wrapper_dict(resource_class, node))

self._build_wrapper_dict(resource_class,
node,
resource_def.get_delete()))
if body:
headers = {'nsx-enable-partial-patch': 'true'}
self.client.patch(url, body, headers=headers)

Loading…
Cancel
Save