1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 # and others. All rights reserved.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain 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,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 from neutronclient.common.exceptions import NotFound
18 from neutronclient.neutron.client import Client
19 from snaps.domain.vm_inst import FloatingIp
20 from snaps.openstack.utils import keystone_utils
22 __author__ = 'spisarski'
24 logger = logging.getLogger('neutron_utils')
27 Utilities for basic neutron API calls
31 def neutron_client(os_creds):
33 Instantiates and returns a client for communications with OpenStack's
35 :param os_creds: the credentials for connecting to the OpenStack remote API
36 :return: the client object
38 return Client(api_version=os_creds.network_api_version,
39 session=keystone_utils.keystone_session(os_creds))
42 def create_network(neutron, os_creds, network_settings):
44 Creates a network for OpenStack
45 :param neutron: the client
46 :param os_creds: the OpenStack credentials
47 :param network_settings: A dictionary containing the network configuration
48 and is responsible for creating the network
50 :return: the network object
52 if neutron and network_settings:
53 logger.info('Creating network with name ' + network_settings.name)
54 json_body = network_settings.dict_for_neutron(os_creds)
55 return neutron.create_network(body=json_body)
57 logger.error("Failed to create network")
61 def delete_network(neutron, network):
63 Deletes a network for OpenStack
64 :param neutron: the client
65 :param network: the network object
67 if neutron and network:
68 logger.info('Deleting network with name ' + network['network']['name'])
69 neutron.delete_network(network['network']['id'])
72 def get_network(neutron, network_name, project_id=None):
74 Returns an object (dictionary) of the first network found with a given name
75 and project_id (if included)
76 :param neutron: the client
77 :param network_name: the name of the network to retrieve
78 :param project_id: the id of the network's project
83 net_filter['name'] = network_name
85 net_filter['project_id'] = project_id
87 networks = neutron.list_networks(**net_filter)
88 for network, netInsts in networks.items():
90 if inst.get('name') == network_name:
91 if project_id and inst.get('project_id') == project_id:
92 return {'network': inst}
94 return {'network': inst}
98 def get_network_by_id(neutron, network_id):
100 Returns the network object (dictionary) with the given ID
101 :param neutron: the client
102 :param network_id: the id of the network to retrieve
105 networks = neutron.list_networks(**{'id': network_id})
106 for network, netInsts in networks.items():
107 for inst in netInsts:
108 if inst.get('id') == network_id:
109 return {'network': inst}
113 def create_subnet(neutron, subnet_settings, os_creds, network=None):
115 Creates a network subnet for OpenStack
116 :param neutron: the client
117 :param network: the network object
118 :param subnet_settings: A dictionary containing the subnet configuration
119 and is responsible for creating the subnet request
121 :param os_creds: the OpenStack credentials
122 :return: the subnet object
124 if neutron and network and subnet_settings:
125 json_body = {'subnets': [subnet_settings.dict_for_neutron(
126 os_creds, network=network)]}
127 logger.info('Creating subnet with name ' + subnet_settings.name)
128 subnets = neutron.create_subnet(body=json_body)
129 return {'subnet': subnets['subnets'][0]}
131 logger.error("Failed to create subnet.")
135 def delete_subnet(neutron, subnet):
137 Deletes a network subnet for OpenStack
138 :param neutron: the client
139 :param subnet: the subnet object
141 if neutron and subnet:
142 logger.info('Deleting subnet with name ' + subnet['subnet']['name'])
143 neutron.delete_subnet(subnet['subnet']['id'])
146 def get_subnet_by_name(neutron, subnet_name):
148 Returns the first subnet object (dictionary) found with a given name
149 :param neutron: the client
150 :param subnet_name: the name of the network to retrieve
153 subnets = neutron.list_subnets(**{'name': subnet_name})
154 for subnet, subnetInst in subnets.items():
155 for inst in subnetInst:
156 if inst.get('name') == subnet_name:
157 return {'subnet': inst}
161 def create_router(neutron, os_creds, router_settings):
163 Creates a router for OpenStack
164 :param neutron: the client
165 :param os_creds: the OpenStack credentials
166 :param router_settings: A dictionary containing the router configuration
167 and is responsible for creating the subnet request
169 :return: the router object
172 json_body = router_settings.dict_for_neutron(neutron, os_creds)
173 logger.info('Creating router with name - ' + router_settings.name)
174 return neutron.create_router(json_body)
176 logger.error("Failed to create router.")
180 def delete_router(neutron, router):
182 Deletes a router for OpenStack
183 :param neutron: the client
184 :param router: the router object
186 if neutron and router:
187 logger.info('Deleting router with name - ' + router['router']['name'])
188 neutron.delete_router(router=router['router']['id'])
192 def get_router_by_name(neutron, router_name):
194 Returns the first router object (dictionary) found with a given name
195 :param neutron: the client
196 :param router_name: the name of the network to retrieve
199 routers = neutron.list_routers(**{'name': router_name})
200 for router, routerInst in routers.items():
201 for inst in routerInst:
202 if inst.get('name') == router_name:
203 return {'router': inst}
207 def add_interface_router(neutron, router, subnet=None, port=None):
209 Adds an interface router for OpenStack for either a subnet or port.
210 Exception will be raised if requesting for both.
211 :param neutron: the client
212 :param router: the router object
213 :param subnet: the subnet object
214 :param port: the port object
215 :return: the interface router object
218 raise Exception('Cannot add interface to the router. Both subnet and '
219 'port were sent in. Either or please.')
221 if neutron and router and (router or subnet):
222 logger.info('Adding interface to router with name ' +
223 router['router']['name'])
224 return neutron.add_interface_router(
225 router=router['router']['id'],
226 body=__create_port_json_body(subnet, port))
228 raise Exception('Unable to create interface router as neutron client,'
229 ' router or subnet were not created')
232 def remove_interface_router(neutron, router, subnet=None, port=None):
234 Removes an interface router for OpenStack
235 :param neutron: the client
236 :param router: the router object
237 :param subnet: the subnet object (either subnet or port, not both)
238 :param port: the port object
242 logger.info('Removing router interface from router named ' +
243 router['router']['name'])
244 neutron.remove_interface_router(
245 router=router['router']['id'],
246 body=__create_port_json_body(subnet, port))
247 except NotFound as e:
248 logger.warning('Could not remove router interface. NotFound - %s',
252 logger.warning('Could not remove router interface, No router object')
255 def __create_port_json_body(subnet=None, port=None):
257 Returns the dictionary required for creating and deleting router
258 interfaces. Will only work on a subnet or port object. Will throw and
259 exception if parameters contain both or neither
260 :param subnet: the subnet object
261 :param port: the port object
265 raise Exception('Cannot create JSON body with both subnet and port')
266 if not subnet and not port:
267 raise Exception('Cannot create JSON body without subnet or port')
270 return {"subnet_id": subnet['subnet']['id']}
272 return {"port_id": port['port']['id']}
275 def create_port(neutron, os_creds, port_settings):
277 Creates a port for OpenStack
278 :param neutron: the client
279 :param os_creds: the OpenStack credentials
280 :param port_settings: the settings object for port configuration
281 :return: the port object
283 json_body = port_settings.dict_for_neutron(neutron, os_creds)
284 logger.info('Creating port for network with name - %s',
285 port_settings.network_name)
286 return neutron.create_port(body=json_body)
289 def delete_port(neutron, port):
291 Removes an OpenStack port
292 :param neutron: the client
293 :param port: the port object
296 logger.info('Deleting port with name ' + port['port']['name'])
297 neutron.delete_port(port['port']['id'])
300 def get_port_by_name(neutron, port_name):
302 Returns the first port object (dictionary) found with a given name
303 :param neutron: the client
304 :param port_name: the name of the port to retrieve
307 ports = neutron.list_ports(**{'name': port_name})
308 for port in ports['ports']:
309 if port['name'] == port_name:
310 return {'port': port}
314 def create_security_group(neutron, keystone, sec_grp_settings):
316 Creates a security group object in OpenStack
317 :param neutron: the Neutron client
318 :param keystone: the Keystone client
319 :param sec_grp_settings: the security group settings
320 :return: the security group object
322 logger.info('Creating security group with name - %s',
323 sec_grp_settings.name)
324 return neutron.create_security_group(
325 sec_grp_settings.dict_for_neutron(keystone))
328 def delete_security_group(neutron, sec_grp):
330 Deletes a security group object from OpenStack
331 :param neutron: the client
332 :param sec_grp: the security group object to delete
334 logger.info('Deleting security group with name - %s',
335 sec_grp['security_group']['name'])
336 return neutron.delete_security_group(sec_grp['security_group']['id'])
339 def get_security_group(neutron, name):
341 Returns the first security group object of the given name else None
342 :param neutron: the client
343 :param name: the name of security group object to retrieve
345 logger.info('Retrieving security group with name - ' + name)
347 groups = neutron.list_security_groups(**{'name': name})
348 for group in groups['security_groups']:
349 if group['name'] == name:
350 return {'security_group': group}
354 def get_security_group_by_id(neutron, sec_grp_id):
356 Returns the first security group object of the given name else None
357 :param neutron: the client
358 :param sec_grp_id: the id of the security group to retrieve
360 logger.info('Retrieving security group with ID - ' + sec_grp_id)
362 groups = neutron.list_security_groups(**{'id': sec_grp_id})
363 for group in groups['security_groups']:
364 if group['id'] == sec_grp_id:
365 return {'security_group': group}
369 def create_security_group_rule(neutron, sec_grp_rule_settings):
371 Creates a security group object in OpenStack
372 :param neutron: the client
373 :param sec_grp_rule_settings: the security group rule settings
374 :return: the security group object
376 logger.info('Creating security group to security group - %s',
377 sec_grp_rule_settings.sec_grp_name)
378 return neutron.create_security_group_rule(
379 sec_grp_rule_settings.dict_for_neutron(neutron))
382 def delete_security_group_rule(neutron, sec_grp_rule):
384 Deletes a security group object from OpenStack
385 :param neutron: the client
386 :param sec_grp_rule: the security group rule object to delete
388 logger.info('Deleting security group rule with ID - %s',
389 sec_grp_rule['security_group_rule']['id'])
390 neutron.delete_security_group_rule(
391 sec_grp_rule['security_group_rule']['id'])
394 def get_rules_by_security_group(neutron, sec_grp):
396 Retrieves all of the rules for a given security group
397 :param neutron: the client
398 :param sec_grp: the security group object
400 logger.info('Retrieving security group rules associate with the '
401 'security group - %s', sec_grp['security_group']['name'])
403 rules = neutron.list_security_group_rules(
404 **{'security_group_id': sec_grp['security_group']['id']})
405 for rule in rules['security_group_rules']:
406 if rule['security_group_id'] == sec_grp['security_group']['id']:
407 out.append({'security_group_rule': rule})
411 def get_rule_by_id(neutron, sec_grp, rule_id):
413 Deletes a security group object from OpenStack
414 :param neutron: the client
415 :param sec_grp: the security group object
416 :param rule_id: the rule's ID
418 rules = neutron.list_security_group_rules(
419 **{'security_group_id': sec_grp['security_group']['id']})
420 for rule in rules['security_group_rules']:
421 if rule['id'] == rule_id:
422 return {'security_group_rule': rule}
426 def get_external_networks(neutron):
428 Returns a list of external OpenStack network object/dict for all external
430 :param neutron: the client
431 :return: a list of external networks (empty list if none configured)
434 for network in neutron.list_networks(
435 **{'router:external': True})['networks']:
436 out.append({'network': network})
440 def get_floating_ips(neutron):
442 Returns all of the floating IPs
443 :param neutron: the Neutron client
444 :return: a list of SNAPS FloatingIp objects
447 fips = neutron.list_floatingips()
448 for fip in fips['floatingips']:
449 out.append(FloatingIp(inst_id=fip['id'],
450 ip=fip['floating_ip_address']))
455 def create_floating_ip(neutron, ext_net_name):
457 Returns the floating IP object that was created with this call
458 :param neutron: the Neutron client
459 :param ext_net_name: the name of the external network on which to apply the
461 :return: the SNAPS FloatingIp object
463 logger.info('Creating floating ip to external network - ' + ext_net_name)
464 ext_net = get_network(neutron, ext_net_name)
466 fip = neutron.create_floatingip(
468 {'floating_network_id': ext_net['network']['id']}})
470 return FloatingIp(inst_id=fip['floatingip']['id'],
471 ip=fip['floatingip']['floating_ip_address'])
473 raise Exception('Cannot create floating IP, '
474 'external network not found')
477 def get_floating_ip(neutron, floating_ip):
479 Returns a floating IP object that should be identical to the floating_ip
481 :param neutron: the Neutron client
482 :param floating_ip: the SNAPS FloatingIp object
483 :return: hopefully the same floating IP object input
485 logger.debug('Attempting to retrieve existing floating ip with IP - %s',
487 os_fip = get_os_floating_ip(neutron, floating_ip)
490 inst_id=os_fip['id'], ip=os_fip['floating_ip_address'])
493 def get_os_floating_ip(neutron, floating_ip):
495 Returns an OpenStack floating IP object
497 :param neutron: the Neutron client
498 :param floating_ip: the SNAPS FloatingIp object
499 :return: hopefully the same floating IP object input
501 logger.debug('Attempting to retrieve existing floating ip with IP - %s',
503 fips = neutron.list_floatingips(ip=floating_ip.id)
505 for fip in fips['floatingips']:
506 if fip['id'] == floating_ip.id:
510 def delete_floating_ip(neutron, floating_ip):
512 Responsible for deleting a floating IP
513 :param neutron: the Neutron client
514 :param floating_ip: the SNAPS FloatingIp object
517 logger.debug('Attempting to delete existing floating ip with IP - %s',
519 return neutron.delete_floatingip(floating_ip.id)