
Fixing the ID of the rules remote/local NSX groups, and adding devstck cleanup for those. In addition, this patch adds try clauses around all cleanup actions, to make sure the cleanup runs through even if some actions fail. Change-Id: I9c0d70ceb174dcf8e00ac209e1eedc4afc833cc8
270 lines
11 KiB
Python
Executable File
270 lines
11 KiB
Python
Executable File
# Copyright 2018 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 neutron.db.models import l3
|
|
from neutron.db.models import securitygroup
|
|
from neutron.db.models import segment # noqa
|
|
from neutron.db import models_v2
|
|
|
|
from vmware_nsxlib import v3
|
|
from vmware_nsxlib.v3 import config
|
|
from vmware_nsxlib.v3 import exceptions
|
|
|
|
|
|
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_security_groups(self):
|
|
return self.query_all('id', securitygroup.SecurityGroup)
|
|
|
|
def get_security_groups_rules(self):
|
|
return self.query_all('id', securitygroup.SecurityGroupRule)
|
|
|
|
def get_routers(self):
|
|
return self.query_all('id', l3.Router)
|
|
|
|
def get_networks(self):
|
|
return self.query_all('id', models_v2.Network)
|
|
|
|
def get_ports(self):
|
|
return self.query_all('id', models_v2.Port)
|
|
|
|
|
|
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.nsxpolicy = v3.NsxPolicyLib(nsxlib_config)
|
|
|
|
def get_nsx_os_domains(self):
|
|
domains = self.get_os_resources(self.nsxpolicy.domain.list())
|
|
return [d['id'] for d in domains]
|
|
|
|
def cleanup_domains(self, domains):
|
|
"""Delete all OS created NSX Policy segments ports per segment"""
|
|
for domain_id in domains:
|
|
try:
|
|
self.nsxpolicy.domain.delete(domain_id)
|
|
except exceptions.ManagerError as e:
|
|
print("Failed to delete domain %s: %s" % (domain_id, e))
|
|
|
|
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_os_nsx_groups_and_maps(self, domain_id):
|
|
"""
|
|
Retrieve all NSX policy groups & maps created from OpenStack (by tags)
|
|
If the DB is available - use only objects in the neutron DB
|
|
"""
|
|
groups = self.get_os_resources(self.nsxpolicy.group.list(domain_id))
|
|
maps = self.get_os_resources(self.nsxpolicy.comm_map.list(domain_id))
|
|
|
|
if self.neutron_db:
|
|
db_sgs = self.neutron_db.get_security_groups()
|
|
filtered_groups = [g for g in groups if g['id'] in db_sgs]
|
|
maps = [m for m in maps if m['id'] in db_sgs]
|
|
# Add groups based on SG rules local/remote ips
|
|
db_rules = self.neutron_db.get_security_groups_rules()
|
|
filtered_groups.extend([g for g in groups
|
|
if g['id'][:36] in db_rules])
|
|
groups = filtered_groups
|
|
return groups, maps
|
|
|
|
def cleanup_security_groups(self, domain_id):
|
|
"""Delete all OS created NSX Policy security group resources"""
|
|
groups, maps = self.get_os_nsx_groups_and_maps(domain_id)
|
|
print("Number of OS Communication maps of domain %s to be deleted: "
|
|
"%s" % (domain_id, len(maps)))
|
|
for m in maps:
|
|
self.nsxpolicy.comm_map.delete(domain_id, m['id'])
|
|
print("Number of OS Groups of domain %s to be deleted: "
|
|
"%s" % (domain_id, len(groups)))
|
|
for grp in groups:
|
|
try:
|
|
self.nsxpolicy.group.delete(domain_id, grp['id'])
|
|
except exceptions.ManagerError as e:
|
|
print("Failed to delete group %s: %s" % (grp['id'], e))
|
|
|
|
def get_os_nsx_tier1_routers(self):
|
|
"""
|
|
Retrieve all NSX policy routers created from OpenStack (by tags)
|
|
If the DB is available - use only objects in the neutron DB
|
|
"""
|
|
routers = self.get_os_resources(self.nsxpolicy.tier1.list())
|
|
if routers and self.neutron_db:
|
|
db_routers = self.neutron_db.get_routers()
|
|
routers = [r for r in routers if r['id'] in db_routers]
|
|
return routers
|
|
|
|
def cleanup_tier1_nat_rules(self, tier1_uuid):
|
|
rules = self.nsxpolicy.tier1_nat_rule.list(tier1_uuid)
|
|
for rule in rules:
|
|
try:
|
|
self.nsxpolicy.tier1_nat_rule.delete(tier1_uuid, rule['id'])
|
|
except exceptions.ManagerError as e:
|
|
print("Failed to delete nat rule %s: %s" % (rule['id'], e))
|
|
|
|
def cleanup_tier1_routers(self):
|
|
"""Delete all OS created NSX Policy routers"""
|
|
routers = self.get_os_nsx_tier1_routers()
|
|
print("Number of OS Tier1 routers to be deleted: %s" % len(routers))
|
|
for rtr in routers:
|
|
# remove all nat rules from this router before deletion
|
|
self.cleanup_tier1_nat_rules(rtr['id'])
|
|
try:
|
|
self.nsxpolicy.tier1.delete(rtr['id'])
|
|
except exceptions.ManagerError as e:
|
|
print("Failed to delete tier1 %s: %s" % (rtr['id'], e))
|
|
|
|
def get_os_nsx_segments(self):
|
|
"""
|
|
Retrieve all NSX policy segments created from OpenStack (by tags)
|
|
If the DB is available - use only objects in the neutron DB
|
|
"""
|
|
segments = self.get_os_resources(self.nsxpolicy.segment.list())
|
|
if segments and self.neutron_db:
|
|
db_networks = self.neutron_db.get_networks()
|
|
segments = [s for s in segments if s['id'] in db_networks]
|
|
return segments
|
|
|
|
def cleanup_segments(self):
|
|
"""Delete all OS created NSX Policy segments & ports"""
|
|
segments = self.get_os_nsx_segments()
|
|
print("Number of OS segments to be deleted: %s" % len(segments))
|
|
for s in segments:
|
|
# Delete all the ports
|
|
self.cleanup_segment_ports(s['id'])
|
|
# Disassociate from a tier1 router
|
|
try:
|
|
self.nsxpolicy.segment.update(s['id'], tier1_id=None)
|
|
self.nsxpolicy.segment.delete(s['id'])
|
|
except exceptions.ManagerError as e:
|
|
print("Failed to delete segment %s: %s" % (s['id'], e))
|
|
|
|
def get_os_nsx_segment_ports(self, segment_id):
|
|
"""
|
|
Retrieve all NSX policy segment ports created from OpenStack (by tags)
|
|
If the DB is available - use only objects in the neutron DB
|
|
"""
|
|
segment_ports = self.get_os_resources(
|
|
self.nsxpolicy.segment_port.list(segment_id))
|
|
if segment_ports and self.neutron_db:
|
|
db_ports = self.neutron_db.get_ports()
|
|
segment_ports = [s for s in segment_ports if s['id'] in db_ports]
|
|
return segment_ports
|
|
|
|
def cleanup_segment_ports(self, segment_id):
|
|
"""Delete all OS created NSX Policy segments ports per segment"""
|
|
segment_ports = self.get_os_nsx_segment_ports(segment_id)
|
|
for p in segment_ports:
|
|
try:
|
|
self.nsxpolicy.segment_port.delete(segment_id, p['id'])
|
|
except exceptions.ManagerError as e:
|
|
print("Failed to delete segment port %s: %s" % (p['id'], e))
|
|
|
|
def get_os_nsx_services(self):
|
|
"""
|
|
Retrieve all NSX policy services created from OpenStack SG rules
|
|
(by tags)
|
|
If the DB is available - use only objects in the neutron DB
|
|
"""
|
|
services = self.get_os_resources(self.nsxpolicy.service.list())
|
|
if services and self.neutron_db:
|
|
db_rules = self.neutron_db.get_security_groups_rules()
|
|
services = [s for s in services if s['id'] in db_rules]
|
|
return services
|
|
|
|
def cleanup_rules_services(self):
|
|
"""Delete all OS created NSX services"""
|
|
services = self.get_os_nsx_services()
|
|
print("Number of OS rule services to be deleted: %s" % len(services))
|
|
for srv in services:
|
|
try:
|
|
self.nsxpolicy.service.delete(srv['id'])
|
|
except exceptions.ManagerError as e:
|
|
print("Failed to delete service %s: %s" % (srv['id'], e))
|
|
|
|
def cleanup_all(self):
|
|
"""
|
|
Per domain cleanup steps:
|
|
- Security groups resources
|
|
|
|
Global cleanup steps:
|
|
- Tier1 routers
|
|
- Segments and ports
|
|
"""
|
|
domains = self.get_nsx_os_domains()
|
|
for domain_id in domains:
|
|
print("Cleaning up openstack resources from domain %s" % domain_id)
|
|
self.cleanup_security_groups(domain_id)
|
|
|
|
print("Cleaning up openstack global resources")
|
|
self.cleanup_segments()
|
|
self.cleanup_tier1_routers()
|
|
self.cleanup_rules_services()
|
|
self.cleanup_domains(domains)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
parser = optparse.OptionParser()
|
|
parser.add_option("--policy-ip", dest="policy_ip", help="NSX Policy IP "
|
|
"address")
|
|
parser.add_option("-u", "--username", default="admin", dest="username",
|
|
help="NSX Policy username")
|
|
parser.add_option("-p", "--password", default="default", dest="password",
|
|
help="NSX Policy 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.policy_ip, options.username,
|
|
options.password, options.db_connection)
|
|
# Clean all objects created by OpenStack
|
|
nsx_client.cleanup_all()
|