diff --git a/gbpservice/neutron/services/grouppolicy/drivers/vmware/nsx_policy/nsx_policy_mapping.py b/gbpservice/neutron/services/grouppolicy/drivers/vmware/nsx_policy/nsx_policy_mapping.py index eb6fa1567..d84a4561d 100644 --- a/gbpservice/neutron/services/grouppolicy/drivers/vmware/nsx_policy/nsx_policy_mapping.py +++ b/gbpservice/neutron/services/grouppolicy/drivers/vmware/nsx_policy/nsx_policy_mapping.py @@ -72,6 +72,11 @@ class UpdateClassifierProtocolNotSupported(gpexc.GroupPolicyBadRequest): "with %s" % DRIVER_NAME) +class UpdateClassifierDirectionNotSupported(gpexc.GroupPolicyBadRequest): + message = ("Update operation on classifier direction is not supported " + "with %s" % DRIVER_NAME) + + class ProtocolNotSupported(gpexc.GroupPolicyBadRequest): message = ("Unsupported classifier protocol. Only icmp, tcp and udp are " "supported with %s" % DRIVER_NAME) @@ -203,19 +208,6 @@ class NsxPolicyMappingDriver(api.ResourceMappingDriver): LOG.warning('Domain %s was not found on backend', project_id) - def _create_or_update_communication_profile(self, profile_id, name, - description, rules, - update_flow=False): - - services = [rule['policy_classifier_id'] - for rule in rules] - - self.nsx_policy.comm_profile.create_or_overwrite( - name=generate_nsx_name(profile_id, name), - profile_id=profile_id, - description=description, - services=services) - def _split_rules_by_direction(self, context, rules): in_dir = [g_const.GP_DIRECTION_BI, g_const.GP_DIRECTION_IN] out_dir = [g_const.GP_DIRECTION_BI, g_const.GP_DIRECTION_OUT] @@ -236,44 +228,23 @@ class NsxPolicyMappingDriver(api.ResourceMappingDriver): return in_rules, out_rules - def _delete_comm_profile(self, comm_profile_id): - try: - self.nsx_policy.comm_profile.delete(comm_profile_id) - except nsxlib_exc.ManagerError: - LOG.error('Communication profile %s not found on backend', - comm_profile_id) - - def _create_or_update_policy_rule_set(self, context, update_flow=False): - - rule_set_id = context.current['id'] + def _get_services_from_rule_set(self, context, rule_set_id): + ruleset = self.gbp_plugin.get_policy_rule_set( + context._plugin_context, rule_set_id) rules = self.gbp_plugin.get_policy_rules( context._plugin_context, - {'id': context.current['policy_rules']}) + {'id': ruleset['policy_rules']}) in_rules, out_rules = self._split_rules_by_direction(context, rules) + in_services = set() + for rule in in_rules: + in_services.add(rule['policy_classifier_id']) + out_services = set() + for rule in out_rules: + out_services.add(rule['policy_classifier_id']) - if in_rules: - self._create_or_update_communication_profile( - append_in_dir(rule_set_id), - generate_nsx_name(rule_set_id, - context.current['name'], - '_IN'), - context.current['description'] + '(ingress)', - in_rules) - elif update_flow: - self._delete_comm_profile(append_in_dir(rule_set_id)) - - if out_rules: - self._create_or_update_communication_profile( - append_out_dir(rule_set_id), - generate_nsx_name(rule_set_id, - context.current['name'], - '_OUT'), - context.current['description'] + '(egress)', - out_rules) - elif update_flow: - self._delete_comm_profile(append_out_dir(rule_set_id)) + return sorted(list(in_services)), sorted(list(out_services)) def _filter_ptgs_by_ruleset(self, ptgs, ruleset_id): providing_ptgs = [ptg['id'] for ptg in ptgs @@ -282,8 +253,15 @@ class NsxPolicyMappingDriver(api.ResourceMappingDriver): if ruleset_id in ptg['consumed_policy_rule_sets']] return providing_ptgs, consuming_ptgs - def _map_rule_set(self, ptgs, profiles, project_id, - group_id, ruleset_id, delete_flow): + def _map_rule_set(self, context, ptgs, project_id, + ruleset_id, delete_flow): + + def delete_map_if_exists(ruleset): + try: + self.nsx_policy.comm_map.delete(project_id, ruleset) + except nsxlib_exc.ManagerError: + # TODO(annak) - narrow this exception down + pass providing_ptgs, consuming_ptgs = self._filter_ptgs_by_ruleset( ptgs, ruleset_id) @@ -298,53 +276,54 @@ class NsxPolicyMappingDriver(api.ResourceMappingDriver): # we need to delete map entry if exists for ruleset in (ruleset_in, ruleset_out): - if ruleset in profiles: - try: - self.nsx_policy.comm_map.delete(project_id, ruleset) - except nsxlib_exc.ManagerError: - pass + delete_map_if_exists(ruleset) + return - if ruleset_in in profiles: - self.nsx_policy.comm_map.create_or_overwrite( - name=ruleset_in, - domain_id=project_id, - map_id=ruleset_in, - description="GBP ruleset ingress", - profile_id=ruleset_in, - source_groups=consuming_ptgs, - dest_groups=providing_ptgs) + services_in, services_out = self._get_services_from_rule_set( + context, ruleset_id) - if ruleset_out in profiles: + if services_in: self.nsx_policy.comm_map.create_or_overwrite( - name=ruleset_out, - domain_id=project_id, - map_id=ruleset_out, - description="GBP ruleset egress", - profile_id=ruleset_out, - source_groups=providing_ptgs, - dest_groups=consuming_ptgs) + name=ruleset_in, + domain_id=project_id, + map_id=ruleset_in, + description="GBP ruleset ingress", + service_ids=services_in, + source_groups=consuming_ptgs, + dest_groups=providing_ptgs) + else: + delete_map_if_exists(ruleset_in) - def _map_group_rule_sets(self, context, group_id, + if services_out: + self.nsx_policy.comm_map.create_or_overwrite( + name=ruleset_out, + domain_id=project_id, + map_id=ruleset_out, + description="GBP ruleset egress", + service_ids=services_out, + source_groups=providing_ptgs, + dest_groups=consuming_ptgs) + else: + delete_map_if_exists(ruleset_out) + + def _map_group_rule_sets(self, context, provided_policy_rule_sets, consumed_policy_rule_sets, delete_flow=False): project_id = context.current['project_id'] - profiles = self.nsx_policy.comm_profile.list() - profiles = [p['id'] for p in profiles] - # create communication maps ptgs = context._plugin.get_policy_target_groups( - context._plugin_context) + context._plugin_context) for ruleset in provided_policy_rule_sets: - self._map_rule_set(ptgs, profiles, project_id, - group_id, ruleset, delete_flow) + self._map_rule_set(context, ptgs, project_id, + ruleset, delete_flow) for ruleset in consumed_policy_rule_sets: - self._map_rule_set(ptgs, profiles, project_id, - group_id, ruleset, delete_flow) + self._map_rule_set(context, ptgs, project_id, + ruleset, delete_flow) # overrides base class, called from base group_create_postcommit # REVISIT(annak): Suggest a better design for driver-specific callbacks, @@ -365,10 +344,8 @@ class NsxPolicyMappingDriver(api.ResourceMappingDriver): provided_policy_rule_sets, consumed_policy_rule_sets, op): - group_id = context.current['id'] - self._map_group_rule_sets( - context, group_id, + context, provided_policy_rule_sets, consumed_policy_rule_sets, delete_flow=(op == "DISASSOCIATE")) @@ -402,7 +379,7 @@ class NsxPolicyMappingDriver(api.ResourceMappingDriver): ports = [str(p) for p in range(lower, upper)] # service entry in nsx policy has single direction - # directions will be enforced on communication profile level + # directions will be enforced on communication map level self.nsx_policy.service.create_or_overwrite( name=generate_nsx_name(classifier['id'], classifier['name']), service_id=classifier['id'], @@ -421,7 +398,7 @@ class NsxPolicyMappingDriver(api.ResourceMappingDriver): raise HierarchicalContractsNotSupported() def create_policy_rule_set_postcommit(self, context): - self._create_or_update_policy_rule_set(context) + pass def create_policy_target_precommit(self, context): super(NsxPolicyMappingDriver, @@ -515,17 +492,7 @@ class NsxPolicyMappingDriver(api.ResourceMappingDriver): pass def delete_policy_rule_set_postcommit(self, context): - ruleset_id = context.current['id'] - rules = self.gbp_plugin.get_policy_rules( - context._plugin_context, - {'id': context.current['policy_rules']}) - - in_rules, out_rules = self._split_rules_by_direction(context, rules) - if in_rules: - self._delete_comm_profile(append_in_dir(ruleset_id)) - - if out_rules: - self._delete_comm_profile(append_out_dir(ruleset_id)) + pass def delete_policy_target_postcommit(self, context): # This is inherited behavior without: @@ -541,7 +508,9 @@ class NsxPolicyMappingDriver(api.ResourceMappingDriver): self._reject_shared(context.current, 'policy_rule_set') def update_policy_rule_set_postcommit(self, context): - self._create_or_update_policy_rule_set(context, update_flow=True) + if (context.current['policy_rules'] != + context.original['policy_rules']): + self._on_policy_rule_set_updated(context, context.current) def update_policy_target_precommit(self, context): # Parent call verifies change of PTG is not supported @@ -554,10 +523,27 @@ class NsxPolicyMappingDriver(api.ResourceMappingDriver): self).update_policy_target_postcommit(context) def update_policy_rule_precommit(self, context): - raise UpdateOperationNotSupported() + super(NsxPolicyMappingDriver, + self).update_policy_rule_precommit(context) + + def _on_policy_rule_set_updated(self, context, prs): + ptgs = context._plugin.get_policy_target_groups( + context._plugin_context) + + self._map_rule_set(context, ptgs, prs['project_id'], + prs['id'], False) def update_policy_rule_postcommit(self, context): - pass + if (context.current['policy_classifier_id'] != + context.original['policy_classifier_id']): + # All groups using this rule need to be updated + prs_ids = ( + context._plugin._get_policy_rule_policy_rule_sets( + context._plugin_context, context.current['id'])) + policy_rule_sets = context._plugin.get_policy_rule_sets( + context._plugin_context, filters={'id': prs_ids}) + for prs in policy_rule_sets: + self._on_policy_rule_set_updated(context, prs) def update_policy_action_precommit(self, context): raise UpdateOperationNotSupported() @@ -566,6 +552,10 @@ class NsxPolicyMappingDriver(api.ResourceMappingDriver): if context.current['protocol'] != context.original['protocol']: raise UpdateClassifierProtocolNotSupported() + # TODO(annak): support this + if context.current['direction'] != context.original['direction']: + raise UpdateClassifierDirectionNotSupported() + def update_policy_classifier_postcommit(self, context): self.create_policy_classifier_postcommit(context) diff --git a/gbpservice/neutron/tests/unit/services/grouppolicy/test_nsx_policy_mapping_driver.py b/gbpservice/neutron/tests/unit/services/grouppolicy/test_nsx_policy_mapping_driver.py index 1c883d74c..458ac9b99 100644 --- a/gbpservice/neutron/tests/unit/services/grouppolicy/test_nsx_policy_mapping_driver.py +++ b/gbpservice/neutron/tests/unit/services/grouppolicy/test_nsx_policy_mapping_driver.py @@ -23,11 +23,9 @@ from gbpservice.neutron.services.grouppolicy.drivers.vmware.nsx_policy import ( nsx_policy_mapping as driver) from gbpservice.neutron.tests.unit.services.grouppolicy import ( test_resource_mapping as test_rmd) -import unittest2 TEST_PROJECT = 'test-project' -TEMPORARY_SKIP = 'skipping temporarily while adjusting to next backend version' class NsxPolicyMappingTestCase(test_rmd.ResourceMappingTestCase): @@ -85,30 +83,6 @@ class NsxPolicyMappingTestCase(test_rmd.ResourceMappingTestCase): def _mock_icmp_service_delete(self): return mock.patch.object(self.nsx_policy.icmp_service, 'delete') - def _mock_profile_create(self): - return mock.patch.object(self.nsx_policy.comm_profile, - 'create_or_overwrite') - - def _mock_nth_profile_create_fails(self, n=2): - self.call_count = 1 - - def raise_on_nth_call(**kwargs): - if self.call_count == n: - raise nsxlib_exc.ManagerError - else: - self.call_count += 1 - return mock.patch.object(self.nsx_policy.comm_profile, - 'create_or_overwrite', - side_effect=raise_on_nth_call) - - def _mock_profile_delete(self): - return mock.patch.object(self.nsx_policy.comm_profile, 'delete') - - def _mock_profile_list(self, profile_ids): - return mock.patch.object(self.nsx_policy.comm_profile, 'list', - return_value=[{'id': p} - for p in profile_ids]) - def _mock_group_create(self): return mock.patch.object(self.nsx_policy.group, 'create_or_overwrite') @@ -162,7 +136,6 @@ class NsxPolicyMappingTestCase(test_rmd.ResourceMappingTestCase): class TestPolicyClassifier(NsxPolicyMappingTestCase): - @unittest2.skip(TEMPORARY_SKIP) def test_l4_lifecycle(self): with self._mock_service_create() as service_create_call, \ self._mock_service_delete() as service_delete_call: @@ -187,8 +160,7 @@ class TestPolicyClassifier(NsxPolicyMappingTestCase): # classifier update cl = self.update_policy_classifier( cl['id'], - port_range='443', - direction='in')['policy_classifier'] + port_range='443')['policy_classifier'] service_create_call.assert_called_with( name=mock.ANY, @@ -202,7 +174,6 @@ class TestPolicyClassifier(NsxPolicyMappingTestCase): service_delete_call.assert_called_with(cl['id']) - @unittest2.skip(TEMPORARY_SKIP) def test_create_port_range(self): with self._mock_service_create() as service_create_call: @@ -219,7 +190,6 @@ class TestPolicyClassifier(NsxPolicyMappingTestCase): dest_ports=port_list, service_id=mock.ANY) - @unittest2.skip(TEMPORARY_SKIP) def test_create_without_ports(self): with self._mock_service_create() as service_create_call: @@ -234,7 +204,6 @@ class TestPolicyClassifier(NsxPolicyMappingTestCase): dest_ports=[], service_id=mock.ANY) - @unittest2.skip(TEMPORARY_SKIP) def test_icmp_lifecycle(self): with self._mock_icmp_service_create() as service_create_call, \ self._mock_icmp_service_delete() as service_delete_call: @@ -251,7 +220,6 @@ class TestPolicyClassifier(NsxPolicyMappingTestCase): service_delete_call.assert_called_with(cl['id']) - @unittest2.skip(TEMPORARY_SKIP) def test_update_protocol_fails(self): with self._mock_icmp_service_create(): @@ -266,7 +234,6 @@ class TestPolicyClassifier(NsxPolicyMappingTestCase): protocol='tcp', dest_ports=['80']) - @unittest2.skip(TEMPORARY_SKIP) def test_icmpv6_protocol_fails(self): self.assertRaises(webob.exc.HTTPClientError, self.create_policy_classifier, @@ -277,13 +244,18 @@ class TestPolicyClassifier(NsxPolicyMappingTestCase): class TestPolicyTargetGroup(NsxPolicyMappingTestCase): - def _prepare_rule_set(self, name='test'): - with self._mock_service_create(),\ - self._mock_profile_create(): + def _prepare_rule_set(self, name='test_rule', + direction='bi', rule_count=1): + self.rules = [] + with self._mock_service_create(): - rule = self._create_simple_policy_rule() - return self.create_policy_rule_set( - name=name, policy_rules=[rule['id']])['policy_rule_set'] + for i in range(rule_count): + self.rules.append( + self._create_simple_policy_rule(direction=direction)) + + rule_ids = [rule['id'] for rule in self.rules] + return self.create_policy_rule_set(name=name, + policy_rules=rule_ids)['policy_rule_set'] def assert_neutron_resources(self, net_count, subnet_count, port_count): networks = self._plugin.get_networks(self._context) @@ -305,25 +277,27 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): cond_val=group_id, group_id=group_id) - def ingress_map_call(self, prs_id, provider_ids, consumer_ids): + def ingress_map_call(self, prs_id, provider_ids, + consumer_ids, service_ids): return call(domain_id=TEST_PROJECT, - profile_id=driver.append_in_dir(prs_id), map_id=mock.ANY, name=driver.append_in_dir(prs_id), + service_ids=service_ids, description=mock.ANY, source_groups=consumer_ids, - dest_groups=provider_ids) + dest_groups=provider_ids, + ) - def egress_map_call(self, prs_id, provider_ids, consumer_ids): + def egress_map_call(self, prs_id, provider_ids, + consumer_ids, service_ids): return call(domain_id=TEST_PROJECT, - profile_id=driver.append_out_dir(prs_id), map_id=mock.ANY, + service_ids=service_ids, name=driver.append_out_dir(prs_id), description=mock.ANY, source_groups=provider_ids, dest_groups=consumer_ids) - @unittest2.skip(TEMPORARY_SKIP) def test_create_first_ptg_for_project(self): '''Create first ptg for tenant and verify domain creation''' @@ -340,27 +314,27 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): group_create.assert_has_calls([self.group_call('test', ptg['id'])]) map_create.assert_not_called() - def _test_ptg_pair_with_single_rule(self, - direction_in=True, - direction_out=True): + def _test_ptg_pair_with_rule_set(self, + direction_in=True, + direction_out=True, + rule_count=1): '''Test consumer and producer group pair with single rule lifecycle. Verify backend group and rule creation calls. Verify spawned neutron resources. ''' - policy_rule_set = self._prepare_rule_set() - profile_in = driver.append_in_dir(policy_rule_set['id']) - profile_out = driver.append_out_dir(policy_rule_set['id']) - profile_ids = [] - if direction_in: - profile_ids.append(profile_in) - if direction_out: - profile_ids.append(profile_out) + direction = 'bi' + if not direction_in: + direction = 'out' + if not direction_out: + direction = 'in' + + policy_rule_set = self._prepare_rule_set(direction=direction, + rule_count=rule_count) # Create group pair with self._mock_group_create() as group_create,\ - self._mock_profile_list(profile_ids),\ self._mock_map_create() as map_create,\ self._mock_domain_create(): @@ -374,14 +348,20 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): # validate communication map creation on backend calls = [] + classifier_ids = sorted([rule['policy_classifier_id'] + for rule in self.rules]) if direction_in: - calls.append(self.ingress_map_call(policy_rule_set['id'], - [provider_ptg], - [consumer_ptg])) + calls.append(self.ingress_map_call( + policy_rule_set['id'], + [provider_ptg], + [consumer_ptg], + classifier_ids)) if direction_out: - calls.append(self.egress_map_call(policy_rule_set['id'], - [provider_ptg], - [consumer_ptg])) + calls.append(self.egress_map_call( + policy_rule_set['id'], + [provider_ptg], + [consumer_ptg], + classifier_ids)) map_create.assert_has_calls(calls) # validate neutron resources @@ -389,7 +369,6 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): # Delete producer with self._mock_map_delete() as map_delete,\ - self._mock_profile_list(profile_ids),\ self._mock_group_delete() as group_delete,\ self._mock_domain_delete() as domain_delete: @@ -416,7 +395,6 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): # Delete consumer with self._mock_map_delete() as map_delete,\ - self._mock_profile_list(profile_ids),\ self._mock_group_delete() as group_delete,\ self._mock_domain_delete() as domain_delete: @@ -431,19 +409,24 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): # last group is deleted, domain should go as well domain_delete.assert_called_with(TEST_PROJECT) - @unittest2.skip(TEMPORARY_SKIP) - def test_create_ptg_pair_with_single_rule_in(self): - self._test_ptg_pair_with_single_rule(True, False) + def test_ptg_pair_with_single_rule_in(self): + self._test_ptg_pair_with_rule_set(True, False) - @unittest2.skip(TEMPORARY_SKIP) - def test_create_ptg_pair_with_single_rule_out(self): - self._test_ptg_pair_with_single_rule(False, True) + def test_ptg_pair_with_single_rule_out(self): + self._test_ptg_pair_with_rule_set(False, True) - @unittest2.skip(TEMPORARY_SKIP) - def test_create_ptg_pair_with_single_rule_bi(self): - self._test_ptg_pair_with_single_rule(True, True) + def test_ptg_pair_with_single_rule_bi(self): + self._test_ptg_pair_with_rule_set(True, True) + + def test_ptg_pair_with_multi_rule_in(self): + self._test_ptg_pair_with_rule_set(True, False, 2) + + def test_ptg_pair_with_multi_rule_out(self): + self._test_ptg_pair_with_rule_set(False, True, 3) + + def test_ptg_pair_with_multi_rule_bi(self): + self._test_ptg_pair_with_rule_set(True, True, 5) - @unittest2.skip(TEMPORARY_SKIP) def test_create_fail_isolated(self): '''Verify integrity when backend fails on isolated group creation. @@ -470,7 +453,6 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): self.assert_neutron_rollback() - @unittest2.skip(TEMPORARY_SKIP) def test_create_fail_connected(self): '''Verify integrity when backend fails on connectivity map creation @@ -481,11 +463,8 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): ''' policy_rule_set = self._prepare_rule_set() - profile_ids = [driver.append_in_dir(policy_rule_set['id']), - driver.append_out_dir(policy_rule_set['id'])] with self._mock_group_create(),\ - self._mock_profile_list(profile_ids),\ self._mock_map_create_fails(),\ self._mock_group_delete() as group_delete: @@ -497,7 +476,6 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): self.assert_neutron_resources(1, 1, 1) - @unittest2.skip(TEMPORARY_SKIP) def test_create_fail_multi_connected(self): '''Verify integrity when backend fails on connectivity map creation @@ -509,15 +487,14 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): ''' prs1 = self._prepare_rule_set()['id'] + service1 = self.rules[0]['policy_classifier_id'] prs2 = self._prepare_rule_set()['id'] + service2 = self.rules[0]['policy_classifier_id'] prs3 = self._prepare_rule_set()['id'] - profile_ids = [driver.append_in_dir(prs1), driver.append_out_dir(prs1), - driver.append_in_dir(prs2), driver.append_out_dir(prs2), - driver.append_in_dir(prs3), driver.append_out_dir(prs3)] + service3 = self.rules[0]['policy_classifier_id'] # Create a and c with self._mock_group_create(),\ - self._mock_profile_list(profile_ids),\ self._mock_map_create(): ab_dict = {prs1: None} @@ -530,7 +507,6 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): consumed_policy_rule_sets=bc_dict)['policy_target_group']['id'] with self._mock_group_create(),\ - self._mock_profile_list(profile_ids),\ self._mock_nth_map_create_fails(n=6) as map_create,\ self._mock_map_delete() as map_delete,\ self._mock_group_delete() as group_delete: @@ -542,12 +518,13 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): provided_policy_rule_sets=bc_dict) b = mock.ANY - map_create_calls = [self.ingress_map_call(prs1, [a], [b]), - self.egress_map_call(prs1, [a], [b]), - self.ingress_map_call(prs2, [b], [c]), - self.egress_map_call(prs2, [b], [c]), - self.ingress_map_call(prs3, [b], [c]), - self.egress_map_call(prs3, [b], [c])] + map_create_calls = [ + self.ingress_map_call(prs1, [a], [b], [service1]), + self.egress_map_call(prs1, [a], [b], [service1]), + self.ingress_map_call(prs2, [b], [c], [service2]), + self.egress_map_call(prs2, [b], [c], [service2]), + self.ingress_map_call(prs3, [b], [c], [service3]), + self.egress_map_call(prs3, [b], [c], [service3])] map_create.assert_has_calls(map_create_calls, any_order=True) @@ -564,7 +541,6 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): self.assert_neutron_resources(2, 2, 2) - @unittest2.skip(TEMPORARY_SKIP) def test_create_ptg_pair_multi_rule_set(self): '''Create ptg pair based on 3 rule sets @@ -572,17 +548,14 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): second - only egress connectivity, and third - both ''' prs1 = self._prepare_rule_set()['id'] + service1 = self.rules[0]['policy_classifier_id'] prs2 = self._prepare_rule_set()['id'] + service2 = self.rules[0]['policy_classifier_id'] prs3 = self._prepare_rule_set()['id'] - - profile_ids = [driver.append_in_dir(prs1), - driver.append_out_dir(prs2), - driver.append_in_dir(prs3), - driver.append_out_dir(prs3)] + service3 = self.rules[0]['policy_classifier_id'] with self._mock_domain_create(),\ self._mock_group_create() as group_create,\ - self._mock_profile_list(profile_ids),\ self._mock_map_create() as map_create: rule_set_dict = {prs1: None, prs2: None, prs3: None} @@ -598,25 +571,27 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): self.group_call('ptg2', consumer_id)]) map_calls = [ - self.ingress_map_call(prs1, [provider_id], [consumer_id]), - self.egress_map_call(prs2, [provider_id], [consumer_id]), - self.ingress_map_call(prs3, [provider_id], [consumer_id]), - self.egress_map_call(prs3, [provider_id], [consumer_id])] + self.ingress_map_call(prs1, [provider_id], + [consumer_id], [service1]), + self.egress_map_call(prs2, [provider_id], + [consumer_id], [service2]), + self.ingress_map_call(prs3, [provider_id], + [consumer_id], [service3]), + self.egress_map_call(prs3, [provider_id], + [consumer_id], [service3])] map_create.assert_has_calls(map_calls, any_order=True) - @unittest2.skip(TEMPORARY_SKIP) def test_create_ptg_ring(self): ring_size = 10 prs_ids = [] + services = [] for i in range(0, ring_size): prs_ids.append(self._prepare_rule_set()['id']) - - profile_ids = [driver.append_in_dir(prs_id) for prs_id in prs_ids] + services.append(self.rules[0]['policy_classifier_id']) # Create ring topology with self._mock_domain_create(),\ - self._mock_profile_list(profile_ids),\ self._mock_group_create() as group_create,\ self._mock_map_create() as map_create: @@ -641,11 +616,13 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): map_calls.append(self.ingress_map_call( prs_ids[i], [ptg_id], - [ptg_ids[i - 1]])) + [ptg_ids[i - 1]], + [services[i]])) map_calls.append(self.ingress_map_call(prs_ids[0], [ptg_ids[0]], - [ptg_id])) + [ptg_id], + [services[0]])) group_create.assert_has_calls(group_calls) map_create.assert_has_calls(map_calls, any_order=True) @@ -655,14 +632,15 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): # Delete single group and verify connectors are deleted with self._mock_map_delete() as map_delete,\ self._mock_map_create() as map_create,\ - self._mock_profile_list(profile_ids),\ self._mock_group_delete() as group_delete: ptg_id = ptg_ids[2] self.delete_policy_target_group(ptg_id) map_calls = [call(TEST_PROJECT, driver.append_in_dir(prs_ids[2])), - call(TEST_PROJECT, driver.append_in_dir(prs_ids[3]))] + call(TEST_PROJECT, driver.append_out_dir(prs_ids[2])), + call(TEST_PROJECT, driver.append_in_dir(prs_ids[3])), + call(TEST_PROJECT, driver.append_out_dir(prs_ids[3]))] map_delete.assert_has_calls(map_calls) map_create.assert_not_called() @@ -671,7 +649,6 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): # Remove connectors from single group with self._mock_map_delete() as map_delete,\ self._mock_map_create() as map_create,\ - self._mock_profile_list(profile_ids),\ self._mock_group_delete() as group_delete: ptg_id = ptg_ids[5] @@ -683,18 +660,16 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): map_create.assert_not_called() group_delete.assert_not_called() - @unittest2.skip(TEMPORARY_SKIP) def test_create_ptg_star(self): '''Star-like topology (single producer and N consumers) lifecycle''' star_size = 10 policy_rule_set = self._prepare_rule_set() + service = self.rules[0]['policy_classifier_id'] prs_id = policy_rule_set['id'] - profile_ids = [driver.append_in_dir(prs_id)] # Create topology with self._mock_domain_create(),\ - self._mock_profile_list(profile_ids),\ self._mock_group_create() as group_create,\ self._mock_map_create() as map_create: @@ -722,7 +697,14 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): map_calls.append(self.ingress_map_call( prs_id, [provider_id], - consumer_ids[:])) + consumer_ids[:], + [service])) + + map_calls.append(self.egress_map_call( + prs_id, + [provider_id], + consumer_ids[:], + [service])) group_create.assert_has_calls(group_calls) map_create.assert_has_calls(map_calls) @@ -733,7 +715,6 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): # Delete one consumer group with self._mock_map_delete() as map_delete,\ self._mock_map_create() as map_create,\ - self._mock_profile_list(profile_ids),\ self._mock_group_delete() as group_delete: consumer_id = consumer_ids.pop(0) @@ -742,7 +723,8 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): map_create.assert_has_calls( [self.ingress_map_call(prs_id, [provider_id], - consumer_ids)]) + consumer_ids, + [service])]) map_delete.assert_not_called() @@ -754,164 +736,104 @@ class TestPolicyTargetGroup(NsxPolicyMappingTestCase): # Delete provider group with self._mock_map_delete() as map_delete,\ self._mock_map_create() as map_create,\ - self._mock_profile_list(profile_ids),\ self._mock_group_delete() as group_delete: self.delete_policy_target_group(provider_id) map_create.assert_not_called() - map_delete.assert_called_with(TEST_PROJECT, - driver.append_in_dir(prs_id)) + map_delete.assert_has_calls( + [call(TEST_PROJECT, driver.append_in_dir(prs_id)), + call(TEST_PROJECT, driver.append_out_dir(prs_id))]) star_size -= 1 group_delete.assert_called_with(TEST_PROJECT, provider_id) + def test_update_rule_set(self): + '''Under consumer and producer group pair with single rule lifecycle, + Update rule set and verify backend gets updated + ''' + rule_set = self._prepare_rule_set(direction='in') + + # Create group pair + with self._mock_group_create(), \ + self._mock_map_create(), \ + self._mock_domain_create(): + + provider_ptg, consumer_ptg = self._create_provider_consumer_ptgs( + rule_set['id']) + + # Add another rule to the rule set + with self._mock_service_create(),\ + self._mock_map_create() as map_update: + + rule = self._create_simple_policy_rule(direction='in') + self.rules.append(rule) + + rules = [rule['id'] for rule in self.rules] + self.update_policy_rule_set(rule_set['id'], policy_rules=rules) + classifier_ids = sorted([rule['policy_classifier_id'] + for rule in self.rules]) + + map_update.assert_has_calls([self.ingress_map_call( + rule_set['id'], + [provider_ptg], + [consumer_ptg], + classifier_ids)]) + + # Delete all rules from rule set, leaving the rule set + with self._mock_map_delete() as map_delete: + self.update_policy_rule_set(rule_set['id'], policy_rules=[]) + + # verify communication map delete on backend + map_delete.assert_any_call( + TEST_PROJECT, driver.append_in_dir(rule_set['id'])) + + def test_update_rule_classifier(self): + '''Under consumer and producer group pair with single rule lifecycle, + Update rule set and verify backend gets updated + ''' + rule_set = self._prepare_rule_set(direction='in') + + # Create group pair + with self._mock_group_create(), \ + self._mock_map_create(), \ + self._mock_domain_create(): + + provider_ptg, consumer_ptg = self._create_provider_consumer_ptgs( + rule_set['id']) + + # Update classifier in rule + with self._mock_service_create(),\ + self._mock_map_create() as map_update: + + cl = self.create_policy_classifier( + name='test', + protocol='UDP', + port_range='512', + direction='bi')['policy_classifier'] + + self.update_policy_rule(self.rules[0]['id'], + policy_classifier_id=cl['id']) + + map_update.assert_has_calls([self.ingress_map_call( + rule_set['id'], + [provider_ptg], + [consumer_ptg], + [cl['id']])]) + class TestPolicyRuleSet(NsxPolicyMappingTestCase): - @unittest2.skip(TEMPORARY_SKIP) - def test_bidirectional(self): - ''' Create and delete bidirectional rule set''' + def test_basic(self): + ''' Create and delete unused rule set''' - with self._mock_profile_create() as profile_create,\ - self._mock_profile_delete() as profile_delete: - - rule = self._create_simple_policy_rule() - rule_set = self.create_policy_rule_set( - name='test', policy_rules=[rule['id']])['policy_rule_set'] - - calls = [call(name=mock.ANY, - description=mock.ANY, - profile_id=driver.append_in_dir(rule_set['id']), - services=[rule['policy_classifier_id']]), - call(name=mock.ANY, - description=mock.ANY, - profile_id=driver.append_out_dir(rule_set['id']), - services=[rule['policy_classifier_id']])] - - profile_create.assert_has_calls(calls) - - self.delete_policy_rule_set(rule_set['id']) - - calls = [call(driver.append_in_dir(rule_set['id'])), - call(driver.append_out_dir(rule_set['id']))] - profile_delete.assert_has_calls(calls) - - @unittest2.skip(TEMPORARY_SKIP) - def test_empty(self): - ''' Create and delete empty rule set and verify no backend calls''' rule = self._create_simple_policy_rule() - rule_set = self.create_policy_rule_set( - name='test', policy_rules=[rule['id']])['policy_rule_set'] + rule_set = self.create_policy_rule_set(name='test', + policy_rules=[rule['id']])['policy_rule_set'] + self.update_policy_rule_set(rule_set['id'], name='test1') self.delete_policy_rule_set(rule_set['id']) - @unittest2.skip(TEMPORARY_SKIP) - def test_create_fails(self): - ''' Create bidirectional rule set and fail second API call''' - - with self._mock_nth_profile_create_fails() as profile_create,\ - self._mock_profile_delete() as profile_delete: - - rule = self._create_simple_policy_rule() - self.assertRaises(webob.exc.HTTPClientError, - self.create_policy_rule_set, - name='test', - policy_rules=[rule['id']]) - - # Two create calls expected - calls = [call(name=mock.ANY, - description=mock.ANY, - profile_id=mock.ANY, - services=[rule['policy_classifier_id']]), - call(name=mock.ANY, - description=mock.ANY, - profile_id=mock.ANY, - services=[rule['policy_classifier_id']])] - - profile_create.assert_has_calls(calls) - - # Rollback - two delete calls expected - calls = [call(mock.ANY), call(mock.ANY)] - profile_delete.assert_has_calls(calls) - - @unittest2.skip(TEMPORARY_SKIP) - def _assert_profile_call(self, mock_calls, - name, profile_id, services): - '''Asserts service list in any order''' - - services_set = set(services) - for mock_call in mock_calls.call_args_list: - if isinstance(mock_call, dict): - if (mock_call.get('name') == name and - mock_call.get('profile_id') == profile_id and - set(mock_call.get('services')) == services_set): - - return True - - @unittest2.skip(TEMPORARY_SKIP) - def test_multi_set(self): - '''Test lifecycle of set with 3 rules having different dirs''' - - # Create rule set with 3 rules - with self._mock_profile_create() as profile_create: - - rule1 = self._create_simple_policy_rule('in', 'tcp', '7887') - rule2 = self._create_simple_policy_rule('out', 'udp', '8778') - rule3 = self._create_simple_policy_rule('bi', 'tcp', '5060') - - rule_set = self.create_policy_rule_set( - name='test', policy_rules=[rule1['id'], - rule2['id'], - rule3['id']])['policy_rule_set'] - - self.assertEqual(2, profile_create.call_count) - profile_create._assert_profile_call( - driver.append_in_dir('test'), - driver.append_in_dir(rule_set['id']), - [rule1['policy_classifier_id'], rule3['policy_classifier_id']]) - profile_create._assert_profile_call( - driver.append_out_dir('test'), - driver.append_out_dir(rule_set['id']), - [rule2['policy_classifier_id'], rule3['policy_classifier_id']]) - - # Replace rule3 with rule4 - with self._mock_profile_create() as profile_update: - rule4 = self._create_simple_policy_rule('out', 'tcp', '555:777') - - rule_set1 = self.update_policy_rule_set( - rule_set['id'], policy_rules=[rule1['id'], - rule2['id'], - rule4['id']])['policy_rule_set'] - - self.assertEqual(rule_set['id'], rule_set1['id']) - self.assertEqual(2, profile_create.call_count) - profile_update._assert_profile_call( - driver.append_in_dir('test'), - driver.append_in_dir(rule_set['id']), - [rule1['policy_classifier_id']]) - profile_update._assert_profile_call( - driver.append_out_dir('test'), - driver.append_out_dir(rule_set['id']), - [rule2['policy_classifier_id'], rule4['policy_classifier_id']]) - - # Delete rule1 from the rule set and verify ingress profile is - # is deleted on backend - with self._mock_profile_delete() as profile_delete: - self.update_policy_rule_set(rule_set['id'], - policy_rules=[rule2['id'], - rule4['id']]) - - profile_delete.assert_called_once_with( - driver.append_in_dir(rule_set['id'])) - - # Delete the rule set and verify egress profile is deleted - with self._mock_profile_delete() as profile_delete: - self.delete_policy_rule_set(rule_set['id']) - - profile_delete.assert_called_once_with( - driver.append_out_dir(rule_set['id'])) - class TestPolicyTargetTag(NsxPolicyMappingTestCase): @@ -920,7 +842,6 @@ class TestPolicyTargetTag(NsxPolicyMappingTestCase): return self.create_policy_target_group( name='test')['policy_target_group'] - @unittest2.skip(TEMPORARY_SKIP) def test_target_lifecycle(self): self._mock_nsx_db()