diff options
author | ahothan <ahothan@cisco.com> | 2018-04-09 16:57:25 -0700 |
---|---|---|
committer | ahothan <ahothan@cisco.com> | 2018-04-09 17:11:15 -0700 |
commit | 6226413ca6e1b4c3a52a3deeb66f8f487c2704ae (patch) | |
tree | 4d18c69efc0850af62d6ad6131b226852555648b /nfvbench | |
parent | e8a898337fcdc5e39721f187dabdfd0a51974986 (diff) |
[NFVBENCH-83] Add option to display status and to cleanup
Change-Id: If135fedee4e5ee9226a262800917c4c35bc83bc7
Signed-off-by: ahothan <ahothan@cisco.com>
Diffstat (limited to 'nfvbench')
-rw-r--r-- | nfvbench/cleanup.py | 179 | ||||
-rw-r--r-- | nfvbench/nfvbench.py | 58 |
2 files changed, 226 insertions, 11 deletions
diff --git a/nfvbench/cleanup.py b/nfvbench/cleanup.py new file mode 100644 index 0000000..246be3f --- /dev/null +++ b/nfvbench/cleanup.py @@ -0,0 +1,179 @@ +#!/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 +import time + +from neutronclient.neutron import client as nclient +from novaclient.client import Client +from novaclient.exceptions import NotFound +from tabulate import tabulate + +import credentials as credentials +from log import LOG + +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 instance_exists(self, server): + try: + self.nova_client.servers.get(server.id) + except NotFound: + return False + return True + + def get_resource_list(self): + return [["Instance", server.name, server.id] for server in self.servers] + + def clean(self): + if self.servers: + for server in self.servers: + try: + LOG.info('Deleting instance %s...', server.name) + self.nova_client.servers.delete(server.id) + except Exception: + LOG.exception("Instance %s deletion failed", server.name) + LOG.info(' Waiting for %d instances to be fully deleted...', len(self.servers)) + retry_count = 5 + len(self.servers) * 2 + while True: + retry_count -= 1 + self.servers = [server for server in self.servers if self.instance_exists(server)] + if not self.servers: + break + + if retry_count: + LOG.info(' %d yet to be deleted by Nova, retries left=%d...', + len(self.servers), retry_count) + time.sleep(2) + else: + LOG.warning(' instance deletion verification timed out: %d not removed', + len(self.servers)) + break + + +class NetworkCleaner(object): + """A cleaner for network resources.""" + + def __init__(self, neutron_client, network_names): + self.neutron_client = neutron_client + LOG.info('Discovering networks...') + all_networks = self.neutron_client.list_networks()['networks'] + self.networks = [] + for net in all_networks: + try: + network_names.remove(net['name']) + self.networks.append(net) + except ValueError: + pass + if not network_names: + break + net_ids = [net['id'] for net in self.networks] + 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] + else: + self.ports = [] + + 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]) + return res_list + + def clean(self): + 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 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 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 clean(self): + 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, 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()] + self.cleaners = [ComputeCleaner(self.nova_client, config.loop_vm_name), + FlavorCleaner(self.nova_client, config.flavor_type), + NetworkCleaner(self.neutron_client, network_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:', count) + print 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 all resources shown...") + if prompt: + answer = raw_input("Are you sure? (y/n) ") + if answer.lower() != 'y': + LOG.info("Exiting without deleting any resource") + sys.exit(0) + for cleaner in self.cleaners: + cleaner.clean() diff --git a/nfvbench/nfvbench.py b/nfvbench/nfvbench.py index 5e2de76..90b16d4 100644 --- a/nfvbench/nfvbench.py +++ b/nfvbench/nfvbench.py @@ -30,6 +30,7 @@ from pkg_resources import resource_string from __init__ import __version__ from chain_runner import ChainRunner +from cleanup import Cleaner from config import config_load from config import config_loads import credentials as credentials @@ -49,6 +50,7 @@ fluent_logger = None class NFVBench(object): """Main class of NFV benchmarking tool.""" + STATUS_OK = 'OK' STATUS_ERROR = 'ERROR' @@ -97,8 +99,8 @@ class NFVBench(object): try: if int(frame_size) < int(min_packet_size): new_frame_sizes.append(min_packet_size) - LOG.info("Adjusting frame size %s Bytes to minimum size %s Bytes due to " - + "traffic generator restriction", frame_size, min_packet_size) + LOG.info("Adjusting frame size %s Bytes to minimum size %s Bytes due to " + + "traffic generator restriction", frame_size, min_packet_size) else: new_frame_sizes.append(frame_size) except ValueError: @@ -141,7 +143,7 @@ class NFVBench(object): } def prepare_summary(self, result): - """Prepares summary of the result to print and send it to logger (eg: fluentd)""" + """Prepare summary of the result to print and send it to logger (eg: fluentd).""" global fluent_logger summary = NFVBenchSummarizer(result, fluent_logger) LOG.info(str(summary)) @@ -223,12 +225,12 @@ class NFVBench(object): if self.config.openrc_file: self.config.openrc_file = os.path.expanduser(self.config.openrc_file) - self.config.ndr_run = (not self.config.no_traffic - and 'ndr' in self.config.rate.strip().lower().split('_')) - self.config.pdr_run = (not self.config.no_traffic - and 'pdr' in self.config.rate.strip().lower().split('_')) - self.config.single_run = (not self.config.no_traffic - and not (self.config.ndr_run or self.config.pdr_run)) + self.config.ndr_run = (not self.config.no_traffic and + 'ndr' in self.config.rate.strip().lower().split('_')) + self.config.pdr_run = (not self.config.no_traffic and + 'pdr' in self.config.rate.strip().lower().split('_')) + self.config.single_run = (not self.config.no_traffic and + not (self.config.ndr_run or self.config.pdr_run)) if self.config.vlans and len(self.config.vlans) != 2: raise Exception('Number of configured VLAN IDs for VLAN tagging must be exactly 2.') @@ -252,6 +254,11 @@ class NFVBench(object): def parse_opts_from_cli(): parser = argparse.ArgumentParser() + parser.add_argument('--status', dest='status', + action='store_true', + default=None, + help='Provide NFVbench status') + parser.add_argument('-c', '--config', dest='config', action='store', help='Override default values with a config file or ' @@ -366,6 +373,16 @@ def parse_opts_from_cli(): action='store_true', help='no cleanup after run') + parser.add_argument('--cleanup', dest='cleanup', + default=None, + action='store_true', + help='Cleanup NFVbench resources (prompt to confirm)') + + parser.add_argument('--force-cleanup', dest='force_cleanup', + default=None, + action='store_true', + help='Cleanup NFVbench resources (do not prompt)') + parser.add_argument('--json', dest='json', action='store', help='store results in json format file', @@ -429,8 +446,7 @@ def load_default_config(): def override_custom_traffic(config, frame_sizes, unidir): - """Override the traffic profiles with a custom one - """ + """Override the traffic profiles with a custom one.""" if frame_sizes is not None: traffic_profile_name = "custom_traffic_profile" config.traffic_profile = [ @@ -457,6 +473,23 @@ def check_physnet(name, netattrs): raise Exception("SRIOV requires segmentation_id to be specified for the {n} network" .format(n=name)) +def status_cleanup(config, cleanup, force_cleanup): + LOG.info('Version: %s', pbr.version.VersionInfo('nfvbench').version_string_with_vcs()) + # check if another run is pending + ret_code = 0 + try: + with utils.RunLock(): + LOG.info('Status: idle') + except Exception: + LOG.info('Status: busy (run pending)') + ret_code = 1 + # check nfvbench resources + if config.openrc_file and config.service_chain != ChainType.EXT: + cleaner = Cleaner(config) + count = cleaner.show_resources() + if count and (cleanup or force_cleanup): + cleaner.clean(not force_cleanup) + sys.exit(ret_code) def main(): global fluent_logger @@ -566,6 +599,9 @@ def main(): # in a copy of the dict (config plugin still holds the original dict) config_plugin.set_config(config) + if opts.status or opts.cleanup or opts.force_cleanup: + status_cleanup(config, opts.cleanup, opts.force_cleanup) + # add file log if requested if config.log_file: log.add_file_logger(config.log_file) |