From 90e97c4390b4741cedc1ff32c29ebbda0d28ff55 Mon Sep 17 00:00:00 2001 From: Adit Sarfaty Date: Wed, 28 Nov 2018 14:53:02 +0200 Subject: [PATCH] Policy pass-through api support Adding basic support and config flag for the passthrough api. This will use the nsx manager api for handling policy resources fine tuning when there is no policy apis available. Change-Id: I29cbbd570b8e3eea4f52fb13d01820fddd7b1cff --- .../tests/unit/v3/nsxlib_testcase.py | 3 +- .../tests/unit/v3/test_policy_resources.py | 58 +++++++++++- .../tests/unit/v3/test_policy_transaction.py | 8 +- vmware_nsxlib/v3/__init__.py | 90 +++++++++++-------- vmware_nsxlib/v3/config.py | 8 +- vmware_nsxlib/v3/policy_resources.py | 37 +++++++- 6 files changed, 160 insertions(+), 44 deletions(-) diff --git a/vmware_nsxlib/tests/unit/v3/nsxlib_testcase.py b/vmware_nsxlib/tests/unit/v3/nsxlib_testcase.py index dbd16954..a6eec9a6 100644 --- a/vmware_nsxlib/tests/unit/v3/nsxlib_testcase.py +++ b/vmware_nsxlib/tests/unit/v3/nsxlib_testcase.py @@ -126,7 +126,8 @@ def get_default_nsxlib_config(): plugin_tag=PLUGIN_TAG, plugin_ver=PLUGIN_VER, dns_nameservers=DNS_NAMESERVERS, - dns_domain=DNS_DOMAIN + dns_domain=DNS_DOMAIN, + allow_passthrough=True ) diff --git a/vmware_nsxlib/tests/unit/v3/test_policy_resources.py b/vmware_nsxlib/tests/unit/v3/test_policy_resources.py index f4fdc294..c27bdd93 100644 --- a/vmware_nsxlib/tests/unit/v3/test_policy_resources.py +++ b/vmware_nsxlib/tests/unit/v3/test_policy_resources.py @@ -33,7 +33,9 @@ class NsxPolicyLibTestCase(policy_testcase.TestPolicyApi): super(NsxPolicyLibTestCase, self).setUp() nsxlib_config = nsxlib_testcase.get_default_nsxlib_config() - self.policy_lib = v3.NsxPolicyLib(nsxlib_config) + # Mock the nsx-lib for the passthrough api + with mock.patch('vmware_nsxlib.v3.NsxLib'): + self.policy_lib = v3.NsxPolicyLib(nsxlib_config) self.policy_api = self.policy_lib.policy_api self.policy_api.client = self.client @@ -1864,6 +1866,34 @@ class TestPolicyTier1(NsxPolicyLibTestCase): tier1_id, tenant=TEST_TENANT) self.assertEqual(info, actual_info) + def test_update_transport_zone(self): + # Test the passthrough api + tier1_id = '111' + logical_router_id = 'realized_111' + tz_uuid = 'dummy_tz' + info = {'state': policy_constants.STATE_REALIZED, + 'entity_type': 'RealizedLogicalRouter', + 'realization_specific_identifier': logical_router_id} + passthrough_mock = self.resourceApi.nsx_api.logical_router.update + with mock.patch.object(self.resourceApi, "_get_realization_info", + return_value=info) as realization: + self.resourceApi.update_transport_zone(tier1_id, tz_uuid, + tenant=TEST_TENANT) + realization.assert_called_once() + passthrough_mock.assert_called_once_with( + logical_router_id, transport_zone_id=tz_uuid) + + def test_wait_until_realized(self): + tier1_id = '111' + logical_router_id = 'realized_111' + info = {'state': policy_constants.STATE_UNREALIZED, + 'realization_specific_identifier': logical_router_id} + with mock.patch.object(self.resourceApi, "_get_realization_info", + return_value=info): + self.assertRaises(nsxlib_exc.ManagerError, + self.resourceApi.wait_until_realized, + tier1_id, tenant=TEST_TENANT) + class TestPolicyTier1NatRule(NsxPolicyLibTestCase): @@ -2001,6 +2031,32 @@ class TestPolicyTier0(NsxPolicyLibTestCase): self.assert_called_with_def( update_call, expected_def) + def test_get_overlay_transport_zone(self): + # Test the passthrough api + tier0_id = '111' + logical_router_id = 'realized_111' + info = {'state': policy_constants.STATE_REALIZED, + 'entity_type': 'RealizedLogicalRouter', + 'realization_specific_identifier': logical_router_id} + pt_mock = self.resourceApi.nsx_api.router.get_tier0_router_overlay_tz + with mock.patch.object(self.resourceApi, "_get_realization_info", + return_value=info) as realization: + self.resourceApi.get_overlay_transport_zone( + tier0_id, tenant=TEST_TENANT) + realization.assert_called_once() + pt_mock.assert_called_once_with(logical_router_id) + + def test_wait_until_realized(self): + tier1_id = '111' + logical_router_id = 'realized_111' + info = {'state': policy_constants.STATE_UNREALIZED, + 'realization_specific_identifier': logical_router_id} + with mock.patch.object(self.resourceApi, "_get_realization_info", + return_value=info): + self.assertRaises(nsxlib_exc.ManagerError, + self.resourceApi.wait_until_realized, + tier1_id, tenant=TEST_TENANT) + class TestPolicySegmentProfileBase(NsxPolicyLibTestCase): diff --git a/vmware_nsxlib/tests/unit/v3/test_policy_transaction.py b/vmware_nsxlib/tests/unit/v3/test_policy_transaction.py index 2b3f18a3..0935705f 100644 --- a/vmware_nsxlib/tests/unit/v3/test_policy_transaction.py +++ b/vmware_nsxlib/tests/unit/v3/test_policy_transaction.py @@ -14,11 +14,11 @@ # under the License. # -from vmware_nsxlib import v3 +import mock from vmware_nsxlib.tests.unit.v3 import nsxlib_testcase from vmware_nsxlib.tests.unit.v3 import policy_testcase - +from vmware_nsxlib import v3 from vmware_nsxlib.v3 import policy_transaction as trans @@ -29,7 +29,9 @@ class TestPolicyTransaction(policy_testcase.TestPolicyApi): super(TestPolicyTransaction, self).setUp() nsxlib_config = nsxlib_testcase.get_default_nsxlib_config() - self.policy_lib = v3.NsxPolicyLib(nsxlib_config) + # Mock the nsx-lib for the passthrough api + with mock.patch('vmware_nsxlib.v3.NsxLib'): + self.policy_lib = v3.NsxPolicyLib(nsxlib_config) self.policy_api = self.policy_lib.policy_api self.policy_api.client = self.client diff --git a/vmware_nsxlib/v3/__init__.py b/vmware_nsxlib/v3/__init__.py index 81cf3456..81077130 100644 --- a/vmware_nsxlib/v3/__init__.py +++ b/vmware_nsxlib/v3/__init__.py @@ -14,6 +14,7 @@ # under the License. import abc +import copy from distutils import version from oslo_log import log @@ -59,12 +60,11 @@ class NsxLibBase(object): self.general_apis = utils.NsxLibApiBase( self.client, self.nsxlib_config) + self.nsx_version = None self.init_api() super(NsxLibBase, self).__init__() - self.nsx_version = None - def set_config(self, nsxlib_config): """Set config user provided and extend it according to application""" self.nsxlib_config = nsxlib_config @@ -380,46 +380,59 @@ class NsxLib(NsxLibBase): class NsxPolicyLib(NsxLibBase): def init_api(self): + # Initialize the policy client self.policy_api = policy_defs.NsxPolicyApi(self.client) - self.domain = policy_resources.NsxPolicyDomainApi(self.policy_api) - self.group = policy_resources.NsxPolicyGroupApi(self.policy_api) - self.service = policy_resources.NsxPolicyL4ServiceApi(self.policy_api) + + # NSX manager api will be used as a pass-through for apis which are + # not implemented by the policy manager yet + if self.nsxlib_config.allow_passthrough: + config = copy.deepcopy(self.nsxlib_config) + # X-Allow-Overwrite must be set for passthrough apis + config.allow_overwrite_header = True + self.nsx_api = NsxLib(config) + else: + self.nsx_api = None + self.nsx_version = self.get_version() + args = (self.policy_api, self.nsx_api, self.nsx_version) + + # Initialize all the different resources + self.domain = policy_resources.NsxPolicyDomainApi(*args) + self.group = policy_resources.NsxPolicyGroupApi(*args) + self.service = policy_resources.NsxPolicyL4ServiceApi(*args) self.icmp_service = policy_resources.NsxPolicyIcmpServiceApi( - self.policy_api) + *args) self.ip_protocol_service = ( - policy_resources.NsxPolicyIPProtocolServiceApi( - self.policy_api)) - self.tier0 = policy_resources.NsxPolicyTier0Api(self.policy_api) - self.tier1 = policy_resources.NsxPolicyTier1Api(self.policy_api) - self.tier1_segment = policy_resources.NsxPolicyTier1SegmentApi( - self.policy_api) + policy_resources.NsxPolicyIPProtocolServiceApi(*args)) + self.tier0 = policy_resources.NsxPolicyTier0Api(*args) + self.tier1 = policy_resources.NsxPolicyTier1Api(*args) + self.tier1_segment = policy_resources.NsxPolicyTier1SegmentApi(*args) self.tier1_nat_rule = policy_resources.NsxPolicyTier1NatRuleApi( - self.policy_api) - self.segment = policy_resources.NsxPolicySegmentApi(self.policy_api) + *args) + self.segment = policy_resources.NsxPolicySegmentApi(*args) self.segment_port = policy_resources.NsxPolicySegmentPortApi( - self.policy_api) + *args) self.tier1_segment_port = ( - policy_resources.NsxPolicyTier1SegmentPortApi(self.policy_api)) + policy_resources.NsxPolicyTier1SegmentPortApi(*args)) self.comm_map = policy_resources.NsxPolicyCommunicationMapApi( - self.policy_api) + *args) self.enforcement_point = policy_resources.NsxPolicyEnforcementPointApi( - self.policy_api) + *args) self.transport_zone = policy_resources.NsxPolicyTransportZoneApi( - self.policy_api) + *args) self.deployment_map = policy_resources.NsxPolicyDeploymentMapApi( - self.policy_api) - self.ip_block = policy_resources.NsxPolicyIpBlockApi(self.policy_api) - self.ip_pool = policy_resources.NsxPolicyIpPoolApi(self.policy_api) + *args) + self.ip_block = policy_resources.NsxPolicyIpBlockApi(*args) + self.ip_pool = policy_resources.NsxPolicyIpPoolApi(*args) self.segment_security_profile = ( - policy_resources.NsxSegmentSecurityProfileApi(self.policy_api)) + policy_resources.NsxSegmentSecurityProfileApi(*args)) self.qos_profile = ( - policy_resources.NsxQosProfileApi(self.policy_api)) + policy_resources.NsxQosProfileApi(*args)) self.spoofguard_profile = ( - policy_resources.NsxSpoofguardProfileApi(self.policy_api)) + policy_resources.NsxSpoofguardProfileApi(*args)) self.ip_discovery_profile = ( - policy_resources.NsxIpDiscoveryProfileApi(self.policy_api)) + policy_resources.NsxIpDiscoveryProfileApi(*args)) self.mac_discovery_profile = ( - policy_resources.NsxMacDiscoveryProfileApi(self.policy_api)) + policy_resources.NsxMacDiscoveryProfileApi(*args)) @property def keepalive_section(self): @@ -429,20 +442,16 @@ class NsxPolicyLib(NsxLibBase): """Get the NSX Policy manager version Currently the backend does not support it, so the nsx-manager api - will be used temporarily. + will be used temporarily as a passthrough. """ if self.nsx_version: return self.nsx_version - manager_client = client.NSX3Client( - self.cluster, - nsx_api_managers=self.nsxlib_config.nsx_api_managers, - max_attempts=self.nsxlib_config.max_attempts, - url_path_base=client.NSX3Client.NSX_V1_API_PREFIX, - rate_limit_retry=self.nsxlib_config.rate_limit_retry) - - node = manager_client.get('node') - self.nsx_version = node.get('node_version') + if self.nsx_api: + self.nsx_version = self.nsx_api.get_version() + else: + # return the initial supported version + self.nsx_version = nsx_constants.NSX_VERSION_2_4_0 return self.nsx_version def feature_supported(self, feature): @@ -454,6 +463,13 @@ class NsxPolicyLib(NsxLibBase): return (feature == nsx_constants.FEATURE_NSX_POLICY) + def reinitialize_cluster(self, resource, event, trigger, payload=None): + super(NsxPolicyLib, self).reinitialize_cluster( + resource, event, trigger, payload=payload) + if self.nsx_api: + self.nsx_api.reinitialize_cluster(resource, event, trigger, + payload) + @property def client_url_prefix(self): return client.NSX3Client.NSX_POLICY_V1_API_PREFIX diff --git a/vmware_nsxlib/v3/config.py b/vmware_nsxlib/v3/config.py index 95812ac5..2d1100a3 100644 --- a/vmware_nsxlib/v3/config.py +++ b/vmware_nsxlib/v3/config.py @@ -78,6 +78,10 @@ class NsxLibConfig(object): endpoint in the NSX management cluster is available to serve a request, and retry the request instead. + + -- Additional parameters which are relevant only for the Policy manager: + :param allow_passthrough: If True, use nsx manager api for cases which are + not supported by the policy manager api. """ def __init__(self, @@ -102,7 +106,8 @@ class NsxLibConfig(object): dhcp_profile_uuid=None, allow_overwrite_header=False, rate_limit_retry=True, - cluster_unavailable_retry=False): + cluster_unavailable_retry=False, + allow_passthrough=False): self.nsx_api_managers = nsx_api_managers self._username = username @@ -125,6 +130,7 @@ class NsxLibConfig(object): self.allow_overwrite_header = allow_overwrite_header self.rate_limit_retry = rate_limit_retry self.cluster_unavailable_retry = cluster_unavailable_retry + self.allow_passthrough = allow_passthrough if dhcp_profile_uuid: # this is deprecated, and never used. diff --git a/vmware_nsxlib/v3/policy_resources.py b/vmware_nsxlib/v3/policy_resources.py index 873c8efb..8a48ea11 100644 --- a/vmware_nsxlib/v3/policy_resources.py +++ b/vmware_nsxlib/v3/policy_resources.py @@ -47,8 +47,10 @@ class NsxPolicyResourceBase(object): """ SINGLE_ENTRY_ID = 'entry' - def __init__(self, policy_api): + def __init__(self, policy_api, nsx_api, version): self.policy_api = policy_api + self.nsx_api = nsx_api + self.version = version @property def entry_def(self): @@ -761,6 +763,23 @@ class NsxPolicyTier1Api(NsxPolicyResourceBase): tier1_def = self.entry_def(tier1_id=tier1_id, tenant=tenant) return self._wait_until_realized(tier1_def, entity_type=entity_type) + def update_transport_zone(self, tier1_id, transport_zone_id, + tenant=policy_constants.POLICY_INFRA_TENANT): + """Use the pass-through api to update the TZ zone on the NSX router""" + if not self.nsx_api: + LOG.error("Cannot update tier1 %s transport zone as the " + "passthrough api is forbidden", tier1_id) + return + + realization_info = self.wait_until_realized( + tier1_id, entity_type='RealizedLogicalRouter', tenant=tenant) + + nsx_router_uuid = self.get_realized_id( + tier1_id, tenant=tenant, realization_info=realization_info) + self.nsx_api.logical_router.update( + nsx_router_uuid, + transport_zone_id=transport_zone_id) + class NsxPolicyTier0Api(NsxPolicyResourceBase): """NSX Tier0 API """ @@ -838,6 +857,22 @@ class NsxPolicyTier0Api(NsxPolicyResourceBase): if 'edge_cluster_path' in srv: return srv['edge_cluster_path'] + def get_overlay_transport_zone( + self, tier0_id, + tenant=policy_constants.POLICY_INFRA_TENANT): + """Use the pass-through api to get the TZ zone of the NSX tier0""" + if not self.nsx_api: + LOG.error("Cannot get tier0 %s transport zone as the " + "passthrough api is forbidden", tier0_id) + return + realization_info = self.wait_until_realized( + tier0_id, entity_type='RealizedLogicalRouter', tenant=tenant) + nsx_router_uuid = self.get_realized_id( + tier0_id, tenant=tenant, + realization_info=realization_info) + return self.nsx_api.router.get_tier0_router_overlay_tz( + nsx_router_uuid) + def get_realized_state(self, tier0_id, entity_type=None, tenant=policy_constants.POLICY_INFRA_TENANT, realization_info=None):