NSX cleanup script to clean only related resources

nsxv_cleanup and nsxv3_cleanup scripts are called by unstack.sh and
removes all backend resources, even resources which may have been
created by other devstack deployments using the same backend.
This patch fix this issue, when calling 'unstack.sh' the script will
only remove backend resources that have db record, if 'clean.sh' is
called, then previous behavior is used and all backend resources created
by openstack are removed.

To run the scripts manually, in such way that only backend resources
with db records are cleaned, one must specify '--db-connection' (e.g -
iniget /etc/neutron/neutron.conf database connection) option so the script can
query the DB.
When '--db-connection' option is not specified then all
backend resources are cleaned.

Change-Id: I2283bdb2758c303a46574296e0067f458a6eefcf
This commit is contained in:
Roey Chen 2017-01-08 06:30:55 -08:00
parent 3756e8bd6f
commit 3d24d19309
3 changed files with 170 additions and 27 deletions

View File

@ -34,8 +34,14 @@ fi
if [[ $Q_PLUGIN == 'vmware_nsx_v' ]]; then if [[ $Q_PLUGIN == 'vmware_nsx_v' ]]; then
source $dir/lib/vmware_nsx_v source $dir/lib/vmware_nsx_v
if [[ "$1" == "unstack" ]]; then if [[ "$1" == "unstack" ]]; then
python $dir/tools/nsxv_cleanup.py --vsm-ip ${NSXV_MANAGER_URI/https:\/\/} --user $NSXV_USER --password $NSXV_PASSWORD db_connection=$(iniget $NEUTRON_CONF database connection)
python $dir/tools/nsxv_cleanup.py --vsm-ip ${NSXV_MANAGER_URI/https:\/\/} --user $NSXV_USER --password $NSXV_PASSWORD --db-connection $db_connection
elif [[ "$1" == "clean" ]]; then
if is_service_enabled q-svc || is_service_enabled neutron-api; then
python $dir/tools/nsxv_cleanup.py --vsm-ip ${NSXV_MANAGER_URI/https:\/\/} --user $NSXV_USER --password $NSXV_PASSWORD
fi
fi fi
elif [[ $Q_PLUGIN == 'vmware_nsx' ]]; then elif [[ $Q_PLUGIN == 'vmware_nsx' ]]; then
source $dir/lib/vmware_nsx source $dir/lib/vmware_nsx
if [[ "$1" == "stack" && "$2" == "post-config" ]]; then if [[ "$1" == "stack" && "$2" == "post-config" ]]; then
@ -50,6 +56,7 @@ elif [[ $Q_PLUGIN == 'vmware_nsx_v3' ]]; then
if [[ "$1" == "stack" && "$2" == "post-config" ]]; then if [[ "$1" == "stack" && "$2" == "post-config" ]]; then
init_vmware_nsx_v3 init_vmware_nsx_v3
elif [[ "$1" == "unstack" ]]; then elif [[ "$1" == "unstack" ]]; then
db_connection=$(iniget $NEUTRON_CONF database connection)
stop_vmware_nsx_v3 stop_vmware_nsx_v3
# only clean up when q-svc (legacy support) or neutron-api is enabled # only clean up when q-svc (legacy support) or neutron-api is enabled
if is_service_enabled q-svc || is_service_enabled neutron-api; then if is_service_enabled q-svc || is_service_enabled neutron-api; then
@ -57,6 +64,10 @@ elif [[ $Q_PLUGIN == 'vmware_nsx_v3' ]]; then
IFS=',' IFS=','
NSX_MANAGER=($NSX_MANAGER) NSX_MANAGER=($NSX_MANAGER)
unset IFS unset IFS
python $dir/tools/nsxv3_cleanup.py --mgr-ip $NSX_MANAGER --user $NSX_USER --password $NSX_PASSWORD --db-connection $db_connection
fi
elif [[ "$1" == 'clean' ]]; then
if is_service_enabled q-svc || is_service_enabled neutron-api; then
python $dir/tools/nsxv3_cleanup.py --mgr-ip $NSX_MANAGER --user $NSX_USER --password $NSX_PASSWORD python $dir/tools/nsxv3_cleanup.py --mgr-ip $NSX_MANAGER --user $NSX_USER --password $NSX_PASSWORD
fi fi
fi fi

