From 032b6b8e464e2a16446481242cee93c09b5e3954 Mon Sep 17 00:00:00 2001 From: Adit Sarfaty Date: Thu, 11 Apr 2019 15:55:42 +0300 Subject: [PATCH] NSX|V adminUtils: detect and clean orphaned section rules nsxadmin -r orphaned-rules -o list/nsx-clean will detect/delete orphaned rules inside nsx sections that belong to neutron security groups. Change-Id: I18ee55e70b8e3a97d7d5d2453b7994bc07d2c97c --- doc/source/admin_util.rst | 6 ++ vmware_nsx/plugins/nsx_v/vshield/vcns.py | 6 ++ .../shell/admin/plugins/common/constants.py | 1 + .../plugins/nsxv/resources/securitygroups.py | 75 ++++++++++++++++++- vmware_nsx/shell/resources.py | 3 + .../tests/unit/nsx_v/vshield/fake_vcns.py | 3 + 6 files changed, 93 insertions(+), 1 deletion(-) diff --git a/doc/source/admin_util.rst b/doc/source/admin_util.rst index f103bacd4b..48fa374b4e 100644 --- a/doc/source/admin_util.rst +++ b/doc/source/admin_util.rst @@ -284,6 +284,12 @@ Security Groups, Firewall and Spoofguard nsxadmin -r spoofguard-policy -o list-mismatches (--property network=) - List spoofguard policies with mismatching ips or mac, globally or for a specific network nsxadmin -r spoofguard-policy -o fix-mismatch --property port= - Fix the spoofgurad ips of a neutron port +- Orphaned rules in NSX section:: + + nsxadmin -r orphaned-rules -o list + nsxadmin -r orphaned-rules -o nsx-clean + + Metadata ~~~~~~~~ diff --git a/vmware_nsx/plugins/nsx_v/vshield/vcns.py b/vmware_nsx/plugins/nsx_v/vshield/vcns.py index a6a9e7deda..e0646ffb89 100644 --- a/vmware_nsx/plugins/nsx_v/vshield/vcns.py +++ b/vmware_nsx/plugins/nsx_v/vshield/vcns.py @@ -732,6 +732,12 @@ class Vcns(object): return self.do_request(HTTP_DELETE, uri, format='xml', headers=headers) + def get_section_rules(self, section_uri): + headers = self._get_section_header(section_uri) + h, c = self.do_request(HTTP_GET, section_uri, + headers=headers, decode=True) + return c['rules'] + @retry_upon_exception(exceptions.RequestBad) def add_member_to_security_group(self, security_group_id, member_id): """Adds a vnic member to nsx security group.""" diff --git a/vmware_nsx/shell/admin/plugins/common/constants.py b/vmware_nsx/shell/admin/plugins/common/constants.py index b8846426d9..f61e89d8aa 100644 --- a/vmware_nsx/shell/admin/plugins/common/constants.py +++ b/vmware_nsx/shell/admin/plugins/common/constants.py @@ -57,6 +57,7 @@ SPOOFGUARD_POLICY = 'spoofguard-policy' BACKUP_EDGES = 'backup-edges' ORPHANED_EDGES = 'orphaned-edges' ORPHANED_BINDINGS = 'orphaned-bindings' +ORPHANED_RULES = 'orphaned-rules' ORPHANED_VNICS = 'orphaned-vnics' MISSING_EDGES = 'missing-edges' METADATA = 'metadata' diff --git a/vmware_nsx/shell/admin/plugins/nsxv/resources/securitygroups.py b/vmware_nsx/shell/admin/plugins/nsxv/resources/securitygroups.py index 9e05445103..bdfe60206e 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv/resources/securitygroups.py +++ b/vmware_nsx/shell/admin/plugins/nsxv/resources/securitygroups.py @@ -70,6 +70,16 @@ class NeutronSecurityGroupDB( for mapp in q] return sg_mappings + def get_security_group_rules_mappings(self): + q = self.context.session.query( + sg_models.SecurityGroupRule.id, + nsxv_models.NsxvRuleMapping.nsx_rule_id).join( + nsxv_models.NsxvRuleMapping).all() + sg_mappings = [{'rule_id': mapp[0], + 'nsx_rule_id': mapp[1]} + for mapp in q] + return sg_mappings + def get_security_group(self, sg_id): return super(NeutronSecurityGroupDB, self).get_security_group( self.context, sg_id) @@ -165,6 +175,12 @@ class NsxFirewallAPI(object): "config/layer3sections/%s" % section_id) self.vcns.delete_section(section_uri) + def list_fw_section_rules(self, section_uri): + return self.vcns.get_section_rules(section_uri) + + def remove_rule_from_section(self, section_uri, rule_id): + return self.vcns.remove_rule_from_section(section_uri, rule_id) + def reorder_fw_sections(self): # read all the sections h, firewall_config = self.vcns.get_dfw_config() @@ -333,6 +349,55 @@ def clean_unused_firewall_sections(resource, event, trigger, **kwargs): return bool(unused_sections) +def _find_orphaned_section_rules(): + fw_sections = nsxv_firewall.list_fw_sections() + sg_mappings = neutron_sg.get_security_groups_mappings() + rules_mappings = neutron_sg.get_security_group_rules_mappings() + mapped_rules_ids = [rule['nsx_rule_id'] for rule in rules_mappings] + orphaned_rules = [] + + for sg_db in sg_mappings: + for fw_section in fw_sections: + if fw_section['id'] == sg_db.get('section-uri', '').split('/')[-1]: + # Neutron section. + nsx_rules = nsxv_firewall.list_fw_section_rules( + sg_db.get('section-uri')) + for nsx_rule in nsx_rules: + if str(nsx_rule['id']) not in mapped_rules_ids: + orphaned_rules.append( + {'nsx-rule-id': nsx_rule['id'], + 'section-uri': sg_db['section-uri'], + 'section-id': fw_section['id'], + 'security-group-id': sg_db['id'], + 'security-group-name': sg_db['name']}) + + return orphaned_rules + + +@admin_utils.output_header +def list_orphaned_firewall_section_rules(resource, event, trigger, **kwargs): + orphaned_rules = _find_orphaned_section_rules() + _log_info(constants.FIREWALL_SECTIONS, orphaned_rules, + attrs=['security-group-name', 'security-group-id', 'section-id', + 'nsx-rule-id']) + return bool(orphaned_rules) + + +@admin_utils.output_header +def clean_orphaned_firewall_section_rules(resource, event, trigger, **kwargs): + orphaned_rules = _find_orphaned_section_rules() + for rule in orphaned_rules: + try: + nsxv_firewall.remove_rule_from_section( + rule['section-uri'], rule['nsx-rule-id']) + except Exception as e: + LOG.error("Failed to delete rule %s from section %s: %s", + rule['nsx-rule-id'], rule['section-id'], e) + else: + LOG.info("Backend rule %s was deleted from section %s", + rule['nsx-rule-id'], rule['section-id']) + + @admin_utils.output_header def reorder_firewall_sections(resource, event, trigger, **kwargs): nsxv_firewall.reorder_fw_sections() @@ -497,7 +562,7 @@ def update_security_groups_logging(resource, event, trigger, **kwargs): with utils.NsxVPluginWrapper() as plugin: vcns = plugin.nsx_v.vcns - sg_utils = plugin. nsx_sg_utils + sg_utils = plugin.nsx_sg_utils # If the section/sg is already logged, then no action is # required. security_groups = plugin.get_security_groups(context) @@ -562,3 +627,11 @@ registry.subscribe(list_unused_firewall_sections, registry.subscribe(clean_unused_firewall_sections, constants.FIREWALL_SECTIONS, shell.Operations.NSX_CLEAN.value) + +registry.subscribe(list_orphaned_firewall_section_rules, + constants.ORPHANED_RULES, + shell.Operations.LIST.value) + +registry.subscribe(clean_orphaned_firewall_section_rules, + constants.ORPHANED_RULES, + shell.Operations.NSX_CLEAN.value) diff --git a/vmware_nsx/shell/resources.py b/vmware_nsx/shell/resources.py index 55fe500667..69c0e7456f 100644 --- a/vmware_nsx/shell/resources.py +++ b/vmware_nsx/shell/resources.py @@ -211,6 +211,9 @@ nsxv_resources = { Operations.NSX_REORDER.value, Operations.LIST_UNUSED.value, Operations.NSX_CLEAN.value]), + constants.ORPHANED_RULES: Resource(constants.ORPHANED_RULES, + [Operations.LIST.value, + Operations.NSX_CLEAN.value]), constants.METADATA: Resource( constants.METADATA, [Operations.NSX_UPDATE.value, Operations.NSX_UPDATE_SECRET.value, diff --git a/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py b/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py index 078155ac93..9b366794b9 100644 --- a/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py +++ b/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py @@ -1038,6 +1038,9 @@ class FakeVcns(object): else: return self._get_section(section_id) + def get_section_rules(self, section_uri): + return [] + def _get_section(self, section_id): section_rules = ( b''.join(self._sections[section_id]['rules'].values()))