summaryrefslogtreecommitdiffstats
path: root/nfvbench
diff options
context:
space:
mode:
authorahothan <ahothan@cisco.com>2018-04-09 16:57:25 -0700
committerahothan <ahothan@cisco.com>2018-04-09 17:11:15 -0700
commit6226413ca6e1b4c3a52a3deeb66f8f487c2704ae (patch)
tree4d18c69efc0850af62d6ad6131b226852555648b /nfvbench
parente8a898337fcdc5e39721f187dabdfd0a51974986 (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.py179
-rw-r--r--nfvbench/nfvbench.py58
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)