View File

@ -16,20 +16,56 @@
import base64 import base64
import optparse import optparse
import requests import requests
import sqlalchemy as sa
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
import six.moves.urllib.parse as urlparse import six.moves.urllib.parse as urlparse
from vmware_nsx.db import nsx_models
requests.packages.urllib3.disable_warnings() requests.packages.urllib3.disable_warnings()
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)
class NSXClient(object): class NSXClient(object):
"""Base NSX REST client""" """Base NSX REST client"""
API_VERSION = "v1" API_VERSION = "v1"
NULL_CURSOR_PREFIX = '0000' NULL_CURSOR_PREFIX = '0000'
def __init__(self, host, username, password, *args, **kwargs): def __init__(self, host, username, password, db_connection):
self.host = host self.host = host
self.username = username self.username = username
self.password = password self.password = password
@ -43,6 +79,8 @@ class NSXClient(object):
self.url = None self.url = None
self.headers = None self.headers = None
self.api_version = NSXClient.API_VERSION self.api_version = NSXClient.API_VERSION
self.neutron_db = (NeutronNsxDB(db_connection)
if db_connection else None)
self.__set_headers() self.__set_headers()
@ -169,8 +207,12 @@ class NSXClient(object):
""" """
Retrieve all logical ports created from OpenStack Retrieve all logical ports created from OpenStack
""" """
lports = self.get_logical_ports() lports = self.get_os_resources(
return self.get_os_resources(lports) 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): def update_logical_port_attachment(self, lports):
""" """
@ -188,8 +230,7 @@ class NSXClient(object):
""" """
Delete all logical ports created by OpenStack Delete all logical ports created by OpenStack
""" """
lports = self.get_logical_ports() os_lports = self.get_os_logical_ports()
os_lports = self.get_os_resources(lports)
print("Number of OS Logical Ports to be deleted: %s" % len(os_lports)) print("Number of OS Logical Ports to be deleted: %s" % len(os_lports))
# logical port vif detachment # logical port vif detachment
self.update_logical_port_attachment(os_lports) self.update_logical_port_attachment(os_lports)
@ -221,8 +262,14 @@ class NSXClient(object):
""" """
Retrieve all logical switches created from OpenStack Retrieve all logical switches created from OpenStack
""" """
lswitches = self.get_logical_switches() lswitches = self.get_os_resources(
return self.get_os_resources(lswitches) 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): def get_lswitch_ports(self, ls_id):
""" """
@ -258,8 +305,13 @@ class NSXClient(object):
""" """
Retrieve all firewall sections created from OpenStack Retrieve all firewall sections created from OpenStack
""" """
fw_sections = self.get_firewall_sections() fw_sections = self.get_os_resources(
return self.get_os_resources(fw_sections) 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 get_firewall_section_rules(self, fw_section): def get_firewall_section_rules(self, fw_section):
""" """
@ -306,8 +358,13 @@ class NSXClient(object):
""" """
Retrieve all NSGroups on NSX backend Retrieve all NSGroups on NSX backend
""" """
ns_groups = self.get_list_results(endpoint="/ns-groups") ns_groups = self.get_os_resources(
return self.get_os_resources(ns_groups) self.get_list_results(endpoint="/ns-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): def cleanup_os_ns_groups(self):
""" """
@ -333,8 +390,11 @@ class NSXClient(object):
""" """
Retrieve all Switching Profiles created from OpenStack Retrieve all Switching Profiles created from OpenStack
""" """
sw_profiles = self.get_switching_profiles() sw_profiles = self.get_os_resources(
return self.get_os_resources(sw_profiles) self.get_switching_profiles())
if self.neutron_db:
sw_profiles = []
return sw_profiles
def cleanup_os_switching_profiles(self): def cleanup_os_switching_profiles(self):
""" """
@ -362,7 +422,13 @@ class NSXClient(object):
endpoint = "/logical-routers?router_type=%s" % tier endpoint = "/logical-routers?router_type=%s" % tier
else: else:
endpoint = "/logical-routers" endpoint = "/logical-routers"
return self.get_list_results(endpoint=endpoint) lrouters = self.get_list_results(endpoint=endpoint)
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_os_logical_routers(self): def get_os_logical_routers(self):
""" """
@ -438,8 +504,14 @@ class NSXClient(object):
""" """
Retrieve all logical DHCP servers created from OpenStack Retrieve all logical DHCP servers created from OpenStack
""" """
dhcp_servers = self.get_logical_dhcp_servers() dhcp_servers = self.get_os_resources(
return self.get_os_resources(dhcp_servers) 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): def cleanup_os_logical_dhcp_servers(self):
""" """
@ -487,10 +559,13 @@ if __name__ == "__main__":
help="NSX Manager username") help="NSX Manager username")
parser.add_option("-p", "--password", default="default", dest="password", parser.add_option("-p", "--password", default="default", dest="password",
help="NSX Manager 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() (options, args) = parser.parse_args()
# Get NSX REST client # Get NSX REST client
nsx_client = NSXClient(options.mgr_ip, options.username, nsx_client = NSXClient(options.mgr_ip, options.username,
options.password) options.password, options.db_connection)
# Clean all objects created by OpenStack # Clean all objects created by OpenStack
nsx_client.cleanup_all() nsx_client.cleanup_all()

View File

@ -52,19 +52,53 @@ Tong Liu <tongl@vmware.com>
import base64 import base64
import optparse import optparse
import requests import requests
import sqlalchemy as sa
import sys import sys
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from vmware_nsx.db import nsx_models
from vmware_nsx.db import nsxv_models
requests.packages.urllib3.disable_warnings() requests.packages.urllib3.disable_warnings()
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 query_all_firewall_sections(self):
return self.query_all('ip_section_id',
nsxv_models.NsxvSecurityGroupSectionMapping)
def query_all_security_groups(self):
return self.query_all('nsx_id',
nsx_models.NeutronNsxSecurityGroupMapping)
def query_all_logical_switches(self):
return self.query_all('nsx_id',
nsx_models.NeutronNsxNetworkMapping)
def query_all_spoofguard_policies(self):
return self.query_all('policy_id',
nsxv_models.NsxvSpoofGuardPolicyNetworkMapping)
def query_all_edges(self):
return self.query_all('edge_id',
nsxv_models.NsxvRouterBinding)
class VSMClient(object): class VSMClient(object):
"""Base VSM REST client """ """Base VSM REST client """
API_VERSION = "2.0" API_VERSION = "2.0"
def __init__(self, host, username, password, *args, **kwargs): def __init__(self, host, username, password, db_connection, force):
self.force = True if 'force' in kwargs else False self.force = force
self.host = host self.host = host
self.username = username self.username = username
self.password = password self.password = password
@ -78,7 +112,8 @@ class VSMClient(object):
self.url = None self.url = None
self.headers = None self.headers = None
self.api_version = VSMClient.API_VERSION self.api_version = VSMClient.API_VERSION
self.neutron_db = (NeutronNsxDB(db_connection) if db_connection
else None)
self.__set_headers() self.__set_headers()
def __set_endpoint(self, endpoint): def __set_endpoint(self, endpoint):
@ -192,6 +227,11 @@ class VSMClient(object):
temp_lswitches = response.json()['dataPage']['data'] temp_lswitches = response.json()['dataPage']['data']
lswitches += temp_lswitches lswitches += temp_lswitches
if self.neutron_db:
db_lswitches = self.neutron_db.query_all_logical_switches()
lswitches = [ls for ls in lswitches
if ls['objectId'] in db_lswitches]
return lswitches return lswitches
def cleanup_logical_switch(self): def cleanup_logical_switch(self):
@ -224,6 +264,10 @@ class VSMClient(object):
print("ERROR: wrong response status code! Exiting...") print("ERROR: wrong response status code! Exiting...")
sys.exit() sys.exit()
if self.neutron_db:
db_sections = self.neutron_db.query_all_firewall_sections()
firewall_sections = [fws for fws in firewall_sections if fws['id']
in db_sections]
return firewall_sections return firewall_sections
def cleanup_firewall_section(self): def cleanup_firewall_section(self):
@ -254,6 +298,11 @@ class VSMClient(object):
# related to any security group created by OpenStack # related to any security group created by OpenStack
security_groups = [sg for sg in sg_all if security_groups = [sg for sg in sg_all if
sg['name'] != "Activity Monitoring Data Collection"] sg['name'] != "Activity Monitoring Data Collection"]
if self.neutron_db:
db_sgs = self.neutron_db.query_all_security_groups()
security_groups = [sg for sg in security_groups
if sg['objectId'] in db_sgs]
return security_groups return security_groups
def cleanup_security_group(self): def cleanup_security_group(self):
@ -280,6 +329,10 @@ class VSMClient(object):
sgp_all = response.json() sgp_all = response.json()
policies = [sgp for sgp in sgp_all['policies'] if policies = [sgp for sgp in sgp_all['policies'] if
sgp['name'] != 'Default Policy'] sgp['name'] != 'Default Policy']
if self.neutron_db:
db_policies = self.neutron_db.query_all_spoofguard_policies()
policies = [p for p in policies if p['policyId'] in db_policies]
return policies return policies
def cleanup_spoofguard_policies(self): def cleanup_spoofguard_policies(self):
@ -313,6 +366,10 @@ class VSMClient(object):
temp_edges = response.json()['edgePage']['data'] temp_edges = response.json()['edgePage']['data']
edges += temp_edges edges += temp_edges
if self.neutron_db:
db_edges = self.neutron_db.query_all_edges()
edges = [e for e in edges if e['id'] in db_edges]
return edges return edges
def cleanup_edge(self): def cleanup_edge(self):
@ -350,20 +407,20 @@ if __name__ == "__main__":
help="NSX Manager username") help="NSX Manager username")
parser.add_option("-p", "--password", default="default", dest="password", parser.add_option("-p", "--password", default="default", dest="password",
help="NSX Manager password") help="NSX Manager password")
parser.add_option("--db-connection", dest="db_connection", default="",
help=("When set, cleaning only backend resources that "
"have db record."))
parser.add_option("-f", "--force", dest="force", action="store_true", parser.add_option("-f", "--force", dest="force", action="store_true",
help="Force cleanup option") help="Force cleanup option")
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
print("vsm-ip: %s" % options.vsm_ip) print("vsm-ip: %s" % options.vsm_ip)
print("username: %s" % options.username) print("username: %s" % options.username)
print("password: %s" % options.password) print("password: %s" % options.password)
print("db-connection: %s" % options.db_connection)
print("force: %s" % options.force) print("force: %s" % options.force)
# Get VSM REST client # Get VSM REST client
if options.force: vsm_client = VSMClient(options.vsm_ip, options.username, options.password,
vsm_client = VSMClient(options.vsm_ip, options.username, options.db_connection, options.force)
options.password, force=options.force)
else:
vsm_client = VSMClient(options.vsm_ip, options.username,
options.password)
# Clean all objects created by OpenStack # Clean all objects created by OpenStack
vsm_client.cleanup_all() vsm_client.cleanup_all()