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
20 from neutronclient.neutron import client as nclient
21 from novaclient.client import Client
22 from novaclient.exceptions import NotFound
23 from tabulate import tabulate
25 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 instance_exists(self, server):
41 self.nova_client.servers.get(server.id)
46 def get_resource_list(self):
47 return [["Instance", server.name, server.id] for server in self.servers]
49 def get_cleaner_code(self):
52 def clean_needed(self, clean_options):
53 if clean_options is None:
55 code = self.get_cleaner_code()
56 return code[0] in clean_options
58 def clean(self, clean_options):
59 if self.clean_needed(clean_options):
61 for server in self.servers:
63 LOG.info('Deleting instance %s...', server.name)
64 self.nova_client.servers.delete(server.id)
66 LOG.exception("Instance %s deletion failed", server.name)
67 LOG.info(' Waiting for %d instances to be fully deleted...', len(self.servers))
68 retry_count = 15 + len(self.servers) * 5
71 self.servers = [server for server in self.servers if
72 self.instance_exists(server)]
77 LOG.info(' %d yet to be deleted by Nova, retries left=%d...',
78 len(self.servers), retry_count)
82 ' instance deletion verification time-out: %d still not deleted',
87 class NetworkCleaner(object):
88 """A cleaner for network resources."""
90 def __init__(self, neutron_client, network_name_prefixes):
91 self.neutron_client = neutron_client
92 LOG.info('Discovering networks...')
93 all_networks = self.neutron_client.list_networks()['networks']
96 for net in all_networks:
98 for prefix in network_name_prefixes:
99 if prefix and netname.startswith(prefix):
100 self.networks.append(net)
101 net_ids.append(net['id'])
104 LOG.info('Discovering ports...')
105 all_ports = self.neutron_client.list_ports()['ports']
106 self.ports = [port for port in all_ports if port['network_id'] in net_ids]
107 LOG.info('Discovering floating ips...')
108 all_floating_ips = self.neutron_client.list_floatingips()['floatingips']
109 self.floating_ips = [floating_ip for floating_ip in all_floating_ips if
110 floating_ip['floating_network_id'] in net_ids and "nfvbench" in
111 floating_ip['description']]
114 self.floating_ips = []
116 def get_resource_list(self):
117 res_list = [["Network", net['name'], net['id']] for net in self.networks]
118 res_list.extend([["Port", port['name'], port['id']] for port in self.ports])
120 [["Floating IP", floating_ip['description'], floating_ip['id']] for floating_ip in
124 def get_cleaner_code(self):
125 return "networks, ports and floating ips"
127 def clean_needed(self, clean_options):
128 if clean_options is None:
130 code = self.get_cleaner_code()
131 return code[0] in clean_options
133 def clean(self, clean_options):
134 if self.clean_needed(clean_options):
135 for port in self.ports:
136 LOG.info("Deleting port %s...", port['id'])
138 self.neutron_client.delete_port(port['id'])
140 LOG.exception("Port deletion failed")
141 for floating_ip in self.floating_ips:
142 LOG.info("Deleting floating ip %s...", floating_ip['id'])
144 self.neutron_client.delete_floatingip(floating_ip['id'])
146 LOG.exception("Floating IP deletion failed")
147 # associated subnets are automatically deleted by neutron
148 for net in self.networks:
149 LOG.info("Deleting network %s...", net['name'])
151 self.neutron_client.delete_network(net['id'])
153 LOG.exception("Network deletion failed")
156 class RouterCleaner(object):
157 """A cleaner for router resources."""
159 def __init__(self, neutron_client, router_names):
160 self.neutron_client = neutron_client
161 LOG.info('Discovering routers...')
162 all_routers = self.neutron_client.list_routers()['routers']
167 for rtr in all_routers:
168 rtrname = rtr['name']
169 for name in router_names:
171 self.routers.append(rtr)
172 rtr_ids.append(rtr['id'])
174 LOG.info('Discovering router routes for router %s...', rtr['name'])
175 all_routes = rtr['routes']
176 for route in all_routes:
177 LOG.info("destination: %s, nexthop: %s", route['destination'],
180 LOG.info('Discovering router ports for router %s...', rtr['name'])
181 self.ports.extend(self.neutron_client.list_ports(device_id=rtr['id'])['ports'])
184 def get_resource_list(self):
185 res_list = [["Router", rtr['name'], rtr['id']] for rtr in self.routers]
188 def get_cleaner_code(self):
191 def clean_needed(self, clean_options):
192 if clean_options is None:
194 code = self.get_cleaner_code()
195 return code[0] in clean_options
197 def clean(self, clean_options):
198 if self.clean_needed(clean_options):
199 # associated routes needs to be deleted before deleting routers
200 for rtr in self.routers:
201 LOG.info("Deleting routes for %s...", rtr['name'])
208 self.neutron_client.update_router(rtr['id'], body)
210 LOG.exception("Router routes deletion failed")
211 LOG.info("Deleting ports for %s...", rtr['name'])
213 for port in self.ports:
215 'port_id': port['id']
217 self.neutron_client.remove_interface_router(rtr['id'], body)
219 LOG.exception("Router ports deletion failed")
220 LOG.info("Deleting router %s...", rtr['name'])
222 self.neutron_client.delete_router(rtr['id'])
224 LOG.exception("Router deletion failed")
227 class FlavorCleaner(object):
228 """Cleaner for NFVbench flavor."""
230 def __init__(self, nova_client, name):
232 LOG.info('Discovering flavor %s...', name)
234 self.flavor = nova_client.flavors.find(name=name)
238 def get_resource_list(self):
240 return [['Flavor', self.name, self.flavor.id]]
243 def get_cleaner_code(self):
246 def clean_needed(self, clean_options):
247 if clean_options is None:
249 code = self.get_cleaner_code()
250 return code[0] in clean_options
252 def clean(self, clean_options):
253 if self.clean_needed(clean_options):
255 LOG.info("Deleting flavor %s...", self.flavor.name)
259 LOG.exception("Flavor deletion failed")
262 class Cleaner(object):
263 """Cleaner for all NFVbench resources."""
265 def __init__(self, config):
266 cred = credentials.Credentials(config.openrc_file, None, False)
267 session = cred.get_session()
268 self.neutron_client = nclient.Client('2.0', session=session)
269 self.nova_client = Client(2, session=session)
270 network_names = [inet['name'] for inet in config.internal_networks.values()]
271 network_names.extend([inet['name'] for inet in config.edge_networks.values()])
272 network_names.append(config.management_network['name'])
273 network_names.append(config.floating_network['name'])
274 router_names = [rtr['router_name'] for rtr in config.edge_networks.values()]
275 # add idle networks as well
276 if config.idle_networks.name:
277 network_names.append(config.idle_networks.name)
278 self.cleaners = [ComputeCleaner(self.nova_client, config.loop_vm_name),
279 FlavorCleaner(self.nova_client, config.flavor_type),
280 NetworkCleaner(self.neutron_client, network_names),
281 RouterCleaner(self.neutron_client, router_names)]
283 def show_resources(self):
284 """Show all NFVbench resources."""
285 table = [["Type", "Name", "UUID"]]
286 for cleaner in self.cleaners:
287 res_list = cleaner.get_resource_list()
289 table.extend(res_list)
290 count = len(table) - 1
292 LOG.info('Discovered %d NFVbench resources:\n%s', count,
293 tabulate(table, headers="firstrow", tablefmt="psql"))
295 LOG.info('No matching NFVbench resources found')
298 def clean(self, prompt):
299 """Clean all resources."""
300 LOG.info("NFVbench will delete resources shown...")
303 answer = input("Do you want to delete all ressources? (y/n) ")
304 if answer.lower() != 'y':
305 print("What kind of resources do you want to delete?")
307 all_option_codes = []
308 for cleaner in self.cleaners:
309 code = cleaner.get_cleaner_code()
310 print(("%s: %s" % (code[0], code)))
311 all_option += code[0]
312 all_option_codes.append(code)
313 print(("a: all resources - a shortcut for '%s'" % all_option))
314 all_option_codes.append("all resources")
316 answer_res = input(":").lower()
317 # Check only first character because answer_res can be "flavor" and it is != all
318 if answer_res[0] == "a":
319 clean_options = all_option
320 elif answer_res[0] != 'q':
321 # if user write complete code instead of shortcuts
322 # Get only first character of clean code to avoid false clean request
323 # i.e "networks and ports" and "router" have 1 letter in common and router clean
324 # will be called even if user ask for networks and ports
325 if answer_res in all_option_codes:
326 clean_options = answer_res[0]
328 clean_options = answer_res
330 LOG.info("Exiting without deleting any resource")
332 for cleaner in self.cleaners:
333 cleaner.clean(clean_options)