diff options
Diffstat (limited to 'nfvbench/cleanup.py')
-rw-r--r-- | nfvbench/cleanup.py | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/nfvbench/cleanup.py b/nfvbench/cleanup.py new file mode 100644 index 0000000..cefdcfa --- /dev/null +++ b/nfvbench/cleanup.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python +# Copyright 2017 Cisco Systems, 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 sys + +from neutronclient.neutron import client as nclient +from novaclient.client import Client +from novaclient.exceptions import NotFound +from tabulate import tabulate + +from . import credentials +from .log import LOG +from . import utils + + +class ComputeCleaner(object): + """A cleaner for compute resources.""" + + def __init__(self, nova_client, instance_prefix): + self.nova_client = nova_client + LOG.info('Discovering instances %s...', instance_prefix) + all_servers = self.nova_client.servers.list() + self.servers = [server for server in all_servers + if server.name.startswith(instance_prefix)] + + def get_resource_list(self): + return [["Instance", server.name, server.id] for server in self.servers] + + def get_cleaner_code(self): + return "instances" + + def clean_needed(self, clean_options): + if clean_options is None: + return True + code = self.get_cleaner_code() + return code[0] in clean_options + + def clean(self, clean_options): + if self.clean_needed(clean_options): + if self.servers: + for server in self.servers: + utils.delete_server(self.nova_client, server) + utils.waiting_servers_deletion(self.nova_client, self.servers) + + +class NetworkCleaner(object): + """A cleaner for network resources.""" + + def __init__(self, neutron_client, network_name_prefixes): + self.neutron_client = neutron_client + LOG.info('Discovering networks...') + all_networks = self.neutron_client.list_networks()['networks'] + self.networks = [] + net_ids = [] + for net in all_networks: + netname = net['name'] + for prefix in network_name_prefixes: + if prefix and netname.startswith(prefix): + self.networks.append(net) + net_ids.append(net['id']) + break + if net_ids: + LOG.info('Discovering ports...') + all_ports = self.neutron_client.list_ports()['ports'] + self.ports = [port for port in all_ports if port['network_id'] in net_ids] + LOG.info('Discovering floating ips...') + all_floating_ips = self.neutron_client.list_floatingips()['floatingips'] + self.floating_ips = [floating_ip for floating_ip in all_floating_ips if + floating_ip['floating_network_id'] in net_ids and "nfvbench" in + floating_ip['description']] + else: + self.ports = [] + self.floating_ips = [] + + def get_resource_list(self): + res_list = [["Network", net['name'], net['id']] for net in self.networks] + res_list.extend([["Port", port['name'], port['id']] for port in self.ports]) + res_list.extend( + [["Floating IP", floating_ip['description'], floating_ip['id']] for floating_ip in + self.floating_ips]) + return res_list + + def get_cleaner_code(self): + return "networks, ports and floating ips" + + def clean_needed(self, clean_options): + if clean_options is None: + return True + code = self.get_cleaner_code() + return code[0] in clean_options + + def clean(self, clean_options): + if self.clean_needed(clean_options): + for port in self.ports: + LOG.info("Deleting port %s...", port['id']) + try: + self.neutron_client.delete_port(port['id']) + except Exception: + LOG.exception("Port deletion failed") + for floating_ip in self.floating_ips: + LOG.info("Deleting floating ip %s...", floating_ip['id']) + try: + self.neutron_client.delete_floatingip(floating_ip['id']) + except Exception: + LOG.exception("Floating IP deletion failed") + # associated subnets are automatically deleted by neutron + for net in self.networks: + LOG.info("Deleting network %s...", net['name']) + try: + self.neutron_client.delete_network(net['id']) + except Exception: + LOG.exception("Network deletion failed") + + +class RouterCleaner(object): + """A cleaner for router resources.""" + + def __init__(self, neutron_client, router_names): + self.neutron_client = neutron_client + LOG.info('Discovering routers...') + all_routers = self.neutron_client.list_routers()['routers'] + self.routers = [] + self.ports = [] + self.routes = [] + rtr_ids = [] + for rtr in all_routers: + rtrname = rtr['name'] + for name in router_names: + if rtrname == name: + self.routers.append(rtr) + rtr_ids.append(rtr['id']) + + LOG.info('Discovering router routes for router %s...', rtr['name']) + all_routes = rtr['routes'] + for route in all_routes: + LOG.info("destination: %s, nexthop: %s", route['destination'], + route['nexthop']) + + LOG.info('Discovering router ports for router %s...', rtr['name']) + self.ports.extend(self.neutron_client.list_ports(device_id=rtr['id'])['ports']) + break + + def get_resource_list(self): + res_list = [["Router", rtr['name'], rtr['id']] for rtr in self.routers] + return res_list + + def get_cleaner_code(self): + return "router" + + def clean_needed(self, clean_options): + if clean_options is None: + return True + code = self.get_cleaner_code() + return code[0] in clean_options + + def clean(self, clean_options): + if self.clean_needed(clean_options): + # associated routes needs to be deleted before deleting routers + for rtr in self.routers: + LOG.info("Deleting routes for %s...", rtr['name']) + try: + body = { + 'router': { + 'routes': [] + } + } + self.neutron_client.update_router(rtr['id'], body) + except Exception: + LOG.exception("Router routes deletion failed") + LOG.info("Deleting ports for %s...", rtr['name']) + try: + for port in self.ports: + body = { + 'port_id': port['id'] + } + self.neutron_client.remove_interface_router(rtr['id'], body) + except Exception: + LOG.exception("Router ports deletion failed") + LOG.info("Deleting router %s...", rtr['name']) + try: + self.neutron_client.delete_router(rtr['id']) + except Exception: + LOG.exception("Router deletion failed") + + +class FlavorCleaner(object): + """Cleaner for NFVbench flavor.""" + + def __init__(self, nova_client, name): + self.name = name + LOG.info('Discovering flavor %s...', name) + try: + self.flavor = nova_client.flavors.find(name=name) + except NotFound: + self.flavor = None + + def get_resource_list(self): + if self.flavor: + return [['Flavor', self.name, self.flavor.id]] + return None + + def get_cleaner_code(self): + return "flavor" + + def clean_needed(self, clean_options): + if clean_options is None: + return True + code = self.get_cleaner_code() + return code[0] in clean_options + + def clean(self, clean_options): + if self.clean_needed(clean_options): + if self.flavor: + LOG.info("Deleting flavor %s...", self.flavor.name) + try: + self.flavor.delete() + except Exception: + LOG.exception("Flavor deletion failed") + + +class Cleaner(object): + """Cleaner for all NFVbench resources.""" + + def __init__(self, config): + cred = credentials.Credentials(config.openrc_file, config.clouds_detail, None, False) + session = cred.get_session() + self.neutron_client = nclient.Client('2.0', session=session) + self.nova_client = Client(2, session=session) + network_names = [inet['name'] for inet in config.internal_networks.values()] + network_names.extend([inet['name'] for inet in config.edge_networks.values()]) + network_names.append(config.management_network['name']) + network_names.append(config.floating_network['name']) + router_names = [rtr['router_name'] for rtr in config.edge_networks.values()] + # add idle networks as well + if config.idle_networks.name: + network_names.append(config.idle_networks.name) + self.cleaners = [ComputeCleaner(self.nova_client, config.loop_vm_name), + FlavorCleaner(self.nova_client, config.flavor_type), + NetworkCleaner(self.neutron_client, network_names), + RouterCleaner(self.neutron_client, router_names)] + + def show_resources(self): + """Show all NFVbench resources.""" + table = [["Type", "Name", "UUID"]] + for cleaner in self.cleaners: + res_list = cleaner.get_resource_list() + if res_list: + table.extend(res_list) + count = len(table) - 1 + if count: + LOG.info('Discovered %d NFVbench resources:\n%s', count, + tabulate(table, headers="firstrow", tablefmt="psql")) + else: + LOG.info('No matching NFVbench resources found') + return count + + def clean(self, prompt): + """Clean all resources.""" + LOG.info("NFVbench will delete resources shown...") + clean_options = None + if prompt: + answer = input("Do you want to delete all ressources? (y/n) ") + if answer.lower() != 'y': + print("What kind of resources do you want to delete?") + all_option = "" + all_option_codes = [] + for cleaner in self.cleaners: + code = cleaner.get_cleaner_code() + print(("%s: %s" % (code[0], code))) + all_option += code[0] + all_option_codes.append(code) + print(("a: all resources - a shortcut for '%s'" % all_option)) + all_option_codes.append("all resources") + print("q: quit") + answer_res = input(":").lower() + # Check only first character because answer_res can be "flavor" and it is != all + if answer_res[0] == "a": + clean_options = all_option + elif answer_res[0] != 'q': + # if user write complete code instead of shortcuts + # Get only first character of clean code to avoid false clean request + # i.e "networks and ports" and "router" have 1 letter in common and router clean + # will be called even if user ask for networks and ports + if answer_res in all_option_codes: + clean_options = answer_res[0] + else: + clean_options = answer_res + else: + LOG.info("Exiting without deleting any resource") + sys.exit(0) + for cleaner in self.cleaners: + cleaner.clean(clean_options) |