diff --git a/vmware_nsx/api_replay/client.py b/vmware_nsx/api_replay/client.py index d19d800fbd..4408353b3c 100644 --- a/vmware_nsx/api_replay/client.py +++ b/vmware_nsx/api_replay/client.py @@ -90,6 +90,7 @@ class ApiReplayClient(utils.PrepareObjectForMigration): self.migrate_networks_subnets_ports(routers_gw_info) self.migrate_floatingips() self.migrate_routers_routes(routers_routes) + self.migrate_fwaas() LOG.info("NSX migration is Done.") def connect_to_client(self, username, user_domain_id, @@ -621,3 +622,68 @@ class ApiReplayClient(utils.PrepareObjectForMigration): except Exception as e: LOG.error("Failed to create floating ip (%(fip)s) : %(e)s", {'fip': source_fip, 'e': e}) + + def _migrate_fwaas_resource(self, resource_type, source_objects, + dest_objects, prepare_method, create_method): + total_num = len(source_objects) + for count, source_obj in enumerate(source_objects, 1): + # Check if the object already exists + if self.have_id(source_obj['id'], dest_objects): + LOG.info("Skipping %s %s as it already exists on the " + "destination server", resource_type, source_obj['id']) + continue + body = prepare_method(source_obj) + try: + new_obj = create_method({resource_type: body}) + LOG.info("Created %(resource)s %(count)s/%(total)s : %(obj)s", + {'resource': resource_type, 'count': count, + 'total': total_num, 'obj': new_obj}) + except Exception as e: + LOG.error("Failed to create %(resource)s (%(obj)s) : %(e)s", + {'resource': resource_type, 'obj': source_obj, + 'e': e}) + + def migrate_fwaas(self): + """Migrates FWaaS V2 objects from source to dest neutron.""" + try: + source_rules = self.source_neutron.\ + list_fwaas_firewall_rules()['firewall_rules'] + source_polices = self.source_neutron.\ + list_fwaas_firewall_policies()['firewall_policies'] + source_groups = self.source_neutron.\ + list_fwaas_firewall_groups()['firewall_groups'] + except Exception as e: + # FWaaS might be disabled in the source + LOG.info("FWaaS V2 was not found on the source server: %s", e) + return + + try: + dest_rules = self.dest_neutron.\ + list_fwaas_firewall_rules()['firewall_rules'] + dest_polices = self.dest_neutron.\ + list_fwaas_firewall_policies()['firewall_policies'] + dest_groups = self.dest_neutron.\ + list_fwaas_firewall_groups()['firewall_groups'] + except Exception as e: + # FWaaS might be disabled in the destination + LOG.warning("Skipping FWaaS V2 migration. FWaaS V2 was not found " + "on the destination server: %s", e) + return + + # Migrate all FWaaS objects: + self._migrate_fwaas_resource( + 'firewall_rule', source_rules, dest_rules, + self.prepare_fwaas_rule, + self.dest_neutron.create_fwaas_firewall_rule) + + self._migrate_fwaas_resource( + 'firewall_policy', source_polices, dest_polices, + self.prepare_fwaas_policy, + self.dest_neutron.create_fwaas_firewall_policy) + + self._migrate_fwaas_resource( + 'firewall_group', source_groups, dest_groups, + self.prepare_fwaas_group, + self.dest_neutron.create_fwaas_firewall_group) + + LOG.info("FWaaS V2 migration done") diff --git a/vmware_nsx/api_replay/utils.py b/vmware_nsx/api_replay/utils.py index 40299b368c..988dab6178 100644 --- a/vmware_nsx/api_replay/utils.py +++ b/vmware_nsx/api_replay/utils.py @@ -98,6 +98,10 @@ class PrepareObjectForMigration(object): drop_qos_rule_fields = ['revision', 'type', 'qos_policy_id', 'id'] drop_qos_policy_fields = ['revision'] + drop_fwaas_rule_fields = ['firewall_policy_id'] + drop_fwaas_policy_fields = [] + drop_fwaas_group_fields = ['status'] + def drop_fields(self, item, drop_fields): body = {} for k, v in item.items(): @@ -248,3 +252,15 @@ class PrepareObjectForMigration(object): def prepare_qos_policy(self, policy, direct_call=False): self.fix_description(policy) return self.drop_fields(policy, self.drop_qos_policy_fields) + + def prepare_fwaas_rule(self, rule): + self.fix_description(rule) + return self.drop_fields(rule, self.drop_fwaas_rule_fields) + + def prepare_fwaas_policy(self, policy): + self.fix_description(policy) + return self.drop_fields(policy, self.drop_fwaas_policy_fields) + + def prepare_fwaas_group(self, group): + self.fix_description(group) + return self.drop_fields(group, self.drop_fwaas_group_fields) diff --git a/vmware_nsx/extensions/api_replay.py b/vmware_nsx/extensions/api_replay.py index 9e7d02798b..75cb4b6ade 100644 --- a/vmware_nsx/extensions/api_replay.py +++ b/vmware_nsx/extensions/api_replay.py @@ -52,6 +52,16 @@ RESOURCE_ATTRIBUTE_MAP = { 'policies': { # QoS policies 'id': ID_WITH_POST, }, + 'firewall_rules': { # FWaaS V2 rules + 'id': ID_WITH_POST, + }, + 'firewall_policies': { # FWaaS V2 policies + 'id': ID_WITH_POST, + }, + 'firewall_groups': { # FWaaS V2 groups + 'id': ID_WITH_POST, + }, + } diff --git a/vmware_nsx/services/fwaas/common/api_replay_driver.py b/vmware_nsx/services/fwaas/common/api_replay_driver.py new file mode 100644 index 0000000000..03c0c06176 --- /dev/null +++ b/vmware_nsx/services/fwaas/common/api_replay_driver.py @@ -0,0 +1,99 @@ +# Copyright 2019 VMware, Inc. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutron_fwaas.db.firewall.v2 import firewall_db_v2 +from neutron_fwaas.services.firewall.service_drivers.agents import agents +from neutron_lib import constants as nl_constants + + +class ApiReplayFirewallPluginDb(firewall_db_v2.FirewallPluginDb): + """Override FWaaS agent DB actions to use given objects IDs""" + def create_firewall_rule(self, context, firewall_rule): + fwr = firewall_rule + src_port_min, src_port_max = self._get_min_max_ports_from_range( + fwr['source_port']) + dst_port_min, dst_port_max = self._get_min_max_ports_from_range( + fwr['destination_port']) + with context.session.begin(subtransactions=True): + fwr_db = firewall_db_v2.FirewallRuleV2( + # Use given ID for api_replay support + id=fwr.get('id'), + tenant_id=fwr['tenant_id'], + name=fwr['name'], + description=fwr['description'], + protocol=fwr['protocol'], + ip_version=fwr['ip_version'], + source_ip_address=fwr['source_ip_address'], + destination_ip_address=fwr['destination_ip_address'], + source_port_range_min=src_port_min, + source_port_range_max=src_port_max, + destination_port_range_min=dst_port_min, + destination_port_range_max=dst_port_max, + action=fwr['action'], + enabled=fwr['enabled'], + shared=fwr['shared']) + context.session.add(fwr_db) + return self._make_firewall_rule_dict(fwr_db) + + def create_firewall_policy(self, context, firewall_policy): + """This method is manipulated to allow the creation of additional + default firewall policy, and do not automatically ensure one exists + """ + fwp = firewall_policy + with context.session.begin(subtransactions=True): + # Use given ID for api_replay support + fwp_db = firewall_db_v2.FirewallPolicy( + id=fwp.get('id'), + tenant_id=fwp['tenant_id'], + name=fwp['name'], + description=fwp['description'], + audited=fwp['audited'], + shared=fwp['shared']) + context.session.add(fwp_db) + self._set_rules_for_policy(context, fwp_db, fwp) + return self._make_firewall_policy_dict(fwp_db) + + def create_firewall_group(self, context, firewall_group, + default_fwg=False): + """This method is manipulated to allow the creation of additional + default firewall group, and do not automatically ensure one exists + """ + fwg = firewall_group + tenant_id = fwg['tenant_id'] + if firewall_group.get('status') is None: + fwg['status'] = nl_constants.CREATED + + with context.session.begin(subtransactions=True): + # Use given ID for api_replay support + fwg_db = firewall_db_v2.FirewallGroup( + id=fwg.get('id'), + tenant_id=tenant_id, + name=fwg['name'], + description=fwg['description'], + status=fwg['status'], + ingress_firewall_policy_id=fwg['ingress_firewall_policy_id'], + egress_firewall_policy_id=fwg['egress_firewall_policy_id'], + admin_state_up=fwg['admin_state_up'], + shared=fwg['shared']) + context.session.add(fwg_db) + self._set_ports_for_firewall_group(context, fwg_db, fwg) + return self._make_firewall_group_dict(fwg_db) + + +class ApiReplayFirewallAgentDriver(agents.FirewallAgentDriver): + """FWaaS V2 agent driver for api-replay allowing POST with id.""" + def __init__(self, *args, **kwargs): + super(ApiReplayFirewallAgentDriver, self).__init__(*args, **kwargs) + self.firewall_db = ApiReplayFirewallPluginDb()