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
20 from snaps.domain.network import (
21 Port, SecurityGroup, SecurityGroupRule, Router, InterfaceRouter)
22 from snaps.domain.vm_inst import FloatingIp
23 from snaps.openstack.utils import keystone_utils
25 __author__ = 'spisarski'
27 logger = logging.getLogger('neutron_utils')
30 Utilities for basic neutron API calls
34 def neutron_client(os_creds):
36 Instantiates and returns a client for communications with OpenStack's
38 :param os_creds: the credentials for connecting to the OpenStack remote API
39 :return: the client object
41 return Client(api_version=os_creds.network_api_version,
42 session=keystone_utils.keystone_session(os_creds))
45 def create_network(neutron, os_creds, network_settings):
47 Creates a network for OpenStack
48 :param neutron: the client
49 :param os_creds: the OpenStack credentials
50 :param network_settings: A dictionary containing the network configuration
51 and is responsible for creating the network
53 :return: the network object
55 if neutron and network_settings:
56 logger.info('Creating network with name ' + network_settings.name)
57 json_body = network_settings.dict_for_neutron(os_creds)
58 return neutron.create_network(body=json_body)
60 logger.error("Failed to create network")
64 def delete_network(neutron, network):
66 Deletes a network for OpenStack
67 :param neutron: the client
68 :param network: the network object
70 if neutron and network:
71 logger.info('Deleting network with name ' + network['network']['name'])
72 neutron.delete_network(network['network']['id'])
75 def get_network(neutron, network_name, project_id=None):
77 Returns an object (dictionary) of the first network found with a given name
78 and project_id (if included)
79 :param neutron: the client
80 :param network_name: the name of the network to retrieve
81 :param project_id: the id of the network's project
86 net_filter['name'] = network_name
88 net_filter['project_id'] = project_id
90 networks = neutron.list_networks(**net_filter)
91 for network, netInsts in networks.items():
93 if inst.get('name') == network_name:
94 if project_id and inst.get('project_id') == project_id:
95 return {'network': inst}
97 return {'network': inst}
101 def get_network_by_id(neutron, network_id):
103 Returns the network object (dictionary) with the given ID
104 :param neutron: the client
105 :param network_id: the id of the network to retrieve
108 networks = neutron.list_networks(**{'id': network_id})
109 for network, netInsts in networks.items():
110 for inst in netInsts:
111 if inst.get('id') == network_id:
112 return {'network': inst}
116 def create_subnet(neutron, subnet_settings, os_creds, network=None):
118 Creates a network subnet for OpenStack
119 :param neutron: the client
120 :param network: the network object
121 :param subnet_settings: A dictionary containing the subnet configuration
122 and is responsible for creating the subnet request
124 :param os_creds: the OpenStack credentials
125 :return: the subnet object
127 if neutron and network and subnet_settings:
128 json_body = {'subnets': [subnet_settings.dict_for_neutron(
129 os_creds, network=network)]}
130 logger.info('Creating subnet with name ' + subnet_settings.name)
131 subnets = neutron.create_subnet(body=json_body)
132 return {'subnet': subnets['subnets'][0]}
134 logger.error("Failed to create subnet.")
138 def delete_subnet(neutron, subnet):
140 Deletes a network subnet for OpenStack
141 :param neutron: the client
142 :param subnet: the subnet object
144 if neutron and subnet:
145 logger.info('Deleting subnet with name ' + subnet['subnet']['name'])
146 neutron.delete_subnet(subnet['subnet']['id'])
149 def get_subnet_by_name(neutron, subnet_name):
151 Returns the first subnet object (dictionary) found with a given name
152 :param neutron: the client
153 :param subnet_name: the name of the network to retrieve
156 subnets = neutron.list_subnets(**{'name': subnet_name})
157 for subnet, subnetInst in subnets.items():
158 for inst in subnetInst:
159 if inst.get('name') == subnet_name:
160 return {'subnet': inst}
164 def create_router(neutron, os_creds, router_settings):
166 Creates a router for OpenStack
167 :param neutron: the client
168 :param os_creds: the OpenStack credentials
169 :param router_settings: A dictionary containing the router configuration
170 and is responsible for creating the subnet request
172 :return: a SNAPS-OO Router domain object
175 json_body = router_settings.dict_for_neutron(neutron, os_creds)
176 logger.info('Creating router with name - ' + router_settings.name)
177 os_router = neutron.create_router(json_body)
178 return Router(**os_router['router'])
180 logger.error("Failed to create router.")
184 def delete_router(neutron, router):
186 Deletes a router for OpenStack
187 :param neutron: the client
188 :param router: a SNAPS-OO Router domain object
190 if neutron and router:
191 logger.info('Deleting router with name - ' + router.name)
192 neutron.delete_router(router=router.id)
195 def get_router_by_name(neutron, router_name):
197 Returns the first router object (dictionary) found with a given name
198 :param neutron: the client
199 :param router_name: the name of the network to retrieve
200 :return: a SNAPS-OO Router domain object
202 routers = neutron.list_routers(**{'name': router_name})
203 for router, routerInst in routers.items():
204 for inst in routerInst:
205 if inst.get('name') == router_name:
206 return Router(**inst)
210 def add_interface_router(neutron, router, subnet=None, port=None):
212 Adds an interface router for OpenStack for either a subnet or port.
213 Exception will be raised if requesting for both.
214 :param neutron: the client
215 :param router: the router object
216 :param subnet: the subnet object
217 :param port: the port object
218 :return: the interface router object
221 raise Exception('Cannot add interface to the router. Both subnet and '
222 'port were sent in. Either or please.')
224 if neutron and router and (router or subnet):
225 logger.info('Adding interface to router with name ' + router.name)
226 os_intf_router = neutron.add_interface_router(
227 router=router.id, body=__create_port_json_body(subnet, port))
228 return InterfaceRouter(**os_intf_router)
230 raise Exception('Unable to create interface router as neutron client,'
231 ' router or subnet were not created')
234 def remove_interface_router(neutron, router, subnet=None, port=None):
236 Removes an interface router for OpenStack
237 :param neutron: the client
238 :param router: the SNAPS-OO Router domain object
239 :param subnet: the subnet object (either subnet or port, not both)
240 :param port: the port object
244 logger.info('Removing router interface from router named ' +
246 neutron.remove_interface_router(
248 body=__create_port_json_body(subnet, port))
249 except NotFound as e:
250 logger.warning('Could not remove router interface. NotFound - %s',
254 logger.warning('Could not remove router interface, No router object')
257 def __create_port_json_body(subnet=None, port=None):
259 Returns the dictionary required for creating and deleting router
260 interfaces. Will only work on a subnet or port object. Will throw and
261 exception if parameters contain both or neither
262 :param subnet: the subnet object
263 :param port: the port object
267 raise Exception('Cannot create JSON body with both subnet and port')
268 if not subnet and not port:
269 raise Exception('Cannot create JSON body without subnet or port')
272 return {"subnet_id": subnet['subnet']['id']}
274 return {"port_id": port.id}
277 def create_port(neutron, os_creds, port_settings):
279 Creates a port for OpenStack
280 :param neutron: the client
281 :param os_creds: the OpenStack credentials
282 :param port_settings: the settings object for port configuration
283 :return: the SNAPS-OO Port domain object
285 json_body = port_settings.dict_for_neutron(neutron, os_creds)
286 logger.info('Creating port for network with name - %s',
287 port_settings.network_name)
288 os_port = neutron.create_port(body=json_body)['port']
289 return Port(name=os_port['name'], id=os_port['id'],
290 ips=os_port['fixed_ips'],
291 mac_address=os_port['mac_address'],
292 allowed_address_pairs=os_port['allowed_address_pairs'])
295 def delete_port(neutron, port):
297 Removes an OpenStack port
298 :param neutron: the client
299 :param port: the SNAPS-OO Port domain object
302 logger.info('Deleting port with name ' + port.name)
303 neutron.delete_port(port.id)
306 def get_port_by_name(neutron, port_name):
308 Returns the first port object (dictionary) found with a given name
309 :param neutron: the client
310 :param port_name: the name of the port to retrieve
313 ports = neutron.list_ports(**{'name': port_name})
314 for port in ports['ports']:
315 if port['name'] == port_name:
316 return Port(name=port['name'], id=port['id'],
317 ips=port['fixed_ips'], mac_address=port['mac_address'])
321 def create_security_group(neutron, keystone, sec_grp_settings):
323 Creates a security group object in OpenStack
324 :param neutron: the Neutron client
325 :param keystone: the Keystone client
326 :param sec_grp_settings: the security group settings
327 :return: the security group object
329 logger.info('Creating security group with name - %s',
330 sec_grp_settings.name)
331 os_group = neutron.create_security_group(
332 sec_grp_settings.dict_for_neutron(keystone))
333 return SecurityGroup(
334 id=os_group['security_group']['id'],
335 name=os_group['security_group']['name'],
336 project_id=os_group['security_group'].get(
337 'project_id', os_group['security_group'].get('tenant_id')))
340 def delete_security_group(neutron, sec_grp):
342 Deletes a security group object from OpenStack
343 :param neutron: the client
344 :param sec_grp: the SNAPS SecurityGroup object to delete
346 logger.info('Deleting security group with name - %s', sec_grp.name)
347 neutron.delete_security_group(sec_grp.id)
350 def get_security_group(neutron, name):
352 Returns the first security group object of the given name else None
353 :param neutron: the client
354 :param name: the name of security group object to retrieve
356 logger.info('Retrieving security group with name - ' + name)
358 groups = neutron.list_security_groups(**{'name': name})
359 for group in groups['security_groups']:
360 if group['name'] == name:
361 return SecurityGroup(
362 id=group['id'], name=group['name'],
363 project_id=group.get('project_id', group.get('tenant_id')))
367 def get_security_group_by_id(neutron, sec_grp_id):
369 Returns the first security group object of the given name else None
370 :param neutron: the client
371 :param sec_grp_id: the id of the security group to retrieve
373 logger.info('Retrieving security group with ID - ' + sec_grp_id)
375 groups = neutron.list_security_groups(**{'id': sec_grp_id})
376 for group in groups['security_groups']:
377 if group['id'] == sec_grp_id:
378 return SecurityGroup(
379 id=group['id'], name=group['name'],
380 project_id=group.get('project_id', group.get('tenant_id')))
384 def create_security_group_rule(neutron, sec_grp_rule_settings):
386 Creates a security group object in OpenStack
387 :param neutron: the client
388 :param sec_grp_rule_settings: the security group rule settings
389 :return: the security group object
391 logger.info('Creating security group to security group - %s',
392 sec_grp_rule_settings.sec_grp_name)
393 os_rule = neutron.create_security_group_rule(
394 sec_grp_rule_settings.dict_for_neutron(neutron))
395 return SecurityGroupRule(**os_rule['security_group_rule'])
398 def delete_security_group_rule(neutron, sec_grp_rule):
400 Deletes a security group object from OpenStack
401 :param neutron: the client
402 :param sec_grp_rule: the SNAPS SecurityGroupRule object to delete
404 logger.info('Deleting security group rule with ID - %s',
406 neutron.delete_security_group_rule(sec_grp_rule.id)
409 def get_rules_by_security_group(neutron, sec_grp):
411 Retrieves all of the rules for a given security group
412 :param neutron: the client
413 :param sec_grp: the SNAPS SecurityGroup object
415 logger.info('Retrieving security group rules associate with the '
416 'security group - %s', sec_grp.name)
418 rules = neutron.list_security_group_rules(
419 **{'security_group_id': sec_grp.id})
420 for rule in rules['security_group_rules']:
421 if rule['security_group_id'] == sec_grp.id:
422 out.append(SecurityGroupRule(**rule))
426 def get_rule_by_id(neutron, sec_grp, rule_id):
428 Deletes a security group object from OpenStack
429 :param neutron: the client
430 :param sec_grp: the SNAPS SecurityGroup domain object
431 :param rule_id: the rule's ID
433 rules = neutron.list_security_group_rules(
434 **{'security_group_id': sec_grp.id})
435 for rule in rules['security_group_rules']:
436 if rule['id'] == rule_id:
437 return SecurityGroupRule(**rule)
441 def get_external_networks(neutron):
443 Returns a list of external OpenStack network object/dict for all external
445 :param neutron: the client
446 :return: a list of external networks (empty list if none configured)
449 for network in neutron.list_networks(
450 **{'router:external': True})['networks']:
451 out.append({'network': network})
455 def get_floating_ips(neutron):
457 Returns all of the floating IPs
458 :param neutron: the Neutron client
459 :return: a list of SNAPS FloatingIp objects
462 fips = neutron.list_floatingips()
463 for fip in fips['floatingips']:
464 out.append(FloatingIp(inst_id=fip['id'],
465 ip=fip['floating_ip_address']))
470 def create_floating_ip(neutron, ext_net_name):
472 Returns the floating IP object that was created with this call
473 :param neutron: the Neutron client
474 :param ext_net_name: the name of the external network on which to apply the
476 :return: the SNAPS FloatingIp object
478 logger.info('Creating floating ip to external network - ' + ext_net_name)
479 ext_net = get_network(neutron, ext_net_name)
481 fip = neutron.create_floatingip(
483 {'floating_network_id': ext_net['network']['id']}})
485 return FloatingIp(inst_id=fip['floatingip']['id'],
486 ip=fip['floatingip']['floating_ip_address'])
488 raise Exception('Cannot create floating IP, '
489 'external network not found')
492 def get_floating_ip(neutron, floating_ip):
494 Returns a floating IP object that should be identical to the floating_ip
496 :param neutron: the Neutron client
497 :param floating_ip: the SNAPS FloatingIp object
498 :return: hopefully the same floating IP object input
500 logger.debug('Attempting to retrieve existing floating ip with IP - %s',
502 os_fip = get_os_floating_ip(neutron, floating_ip)
505 inst_id=os_fip['id'], ip=os_fip['floating_ip_address'])
508 def get_os_floating_ip(neutron, floating_ip):
510 Returns an OpenStack floating IP object
512 :param neutron: the Neutron client
513 :param floating_ip: the SNAPS FloatingIp object
514 :return: hopefully the same floating IP object input
516 logger.debug('Attempting to retrieve existing floating ip with IP - %s',
518 fips = neutron.list_floatingips(ip=floating_ip.id)
520 for fip in fips['floatingips']:
521 if fip['id'] == floating_ip.id:
525 def delete_floating_ip(neutron, floating_ip):
527 Responsible for deleting a floating IP
528 :param neutron: the Neutron client
529 :param floating_ip: the SNAPS FloatingIp object
532 logger.debug('Attempting to delete existing floating ip with IP - %s',
534 return neutron.delete_floatingip(floating_ip.id)