2 # Copyright 2017 Cisco Systems, Inc. All rights reserved.
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
19 from neutronclient.neutron import client as nclient
20 from novaclient.client import Client
21 from novaclient.exceptions import NotFound
22 from tabulate import tabulate
24 from . import credentials
29 class ComputeCleaner(object):
30 """A cleaner for compute resources."""
32 def __init__(self, nova_client, instance_prefix):
33 self.nova_client = nova_client
34 LOG.info('Discovering instances %s...', instance_prefix)
35 all_servers = self.nova_client.servers.list()
36 self.servers = [server for server in all_servers
37 if server.name.startswith(instance_prefix)]
39 def get_resource_list(self):
40 return [["Instance", server.name, server.id] for server in self.servers]
42 def get_cleaner_code(self):
45 def clean_needed(self, clean_options):
46 if clean_options is None:
48 code = self.get_cleaner_code()
49 return code[0] in clean_options
51 def clean(self, clean_options):
52 if self.clean_needed(clean_options):
54 for server in self.servers:
55 utils.delete_server(self.nova_client, server)
56 utils.waiting_servers_deletion(self.nova_client, self.servers)
59 class NetworkCleaner(object):
60 """A cleaner for network resources."""
62 def __init__(self, neutron_client, network_name_prefixes):
63 self.neutron_client = neutron_client
64 LOG.info('Discovering networks...')
65 all_networks = self.neutron_client.list_networks()['networks']
68 for net in all_networks:
70 for prefix in network_name_prefixes:
71 if prefix and netname.startswith(prefix):
72 self.networks.append(net)
73 net_ids.append(net['id'])
76 LOG.info('Discovering ports...')
77 all_ports = self.neutron_client.list_ports()['ports']
78 self.ports = [port for port in all_ports if port['network_id'] in net_ids]
79 LOG.info('Discovering floating ips...')
80 all_floating_ips = self.neutron_client.list_floatingips()['floatingips']
81 self.floating_ips = [floating_ip for floating_ip in all_floating_ips if
82 floating_ip['floating_network_id'] in net_ids and "nfvbench" in
83 floating_ip['description']]
86 self.floating_ips = []
88 def get_resource_list(self):
89 res_list = [["Network", net['name'], net['id']] for net in self.networks]
90 res_list.extend([["Port", port['name'], port['id']] for port in self.ports])
92 [["Floating IP", floating_ip['description'], floating_ip['id']] for floating_ip in
96 def get_cleaner_code(self):
97 return "networks, ports and floating ips"
99 def clean_needed(self, clean_options):
100 if clean_options is None:
102 code = self.get_cleaner_code()
103 return code[0] in clean_options
105 def clean(self, clean_options):
106 if self.clean_needed(clean_options):
107 for port in self.ports:
108 LOG.info("Deleting port %s...", port['id'])
110 self.neutron_client.delete_port(port['id'])
112 LOG.exception("Port deletion failed")
113 for floating_ip in self.floating_ips:
114 LOG.info("Deleting floating ip %s...", floating_ip['id'])
116 self.neutron_client.delete_floatingip(floating_ip['id'])
118 LOG.exception("Floating IP deletion failed")
119 # associated subnets are automatically deleted by neutron
120 for net in self.networks:
121 LOG.info("Deleting network %s...", net['name'])
123 self.neutron_client.delete_network(net['id'])
125 LOG.exception("Network deletion failed")
128 class RouterCleaner(object):
129 """A cleaner for router resources."""
131 def __init__(self, neutron_client, router_names):
132 self.neutron_client = neutron_client
133 LOG.info('Discovering routers...')
134 all_routers = self.neutron_client.list_routers()['routers']
139 for rtr in all_routers:
140 rtrname = rtr['name']
141 for name in router_names:
143 self.routers.append(rtr)
144 rtr_ids.append(rtr['id'])
146 LOG.info('Discovering router routes for router %s...', rtr['name'])
147 all_routes = rtr['routes']
148 for route in all_routes:
149 LOG.info("destination: %s, nexthop: %s", route['destination'],
152 LOG.info('Discovering router ports for router %s...', rtr['name'])
153 self.ports.extend(self.neutron_client.list_ports(device_id=rtr['id'])['ports'])
156 def get_resource_list(self):
157 res_list = [["Router", rtr['name'], rtr['id']] for rtr in self.routers]
160 def get_cleaner_code(self):
163 def clean_needed(self, clean_options):
164 if clean_options is None:
166 code = self.get_cleaner_code()
167 return code[0] in clean_options
169 def clean(self, clean_options):
170 if self.clean_needed(clean_options):
171 # associated routes needs to be deleted before deleting routers
172 for rtr in self.routers:
173 LOG.info("Deleting routes for %s...", rtr['name'])
180 self.neutron_client.update_router(rtr['id'], body)
182 LOG.exception("Router routes deletion failed")
183 LOG.info("Deleting ports for %s...", rtr['name'])
185 for port in self.ports:
187 'port_id': port['id']
189 self.neutron_client.remove_interface_router(rtr['id'], body)
191 LOG.exception("Router ports deletion failed")
192 LOG.info("Deleting router %s...", rtr['name'])
194 self.neutron_client.delete_router(rtr['id'])
196 LOG.exception("Router deletion failed")
199 class FlavorCleaner(object):
200 """Cleaner for NFVbench flavor."""
202 def __init__(self, nova_client, name):
204 LOG.info('Discovering flavor %s...', name)
206 self.flavor = nova_client.flavors.find(name=name)
210 def get_resource_list(self):
212 return [['Flavor', self.name, self.flavor.id]]
215 def get_cleaner_code(self):
218 def clean_needed(self, clean_options):
219 if clean_options is None:
221 code = self.get_cleaner_code()
222 return code[0] in clean_options
224 def clean(self, clean_options):
225 if self.clean_needed(clean_options):
227 LOG.info("Deleting flavor %s...", self.flavor.name)
231 LOG.exception("Flavor deletion failed")
234 class Cleaner(object):
235 """Cleaner for all NFVbench resources."""
237 def __init__(self, config):
238 cred = credentials.Credentials(config.openrc_file, None, False)
239 session = cred.get_session()
240 self.neutron_client = nclient.Client('2.0', session=session)
241 self.nova_client = Client(2, session=session)
242 network_names = [inet['name'] for inet in config.internal_networks.values()]
243 network_names.extend([inet['name'] for inet in config.edge_networks.values()])
244 network_names.append(config.management_network['name'])
245 network_names.append(config.floating_network['name'])
246 router_names = [rtr['router_name'] for rtr in config.edge_networks.values()]
247 # add idle networks as well
248 if config.idle_networks.name:
249 network_names.append(config.idle_networks.name)
250 self.cleaners = [ComputeCleaner(self.nova_client, config.loop_vm_name),
251 FlavorCleaner(self.nova_client, config.flavor_type),
252 NetworkCleaner(self.neutron_client, network_names),
253 RouterCleaner(self.neutron_client, router_names)]
255 def show_resources(self):
256 """Show all NFVbench resources."""
257 table = [["Type", "Name", "UUID"]]
258 for cleaner in self.cleaners:
259 res_list = cleaner.get_resource_list()
261 table.extend(res_list)
262 count = len(table) - 1
264 LOG.info('Discovered %d NFVbench resources:\n%s', count,
265 tabulate(table, headers="firstrow", tablefmt="psql"))
267 LOG.info('No matching NFVbench resources found')
270 def clean(self, prompt):
271 """Clean all resources."""
272 LOG.info("NFVbench will delete resources shown...")
275 answer = input("Do you want to delete all ressources? (y/n) ")
276 if answer.lower() != 'y':
277 print("What kind of resources do you want to delete?")
279 all_option_codes = []
280 for cleaner in self.cleaners:
281 code = cleaner.get_cleaner_code()
282 print(("%s: %s" % (code[0], code)))
283 all_option += code[0]
284 all_option_codes.append(code)
285 print(("a: all resources - a shortcut for '%s'" % all_option))
286 all_option_codes.append("all resources")
288 answer_res = input(":").lower()
289 # Check only first character because answer_res can be "flavor" and it is != all
290 if answer_res[0] == "a":
291 clean_options = all_option
292 elif answer_res[0] != 'q':
293 # if user write complete code instead of shortcuts
294 # Get only first character of clean code to avoid false clean request
295 # i.e "networks and ports" and "router" have 1 letter in common and router clean
296 # will be called even if user ask for networks and ports
297 if answer_res in all_option_codes:
298 clean_options = answer_res[0]
300 clean_options = answer_res
302 LOG.info("Exiting without deleting any resource")
304 for cleaner in self.cleaners:
305 cleaner.clean(clean_options)