# Copyright 2015 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. import optparse import sqlalchemy as sa from vmware_nsx.db import nsx_models from vmware_nsxlib import v3 from vmware_nsxlib.v3 import config from vmware_nsxlib.v3 import nsx_constants class NeutronNsxDB(object): def __init__(self, db_connection): super(NeutronNsxDB, self).__init__() engine = sa.create_engine(db_connection) self.session = sa.orm.session.sessionmaker()(bind=engine) def query_all(self, column, model): return list(set([r[column] for r in self.session.query(model).all()])) def get_logical_ports(self): return self.query_all('nsx_port_id', nsx_models.NeutronNsxPortMapping) def get_nsgroups(self): return self.query_all('nsx_id', nsx_models.NeutronNsxSecurityGroupMapping) def get_firewall_sections(self): return self.query_all('nsx_id', nsx_models.NeutronNsxFirewallSectionMapping) def get_logical_routers(self): return self.query_all('nsx_id', nsx_models.NeutronNsxRouterMapping) def get_logical_switches(self): return self.query_all('nsx_id', nsx_models.NeutronNsxNetworkMapping) def get_logical_dhcp_servers(self): return self.query_all('nsx_service_id', nsx_models.NeutronNsxServiceBinding) def get_vpn_objects(self, column_name): return self.query_all(column_name, nsx_models.NsxVpnConnectionMapping) def get_db_objects_by_table_and_column(self, db_table, db_column): return self.query_all(db_column, db_table) class NSXClient(object): """Base NSX REST client""" API_VERSION = "v1" NULL_CURSOR_PREFIX = '0000' def __init__(self, host, username, password, db_connection): self.host = host self.username = username self.password = password self.neutron_db = (NeutronNsxDB(db_connection) if db_connection else None) nsxlib_config = config.NsxLibConfig( username=self.username, password=self.password, nsx_api_managers=[self.host], # allow admin user to delete entities created # under openstack principal identity allow_overwrite_header=True) self.nsxlib = v3.NsxLib(nsxlib_config) def get_transport_zones(self): """ Retrieve all transport zones """ return self.nsxlib.transport_zone.list()['results'] def get_logical_ports(self): """ Retrieve all logical ports on NSX backend """ return self.nsxlib.logical_port.list()['results'] def get_os_logical_ports(self): """ Retrieve all logical ports created from OpenStack """ lports = self.get_os_resources( self.get_logical_ports()) if self.neutron_db: db_lports = self.neutron_db.get_logical_ports() lports = [lp for lp in lports if lp['id'] in db_lports] return lports def update_logical_port_attachment(self, lports): """ In order to delete logical ports, we need to detach the VIF attachment on the ports first. """ for p in lports: try: self.nsxlib.logical_port.update( p['id'], None, attachment_type=None) except Exception as e: print("ERROR: Failed to update lport %s: %s" % p['id'], e) def _remove_port_from_exclude_list(self, p): try: self.nsxlib.firewall_section.remove_member_from_fw_exclude_list( p['id'], None) except Exception: pass def _cleanup_logical_ports(self, lports): # logical port vif detachment self.update_logical_port_attachment(lports) for p in lports: # delete this port from the exclude list (if in it) self._remove_port_from_exclude_list(p) try: self.nsxlib.logical_port.delete(p['id']) except Exception as e: print("ERROR: Failed to delete logical port %s, error %s" % (p['id'], e)) else: print("Successfully deleted logical port %s" % p['id']) def cleanup_os_logical_ports(self): """ Delete all logical ports created by OpenStack """ os_lports = self.get_os_logical_ports() print("Number of OS Logical Ports to be deleted: %s" % len(os_lports)) self._cleanup_logical_ports(os_lports) def get_os_resources(self, resources): """ Get all logical resources created by OpenStack """ os_resources = [r for r in resources if 'tags' in r for tag in r['tags'] if 'os-api-version' in tag.values()] return os_resources def get_logical_switches(self): """ Retrieve all logical switches on NSX backend """ return self.nsxlib.logical_switch.list()['results'] def get_os_logical_switches(self): """ Retrieve all logical switches created from OpenStack """ lswitches = self.get_os_resources( self.get_logical_switches()) if self.neutron_db: db_lswitches = self.neutron_db.get_logical_switches() lswitches = [ls for ls in lswitches if ls['id'] in db_lswitches] return lswitches def get_lswitch_ports(self, ls_id): """ Return all the logical ports that belong to this lswitch """ lports = self.get_logical_ports() return [p for p in lports if p['logical_switch_id'] == ls_id] def cleanup_os_logical_switches(self): """ Delete all logical switches created from OpenStack """ lswitches = self.get_os_logical_switches() print("Number of OS Logical Switches to be deleted: %s" % len(lswitches)) for ls in lswitches: # Check if there are still ports on switch and blow them away # An example here is a metadata proxy port (this is not stored # in the DB so we are unable to delete it when reading ports # from the DB) lports = self.get_lswitch_ports(ls['id']) if lports: print("Number of orphan OS Logical Ports to be " "deleted: %s" % len(lports)) self._cleanup_logical_ports(lports) try: self.nsxlib.logical_switch.delete(ls['id']) except Exception as e: print("ERROR: Failed to delete logical switch %s-%s, " "error %s" % (ls['display_name'], ls['id'], e)) else: print("Successfully deleted logical switch %s-%s" % (ls['display_name'], ls['id'])) def get_firewall_sections(self): """ Retrieve all firewall sections """ return self.nsxlib.firewall_section.list() def get_os_firewall_sections(self): """ Retrieve all firewall sections created from OpenStack """ fw_sections = self.get_os_resources( self.get_firewall_sections()) if self.neutron_db: db_sections = self.neutron_db.get_firewall_sections() fw_sections = [fws for fws in fw_sections if fws['id'] in db_sections] return fw_sections def cleanup_os_firewall_sections(self): """ Cleanup all firewall sections created from OpenStack """ fw_sections = self.get_os_firewall_sections() print("Number of OS Firewall Sections to be deleted: %s" % len(fw_sections)) for fw in fw_sections: try: self.nsxlib.firewall_section.delete(fw['id']) except Exception as e: print("Failed to delete firewall section %s: %s" % (fw['display_name'], e)) else: print("Successfully deleted firewall section %s" % fw['display_name']) def get_ns_groups(self): """ Retrieve all NSGroups on NSX backend """ backend_groups = self.nsxlib.ns_group.list() ns_groups = self.get_os_resources(backend_groups) if self.neutron_db: db_nsgroups = self.neutron_db.get_nsgroups() ns_groups = [nsg for nsg in ns_groups if nsg['id'] in db_nsgroups] return ns_groups def cleanup_os_ns_groups(self): """ Cleanup all NSGroups created from OpenStack plugin """ ns_groups = self.get_ns_groups() print("Number of OS NSGroups to be deleted: %s" % len(ns_groups)) for nsg in ns_groups: try: self.nsxlib.ns_group.delete(nsg['id']) except Exception as e: print("Failed to delete NSGroup: %s: %s" % (nsg['display_name'], e)) else: print("Successfully deleted NSGroup: %s" % nsg['display_name']) def get_switching_profiles(self): """ Retrieve all Switching Profiles on NSX backend """ return self.nsxlib.switching_profile.list()['results'] def get_os_switching_profiles(self): """ Retrieve all Switching Profiles created from OpenStack """ sw_profiles = self.get_os_resources( self.get_switching_profiles()) if self.neutron_db: sw_profiles = [] return sw_profiles def cleanup_os_switching_profiles(self): """ Cleanup all Switching Profiles created from OpenStack plugin """ sw_profiles = self.get_os_switching_profiles() print("Number of OS SwitchingProfiles to be deleted: %s" % len(sw_profiles)) for swp in sw_profiles: try: self.nsxlib.switching_profile.delete(swp['id']) except Exception as e: print("Failed to delete Switching Profile: %s: %s" % (swp['display_name'], e)) else: print("Successfully deleted Switching Profile: %s" % swp['display_name']) def get_logical_routers(self, tier=None): """ Retrieve all the logical routers based on router type. If tier is None, it will return all logical routers. """ return self.nsxlib.logical_router.list( router_type=tier)['results'] def get_tier1_logical_routers(self): """ Retrieve all tier1 logical routers, and return only neutron ones. """ lrouters = self.get_logical_routers(tier='TIER1') if self.neutron_db: db_routers = self.neutron_db.get_logical_routers() lrouters = [lr for lr in lrouters if lr['id'] in db_routers] return lrouters def get_tier0_logical_routers(self): """ Retrieve all tier0 logical routers. """ return self.get_logical_routers(tier='TIER0') def get_os_logical_routers(self): """ Retrieve all logical routers created from Neutron NSXv3 plugin """ lrouters = self.get_tier1_logical_routers() return self.get_os_resources(lrouters) def get_logical_router_ports(self, lrouter): """ Get all logical ports attached to lrouter """ return self.nsxlib.logical_router_port.get_by_router_id(lrouter['id']) def get_os_logical_router_ports(self, lrouter): """ Retrieve all logical router ports created from Neutron NSXv3 plugin """ lports = self.get_logical_router_ports(lrouter) return self.get_os_resources(lports) def cleanup_logical_router_ports(self, lrouter): """ Cleanup all logical ports on a logical router """ lports = self.get_os_logical_router_ports(lrouter) for lp in lports: try: self.nsxlib.logical_router_port.delete(lp['id']) except Exception as e: print("Failed to delete logical router port %s-%s, " "and response is %s" % (lp['display_name'], lp['id'], e)) else: print("Successfully deleted logical router port %s-%s" % (lp['display_name'], lp['id'])) def cleanup_os_logical_routers(self): """ Delete all logical routers created from OpenStack To delete a logical router, we need to delete all logical ports on the router first. """ lrouters = self.get_os_logical_routers() print("Number of OS Logical Routers to be deleted: %s" % len(lrouters)) for lr in lrouters: self.cleanup_logical_router_ports(lr) self.cleanup_logical_router_vpn_sess(lr) try: self.nsxlib.logical_router.delete(lr['id']) except Exception as e: print("ERROR: Failed to delete logical router %s-%s, " "error %s" % (lr['display_name'], lr['id'], e)) else: print("Successfully deleted logical router %s-%s" % (lr['display_name'], lr['id'])) def cleanup_os_tier0_logical_ports(self): """ Delete all TIER0 logical router ports created from OpenStack """ tier0_routers = self.get_tier0_logical_routers() print("Number of TIER0 Logical Routers whos ports will be deleted: " "%s" % len(tier0_routers)) for lr in tier0_routers: self.cleanup_logical_router_ports(lr) def get_logical_dhcp_servers(self): """ Retrieve all logical DHCP servers on NSX backend """ return self.nsxlib.dhcp_server.list()['results'] def get_os_logical_dhcp_servers(self): """ Retrieve all logical DHCP servers created from OpenStack """ dhcp_servers = self.get_os_resources( self.get_logical_dhcp_servers()) if self.neutron_db: db_dhcp_servers = self.neutron_db.get_logical_dhcp_servers() dhcp_servers = [srv for srv in dhcp_servers if srv['id'] in db_dhcp_servers] return dhcp_servers def cleanup_os_logical_dhcp_servers(self): """ Cleanup all logical DHCP servers created from OpenStack plugin """ dhcp_servers = self.get_os_logical_dhcp_servers() print("Number of OS Logical DHCP Servers to be deleted: %s" % len(dhcp_servers)) for server in dhcp_servers: try: self.nsxlib.dhcp_server.delete(server['id']) except Exception as e: print("ERROR: Failed to delete logical DHCP server %s, " "error %s" % (server['display_name'], e)) else: print("Successfully deleted logical DHCP server %s" % server['display_name']) def get_os_vpn_objects(self, nsxlib_class, db_column_name): """ Retrieve all nsx vpn sessions from nsx and OpenStack """ objects = self.get_os_resources(nsxlib_class.list()['results']) if self.neutron_db: db_objects = self.neutron_db.get_vpn_objects(db_column_name) objects = [obj for obj in objects if obj['id'] in db_objects] return objects def clean_vpn_objects(self, obj_name, nsxlib_class, db_column_name): objects = self.get_os_vpn_objects(nsxlib_class, db_column_name) print("Number of VPN %(name)ss to be deleted: %(num)s" % {'name': obj_name, 'num': len(objects)}) for obj in objects: try: nsxlib_class.delete(obj['id']) except Exception as e: print("ERROR: Failed to delete vpn ipsec %(name)s %(id)s, " "error %(e)s" % {'name': obj_name, 'id': obj['id'], 'e': e}) else: print("Successfully deleted vpn ipsec %(name)s %(id)s" % {'name': obj_name, 'id': obj['id']}) def cleanup_vpnaas(self): """ Cleanup vpn/ipsec nsx objects """ if not self.nsxlib.feature_supported(nsx_constants.FEATURE_IPSEC_VPN): # no vpn support return self.clean_vpn_objects('session', self.nsxlib.vpn_ipsec.session, 'session_id') self.clean_vpn_objects('peer endpoint', self.nsxlib.vpn_ipsec.peer_endpoint, 'peer_ep_id') self.clean_vpn_objects('DPD profile', self.nsxlib.vpn_ipsec.dpd_profile, 'dpd_profile_id') self.clean_vpn_objects('IKE profile', self.nsxlib.vpn_ipsec.ike_profile, 'ike_profile_id') self.clean_vpn_objects('tunnel profile', self.nsxlib.vpn_ipsec.tunnel_profile, 'ipsec_profile_id') #NOTE(asarfaty): The vpn services are not deleted since we have 1 per # Tier-0 router, and those can be used outside of openstack too. def cleanup_logical_router_vpn_sess(self, lr): """ Cleanup the vpn local session of the logical router """ if not self.nsxlib.feature_supported(nsx_constants.FEATURE_IPSEC_VPN): # no vpn support return # find the router neutron id in its tags neutron_id = None for tag in lr['tags']: if tag.get('scope') == 'os-neutron-router-id': neutron_id = tag.get('tag') break if not neutron_id: return tags = [{'scope': 'os-neutron-router-id', 'tag': neutron_id}] ep_list = self.nsxlib.search_by_tags( tags=tags, resource_type=self.nsxlib.vpn_ipsec.local_endpoint.resource_type) if ep_list['results']: id = ep_list['results'][0]['id'] try: self.nsxlib.vpn_ipsec.local_endpoint.delete(id) except Exception as e: print("ERROR: Failed to delete vpn ipsec local endpoint %s, " "error %s" % (id, e)) else: print("Successfully deleted vpn ipsec local endpoint %s" % id) def get_os_nsx_objects(self, nsxlib_class, db_table, db_column): """ Retrieve all nsx objects of a given type from the nsx and OpenStack DB """ objects = self.get_os_resources(nsxlib_class.list()['results']) if self.neutron_db: db_objects = self.neutron_db.get_db_objects_by_table_and_column( db_table, db_column) objects = [obj for obj in objects if obj['id'] in db_objects] return objects def clean_lb_objects(self, obj_name, nsxlib_class, objects): print("Number of LB %(name)ss to be deleted: %(num)s" % {'name': obj_name, 'num': len(objects)}) for obj in objects: try: nsxlib_class.delete(obj['id']) except Exception as e: print("ERROR: Failed to delete LB %(name)s %(id)s, " "error %(e)s" % {'name': obj_name, 'id': obj['id'], 'e': e}) else: print("Successfully deleted LB %(name)s %(id)s" % {'name': obj_name, 'id': obj['id']}) def cleanup_loadbalancer(self): """ Cleanup LBaaS/Octavia loadbalancer objects """ if not self.nsxlib.feature_supported( nsx_constants.FEATURE_LOAD_BALANCER): # no LB support return # lb services objects = self.get_os_nsx_objects(self.nsxlib.load_balancer.service, nsx_models.NsxLbaasLoadbalancer, 'lb_service_id') self.clean_lb_objects('service', self.nsxlib.load_balancer.service, objects) # listeners objects = self.get_os_nsx_objects( self.nsxlib.load_balancer.virtual_server, nsx_models.NsxLbaasListener, 'lb_vs_id') # get a list of application profiles by their virtual servers app_profiles = [] for virtual_server in objects: lb_vs = self.nsxlib.load_balancer.virtual_server.get( virtual_server['id']) if lb_vs.get('application_profile_id'): app_profiles.append({'id': lb_vs['application_profile_id']}) self.clean_lb_objects('listener', self.nsxlib.load_balancer.virtual_server, objects) # pools objects = self.get_os_nsx_objects(self.nsxlib.load_balancer.pool, nsx_models.NsxLbaasPool, 'lb_pool_id') self.clean_lb_objects('pool', self.nsxlib.load_balancer.pool, objects) # health monitors objects = self.get_os_nsx_objects(self.nsxlib.load_balancer.monitor, nsx_models.NsxLbaasMonitor, 'lb_monitor_id') self.clean_lb_objects('monitor', self.nsxlib.load_balancer.monitor, objects) # application profiles self.clean_lb_objects('application-profile', self.nsxlib.load_balancer.application_profile, app_profiles) def cleanup_all(self): """ Cleanup steps: - Firewall sections - NSGroups - VPN objects - Loadbalancer objects - Logical router and their ports - Logical Tier 0 routers ports - Logical switch ports - Logical switches - DHCP servers - Switching profiles """ self.cleanup_os_firewall_sections() self.cleanup_os_ns_groups() self.cleanup_vpnaas() self.cleanup_loadbalancer() self.cleanup_os_logical_routers() self.cleanup_os_tier0_logical_ports() self.cleanup_os_logical_ports() self.cleanup_os_logical_switches() self.cleanup_os_logical_dhcp_servers() self.cleanup_os_switching_profiles() if __name__ == "__main__": parser = optparse.OptionParser() parser.add_option("--mgr-ip", dest="mgr_ip", help="NSX Manager IP address") parser.add_option("-u", "--username", default="admin", dest="username", help="NSX Manager username") parser.add_option("-p", "--password", default="default", dest="password", help="NSX Manager password") parser.add_option("--db-connection", default="", dest="db_connection", help=("When set, cleaning only backend resources that " "have db record.")) (options, args) = parser.parse_args() # Get NSX REST client nsx_client = NSXClient(options.mgr_ip, options.username, options.password, options.db_connection) # Clean all objects created by OpenStack nsx_client.cleanup_all()