From ab39a63e3f4e41fb4720751931b1f0c21f03820f Mon Sep 17 00:00:00 2001 From: asarfaty Date: Tue, 14 Jul 2020 17:31:03 +0200 Subject: [PATCH] MP2P migration: manually migrate edge firewall sections Becasue of the different implementation fo edge firewall in the mp/policy drivers, the script will migrate those without the migration coordinator. Once the migration is done, each MP edge firewall with rule will be re-created as a policy one, and its rules will be deleted. Change-Id: I8de2055bd40a195067fb8c0b23309a25225dd083 --- vmware_nsx/plugins/common_v3/plugin.py | 4 + vmware_nsx/plugins/nsx_p/plugin.py | 8 +- .../fwaas/nsx_p/fwaas_callbacks_v2.py | 2 +- .../services/vpnaas/nsxp/ipsec_driver.py | 2 +- .../plugins/nsxv3/resources/migration.py | 144 ++++++++---------- .../admin/plugins/nsxv3/resources/utils.py | 13 +- 6 files changed, 86 insertions(+), 87 deletions(-) diff --git a/vmware_nsx/plugins/common_v3/plugin.py b/vmware_nsx/plugins/common_v3/plugin.py index 98451603bb..b5443c06b1 100644 --- a/vmware_nsx/plugins/common_v3/plugin.py +++ b/vmware_nsx/plugins/common_v3/plugin.py @@ -2918,6 +2918,10 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, "as default %(e)s", {'id': secgroup_db['id'], 'e': e}) + def verify_sr_at_backend(self, context, router_id): + """Should be implemented by each plugin""" + pass + class TagsCallbacks(object): diff --git a/vmware_nsx/plugins/nsx_p/plugin.py b/vmware_nsx/plugins/nsx_p/plugin.py index 62383b3a1c..8c8daa73c4 100644 --- a/vmware_nsx/plugins/nsx_p/plugin.py +++ b/vmware_nsx/plugins/nsx_p/plugin.py @@ -2468,8 +2468,10 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): return True return False - def verify_sr_at_backend(self, router_id): + def verify_sr_at_backend(self, context, router_id): """Check if the backend Tier1 has a service router or not""" + # Note - this method gets the context so it will have the same + # signature as the v3 plugin method if self.nsxpolicy.tier1.get_edge_cluster_path(router_id): return True @@ -2598,7 +2600,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): new_enable_snat = router.enable_snat newaddr, newmask, _newnexthop = self._get_external_attachment_info( context, router) - sr_currently_exists = self.verify_sr_at_backend(router_id) + sr_currently_exists = self.verify_sr_at_backend(context, router_id) fw_exist = self._router_has_edge_fw_rules(context, router) vpn_exist = self.service_router_has_vpnaas(context, router_id) lb_exist = False @@ -3041,7 +3043,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): prefix_len=prefix_len)) # Service router is mandatory for VLAN interfaces - if not self.verify_sr_at_backend(router_id): + if not self.verify_sr_at_backend(context, router_id): self.create_service_router( context, router_id, router=router_db, update_firewall=False) diff --git a/vmware_nsx/services/fwaas/nsx_p/fwaas_callbacks_v2.py b/vmware_nsx/services/fwaas/nsx_p/fwaas_callbacks_v2.py index 7e71530f06..9059a19c2d 100644 --- a/vmware_nsx/services/fwaas/nsx_p/fwaas_callbacks_v2.py +++ b/vmware_nsx/services/fwaas/nsx_p/fwaas_callbacks_v2.py @@ -378,7 +378,7 @@ class NsxpFwaasCallbacksV2(com_callbacks.NsxCommonv3FwaasCallbacksV2): self._set_rules_order(fw_rules) # Update the backend router firewall - sr_exists_on_backend = plugin.verify_sr_at_backend(router_id) + sr_exists_on_backend = plugin.verify_sr_at_backend(context, router_id) if called_from_fw: # FW action required if router_with_fw: diff --git a/vmware_nsx/services/vpnaas/nsxp/ipsec_driver.py b/vmware_nsx/services/vpnaas/nsxp/ipsec_driver.py index 9f0f015edb..f696500bca 100644 --- a/vmware_nsx/services/vpnaas/nsxp/ipsec_driver.py +++ b/vmware_nsx/services/vpnaas/nsxp/ipsec_driver.py @@ -671,7 +671,7 @@ class NSXpIPsecVpnDriver(common_driver.NSXcommonIPsecVpnDriver): # Make sure this tier1 has service router router_id = vpnservice['router_id'] - if not self._core_plugin.verify_sr_at_backend(router_id): + if not self._core_plugin.verify_sr_at_backend(context, router_id): self._core_plugin.create_service_router(context, router_id) # create the NSX vpn service diff --git a/vmware_nsx/shell/admin/plugins/nsxv3/resources/migration.py b/vmware_nsx/shell/admin/plugins/nsxv3/resources/migration.py index 88474688c0..caff4f37fd 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv3/resources/migration.py +++ b/vmware_nsx/shell/admin/plugins/nsxv3/resources/migration.py @@ -18,6 +18,7 @@ import logging import paramiko import tenacity +from neutron_fwaas.db.firewall.v2 import firewall_db_v2 from neutron_lib.callbacks import registry from neutron_lib import context from oslo_config import cfg @@ -29,6 +30,7 @@ from vmware_nsx.plugins.nsx_p import plugin as p_plugin from vmware_nsx.plugins.nsx_v3 import cert_utils from vmware_nsx.plugins.nsx_v3 import plugin as v3_plugin from vmware_nsx.plugins.nsx_v3 import utils as v3_plugin_utils +from vmware_nsx.services.fwaas.nsx_p import fwaas_callbacks_v2 from vmware_nsx.services.lbaas import lb_const from vmware_nsx.services.lbaas.nsx_p.implementation import lb_utils from vmware_nsx.shell.admin.plugins.common import constants @@ -1078,62 +1080,6 @@ def migrate_lb_services(nsxlib, nsxpolicy): MIGRATE_LIMIT_LB_SERVICE) -def edge_firewall_migration_cond(resource): - return (resource.get('display_name') == 'Default LR Layer3 Section' and - resource.get('enforced_on') == 'LOGICALROUTER' and - resource.get('category') == 'Default' and - resource.get('section_type') == 'LAYER3') - - -def migrate_fwaas_resources(nsxlib, nsxpolicy, migrated_routers): - def get_policy_id_callback(res, policy_id): - # Policy id should be the Policy tier1 id (=neutron id) - ctx = context.get_admin_context() - nsx_id = res['applied_tos'][0]['target_id'] - return db.get_neutron_from_nsx_router_id( - ctx.session, nsx_id) - - def cond(resource): - # Migrate only Edge firewalls related to the migrated tier1s - return (edge_firewall_migration_cond(resource) and - resource['applied_tos'][0].get( - 'target_id', '') in migrated_routers) - - def add_metadata(entry, policy_id, resource): - # Add category, sequence and rule ids - global EDGE_FW_SEQ - metadata = [{'key': 'category', - 'value': policy_constants.CATEGORY_LOCAL_GW}, - {'key': 'sequence', 'value': str(EDGE_FW_SEQ)}] - EDGE_FW_SEQ = EDGE_FW_SEQ + 1 - - # Add the rules - rules = nsxlib.firewall_section.get_rules(resource['id'])['results'] - linked_ids = [] - seq = 1 - for rule in rules: - linked_ids.append({'key': rule['id'], 'value': str(seq)}) - # The id of the policy rule will be random - seq = seq + 1 - entry['metadata'] = metadata - entry['linked_ids'] = linked_ids - - def get_policy_section(sec_id, silent=False): - return nsxpolicy.gateway_policy.get( - policy_constants.DEFAULT_DOMAIN, sec_id, silent=silent) - - entries = get_resource_migration_data( - nsxlib.firewall_section, None, - 'EDGE_FIREWALL_SECTION', resource_condition=cond, - policy_resource_get=get_policy_section, - policy_id_callback=get_policy_id_callback, - metadata_callback=add_metadata) - # Edge firewall migration is not supported yet - migrate_resource(nsxlib, 'EDGE_FIREWALL_SECTION', entries, - MIGRATE_LIMIT_SECTION_AND_RULES, - count_internals=True) - - def migrate_t_resources_2_p(nsxlib, nsxpolicy, plugin): """Create policy resources for all MP resources used by neutron""" @@ -1168,7 +1114,6 @@ def migrate_t_resources_2_p(nsxlib, nsxpolicy, plugin): # Migrate firewall sections last as those take the longest to rollback # in case of error migrate_dfw_sections(nsxlib, nsxpolicy, plugin) - migrate_fwaas_resources(nsxlib, nsxpolicy, mp_routers) # Finalize the migration (cause policy realization) end_migration_process(nsxlib) @@ -1347,31 +1292,72 @@ def post_migration_actions(nsxlib, nsxpolicy, nsxpolicy_admin, plugin): LOG.debug("Updated gateway of network %s", net['id']) break - # Update tags on DFW sections + # -- Migrate edge firewall sections: + # The MP plugin uses the default MP edge firewall section, while the policy + # plugin uses a non default one, so regular migration cannot be used. + # Instead, create new edge firewall sections, and remove rules from the MP + # default sections + + # This is a hack to use the v3 plugin with the policy fwaas driver + class MigrationNsxpFwaasCallbacks(fwaas_callbacks_v2.NsxpFwaasCallbacksV2): + def __init__(self, with_rpc): + super(MigrationNsxpFwaasCallbacks, self).__init__(with_rpc) + # Make sure fwaas is considered as enabled + self.fwaas_enabled = True + + def _get_port_firewall_group_id(self, context, port_id): + # Override this api because directory.get_plugin does not work from + # admin utils context. + driver_db = firewall_db_v2.FirewallPluginDb() + return driver_db.get_fwg_attached_to_port(context, port_id) + + fwaas_callbacks = MigrationNsxpFwaasCallbacks(False) + plugin.nsxpolicy = nsxpolicy routers = plugin.get_routers(ctx) + nsx_router_sections = [] for rtr in routers: - try: - # Check if the edge firewall section exists - nsxpolicy.gateway_policy.get( - policy_constants.DEFAULT_DOMAIN, map_id=rtr['id'], - silent=True) - except nsxlib_exc.ResourceNotFound: - pass - else: - # Update section tags - tags = nsxpolicy.build_v3_tags_payload( - rtr, resource_type='os-neutron-router-id', - project_name=ctx.tenant_name) - nsxpolicy.gateway_policy.update( - policy_constants.DEFAULT_DOMAIN, - map_id=rtr['id'], - tags=tags) - LOG.debug("Updated tags of gateway policy for router %s", - rtr['id']) + nsx_router_id = db.get_nsx_router_id(ctx.session, rtr['id']) + nsx_rtr = nsxlib.logical_router.get(nsx_router_id) + for sec in nsx_rtr.get('firewall_sections', []): + section_id = sec['target_id'] + section = nsxlib.firewall_section.get(section_id) + if section['display_name'] != 'Default LR Layer3 Section': + continue + rules = nsxlib.firewall_section.get_rules(section_id)['results'] + if len(rules) <= 1: + continue + # Non default rules exist. need to migrate this section + router_db = plugin._get_router(ctx, rtr['id']) + ports = plugin._get_router_interfaces(ctx, rtr['id']) + fwaas_callbacks.update_router_firewall( + ctx, rtr['id'], router_db, ports) + LOG.debug("Created GW policy for router %s", rtr['id']) + + # delete rule from the default mp section at the end of the loop + # so the new section will have time to realize + nsx_router_sections.append({'id': section_id, + 'default_rule': rules[-1], + 'router_id': rtr['id']}) + + # Remove old rules from the default sections + for section in nsx_router_sections: + # make sure the policy section was already realized + nsxpolicy.gateway_policy.wait_until_realized( + policy_constants.DEFAULT_DOMAIN, section['router_id']) + nsxlib.firewall_section.update( + section['id'], rules=[section['default_rule']]) + LOG.debug("Deleted MP edge FW section %s rules", section['id']) LOG.info("Post-migration actions done.") +def edge_firewall_migration_cond(resource): + return (resource.get('display_name') == 'Default LR Layer3 Section' and + resource.get('enforced_on') == 'LOGICALROUTER' and + resource.get('category') == 'Default' and + resource.get('section_type') == 'LAYER3') + + def pre_migration_checks(nsxlib, plugin): """Check for unsupported configuration that will block the migration """ @@ -1464,7 +1450,8 @@ def t_2_p_migration(resource, event, trigger, **kwargs): "in the configuration") return - nsxlib = utils.get_connected_nsxlib(verbose=verbose) + nsxlib = utils.get_connected_nsxlib( + verbose=verbose, allow_overwrite_header=True) nsxpolicy = p_utils.get_connected_nsxpolicy( conf_path=cfg.CONF.nsx_v3) # Also create a policy manager with admin user to manipulate admin-defined @@ -1476,6 +1463,9 @@ def t_2_p_migration(resource, event, trigger, **kwargs): nsx_password=cfg.CONF.nsx_v3.nsx_api_password) with utils.NsxV3PluginWrapper(verbose=verbose) as plugin: + # Make sure FWaaS was initialized + plugin.init_fwaas_for_admin_utils() + if not pre_migration_checks(nsxlib, plugin): # Failed LOG.error("T2P migration cannot run. Please fix the configuration " diff --git a/vmware_nsx/shell/admin/plugins/nsxv3/resources/utils.py b/vmware_nsx/shell/admin/plugins/nsxv3/resources/utils.py index 06c2f28666..14a037fd99 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv3/resources/utils.py +++ b/vmware_nsx/shell/admin/plugins/nsxv3/resources/utils.py @@ -48,6 +48,7 @@ def get_nsxv3_client(nsx_username=None, nsx_password=None, def get_connected_nsxlib(nsx_username=None, nsx_password=None, use_basic_auth=False, plugin_conf=None, + allow_overwrite_header=False, verbose=False): global _NSXLIB @@ -60,12 +61,14 @@ def get_connected_nsxlib(nsx_username=None, nsx_password=None, if not verbose: # Return logs to normal logging.disable(logging.NOTSET) - return v3_utils.get_nsxlib_wrapper(nsx_username, - nsx_password, - use_basic_auth, - plugin_conf) + return v3_utils.get_nsxlib_wrapper( + nsx_username, nsx_password, use_basic_auth, + plugin_conf=plugin_conf, + allow_overwrite_header=allow_overwrite_header) if _NSXLIB is None: - _NSXLIB = v3_utils.get_nsxlib_wrapper(plugin_conf=plugin_conf) + _NSXLIB = v3_utils.get_nsxlib_wrapper( + plugin_conf=plugin_conf, + allow_overwrite_header=allow_overwrite_header) if not verbose: # Return logs to normal