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 import credentials as 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 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]
110 def get_resource_list(self):
111 res_list = [["Network", net['name'], net['id']] for net in self.networks]
112 res_list.extend([["Port", port['name'], port['id']] for port in self.ports])
115 def get_cleaner_code(self):
116 return "networks and ports"
118 def clean_needed(self, clean_options):
119 if clean_options is None:
121 code = self.get_cleaner_code()
122 return code[0] in clean_options
124 def clean(self, clean_options):
125 if self.clean_needed(clean_options):
126 for port in self.ports:
127 LOG.info("Deleting port %s...", port['id'])
129 self.neutron_client.delete_port(port['id'])
131 LOG.exception("Port deletion failed")
133 # associated subnets are automatically deleted by neutron
134 for net in self.networks:
135 LOG.info("Deleting network %s...", net['name'])
137 self.neutron_client.delete_network(net['id'])
139 LOG.exception("Network deletion failed")
142 class RouterCleaner(object):
143 """A cleaner for router resources."""
145 def __init__(self, neutron_client, router_names):
146 self.neutron_client = neutron_client
147 LOG.info('Discovering routers...')
148 all_routers = self.neutron_client.list_routers()['routers']
153 for rtr in all_routers:
154 rtrname = rtr['name']
155 for name in router_names:
157 self.routers.append(rtr)
158 rtr_ids.append(rtr['id'])
160 LOG.info('Discovering router routes for router %s...', rtr['name'])
161 all_routes = rtr['routes']
162 for route in all_routes:
163 LOG.info("destination: %s, nexthop: %s", route['destination'],
166 LOG.info('Discovering router ports for router %s...', rtr['name'])
167 self.ports.extend(self.neutron_client.list_ports(device_id=rtr['id'])['ports'])
170 def get_resource_list(self):
171 res_list = [["Router", rtr['name'], rtr['id']] for rtr in self.routers]
174 def get_cleaner_code(self):
177 def clean_needed(self, clean_options):
178 if clean_options is None:
180 code = self.get_cleaner_code()
181 return code[0] in clean_options
183 def clean(self, clean_options):
184 if self.clean_needed(clean_options):
185 # associated routes needs to be deleted before deleting routers
186 for rtr in self.routers:
187 LOG.info("Deleting routes for %s...", rtr['name'])
194 self.neutron_client.update_router(rtr['id'], body)
196 LOG.exception("Router routes deletion failed")
197 LOG.info("Deleting ports for %s...", rtr['name'])
199 for port in self.ports:
201 'port_id': port['id']
203 self.neutron_client.remove_interface_router(rtr['id'], body)
205 LOG.exception("Router ports deletion failed")
206 LOG.info("Deleting router %s...", rtr['name'])
208 self.neutron_client.delete_router(rtr['id'])
210 LOG.exception("Router deletion failed")
213 class FlavorCleaner(object):
214 """Cleaner for NFVbench flavor."""
216 def __init__(self, nova_client, name):
218 LOG.info('Discovering flavor %s...', name)
220 self.flavor = nova_client.flavors.find(name=name)
224 def get_resource_list(self):
226 return [['Flavor', self.name, self.flavor.id]]
229 def get_cleaner_code(self):
232 def clean_needed(self, clean_options):
233 if clean_options is None:
235 code = self.get_cleaner_code()
236 return code[0] in clean_options
238 def clean(self, clean_options):
239 if self.clean_needed(clean_options):
241 LOG.info("Deleting flavor %s...", self.flavor.name)
245 LOG.exception("Flavor deletion failed")
248 class Cleaner(object):
249 """Cleaner for all NFVbench resources."""
251 def __init__(self, config):
252 cred = credentials.Credentials(config.openrc_file, None, False)
253 session = cred.get_session()
254 self.neutron_client = nclient.Client('2.0', session=session)
255 self.nova_client = Client(2, session=session)
256 network_names = [inet['name'] for inet in config.internal_networks.values()]
257 network_names.extend([inet['name'] for inet in config.edge_networks.values()])
258 router_names = [rtr['router_name'] for rtr in config.edge_networks.values()]
259 # add idle networks as well
260 if config.idle_networks.name:
261 network_names.append(config.idle_networks.name)
262 self.cleaners = [ComputeCleaner(self.nova_client, config.loop_vm_name),
263 FlavorCleaner(self.nova_client, config.flavor_type),
264 NetworkCleaner(self.neutron_client, network_names),
265 RouterCleaner(self.neutron_client, router_names)]
267 def show_resources(self):
268 """Show all NFVbench resources."""
269 table = [["Type", "Name", "UUID"]]
270 for cleaner in self.cleaners:
271 res_list = cleaner.get_resource_list()
273 table.extend(res_list)
274 count = len(table) - 1
276 LOG.info('Discovered %d NFVbench resources:\n%s', count,
277 tabulate(table, headers="firstrow", tablefmt="psql"))
279 LOG.info('No matching NFVbench resources found')
282 def clean(self, prompt):
283 """Clean all resources."""
284 LOG.info("NFVbench will delete resources shown...")
287 answer = raw_input("Do you want to delete all ressources? (y/n) ")
288 if answer.lower() != 'y':
289 print "What kind of resources do you want to delete?"
291 all_option_codes = []
292 for cleaner in self.cleaners:
293 code = cleaner.get_cleaner_code()
294 print "%s: %s" % (code[0], code)
295 all_option += code[0]
296 all_option_codes.append(code)
297 print "a: all resources - a shortcut for '%s'" % all_option
298 all_option_codes.append("all resources")
300 answer_res = raw_input(":").lower()
301 # Check only first character because answer_res can be "flavor" and it is != all
302 if answer_res[0] == "a":
303 clean_options = all_option
304 elif answer_res[0] != 'q':
305 # if user write complete code instead of shortcuts
306 # Get only first character of clean code to avoid false clean request
307 # i.e "networks and ports" and "router" have 1 letter in common and router clean
308 # will be called even if user ask for networks and ports
309 if answer_res in all_option_codes:
310 clean_options = answer_res[0]
312 clean_options = answer_res
314 LOG.info("Exiting without deleting any resource")
316 for cleaner in self.cleaners:
317 cleaner.clean(clean_options)