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 SecurityGroup, SecurityGroupRule
21 from snaps.domain.vm_inst import FloatingIp
22 from snaps.openstack.utils import keystone_utils
24 __author__ = 'spisarski'
26 logger = logging.getLogger('neutron_utils')
29 Utilities for basic neutron API calls
33 def neutron_client(os_creds):
35 Instantiates and returns a client for communications with OpenStack's
37 :param os_creds: the credentials for connecting to the OpenStack remote API
38 :return: the client object
40 return Client(api_version=os_creds.network_api_version,
41 session=keystone_utils.keystone_session(os_creds))
44 def create_network(neutron, os_creds, network_settings):
46 Creates a network for OpenStack
47 :param neutron: the client
48 :param os_creds: the OpenStack credentials
49 :param network_settings: A dictionary containing the network configuration
50 and is responsible for creating the network
52 :return: the network object
54 if neutron and network_settings:
55 logger.info('Creating network with name ' + network_settings.name)
56 json_body = network_settings.dict_for_neutron(os_creds)
57 return neutron.create_network(body=json_body)
59 logger.error("Failed to create network")
63 def delete_network(neutron, network):
65 Deletes a network for OpenStack
66 :param neutron: the client
67 :param network: the network object
69 if neutron and network:
70 logger.info('Deleting network with name ' + network['network']['name'])
71 neutron.delete_network(network['network']['id'])
74 def get_network(neutron, network_name, project_id=None):
76 Returns an object (dictionary) of the first network found with a given name
77 and project_id (if included)
78 :param neutron: the client
79 :param network_name: the name of the network to retrieve
80 :param project_id: the id of the network's project
85 net_filter['name'] = network_name
87 net_filter['project_id'] = project_id
89 networks = neutron.list_networks(**net_filter)
90 for network, netInsts in networks.items():
92 if inst.get('name') == network_name:
93 if project_id and inst.get('project_id') == project_id:
94 return {'network': inst}
96 return {'network': inst}
100 def get_network_by_id(neutron, network_id):
102 Returns the network object (dictionary) with the given ID
103 :param neutron: the client
104 :param network_id: the id of the network to retrieve
107 networks = neutron.list_networks(**{'id': network_id})
108 for network, netInsts in networks.items():
109 for inst in netInsts:
110 if inst.get('id') == network_id:
111 return {'network': inst}
115 def create_subnet(neutron, subnet_settings, os_creds, network=None):
117 Creates a network subnet for OpenStack
118 :param neutron: the client
119 :param network: the network object
120 :param subnet_settings: A dictionary containing the subnet configuration
121 and is responsible for creating the subnet request
123 :param os_creds: the OpenStack credentials
124 :return: the subnet object
126 if neutron and network and subnet_settings:
127 json_body = {'subnets': [subnet_settings.dict_for_neutron(
128 os_creds, network=network)]}
129 logger.info('Creating subnet with name ' + subnet_settings.name)
130 subnets = neutron.create_subnet(body=json_body)
131 return {'subnet': subnets['subnets'][0]}
133 logger.error("Failed to create subnet.")
137 def delete_subnet(neutron, subnet):
139 Deletes a network subnet for OpenStack
140 :param neutron: the client
141 :param subnet: the subnet object
143 if neutron and subnet:
144 logger.info('Deleting subnet with name ' + subnet['subnet']['name'])
145 neutron.delete_subnet(subnet['subnet']['id'])
148 def get_subnet_by_name(neutron, subnet_name):
150 Returns the first subnet object (dictionary) found with a given name
151 :param neutron: the client
152 :param subnet_name: the name of the network to retrieve
155 subnets = neutron.list_subnets(**{'name': subnet_name})
156 for subnet, subnetInst in subnets.items():
157 for inst in subnetInst:
158 if inst.get('name') == subnet_name:
159 return {'subnet': inst}
163 def create_router(neutron, os_creds, router_settings):
165 Creates a router for OpenStack
166 :param neutron: the client
167 :param os_creds: the OpenStack credentials
168 :param router_settings: A dictionary containing the router configuration
169 and is responsible for creating the subnet request
171 :return: the router object
174 json_body = router_settings.dict_for_neutron(neutron, os_creds)
175 logger.info('Creating router with name - ' + router_settings.name)
176 return neutron.create_router(json_body)
178 logger.error("Failed to create router.")
182 def delete_router(neutron, router):
184 Deletes a router for OpenStack
185 :param neutron: the client
186 :param router: the router object
188 if neutron and router:
189 logger.info('Deleting router with name - ' + router['router']['name'])
190 neutron.delete_router(router=router['router']['id'])
194 def get_router_by_name(neutron, router_name):
196 Returns the first router object (dictionary) found with a given name
197 :param neutron: the client
198 :param router_name: the name of the network to retrieve
201 routers = neutron.list_routers(**{'name': router_name})
202 for router, routerInst in routers.items():
203 for inst in routerInst:
204 if inst.get('name') == router_name:
205 return {'router': inst}
209 def add_interface_router(neutron, router, subnet=None, port=None):
211 Adds an interface router for OpenStack for either a subnet or port.
212 Exception will be raised if requesting for both.
213 :param neutron: the client
214 :param router: the router object
215 :param subnet: the subnet object
216 :param port: the port object
217 :return: the interface router object
220 raise Exception('Cannot add interface to the router. Both subnet and '
221 'port were sent in. Either or please.')
223 if neutron and router and (router or subnet):
224 logger.info('Adding interface to router with name ' +
225 router['router']['name'])
226 return neutron.add_interface_router(
227 router=router['router']['id'],
228 body=__create_port_json_body(subnet, port))
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 router 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 ' +
245 router['router']['name'])
246 neutron.remove_interface_router(
247 router=router['router']['id'],
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['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 port 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 return neutron.create_port(body=json_body)
291 def delete_port(neutron, port):
293 Removes an OpenStack port
294 :param neutron: the client
295 :param port: the port object
298 logger.info('Deleting port with name ' + port['port']['name'])
299 neutron.delete_port(port['port']['id'])
302 def get_port_by_name(neutron, port_name):
304 Returns the first port object (dictionary) found with a given name
305 :param neutron: the client
306 :param port_name: the name of the port to retrieve
309 ports = neutron.list_ports(**{'name': port_name})
310 for port in ports['ports']:
311 if port['name'] == port_name:
312 return {'port': port}
316 def create_security_group(neutron, keystone, sec_grp_settings):
318 Creates a security group object in OpenStack
319 :param neutron: the Neutron client
320 :param keystone: the Keystone client
321 :param sec_grp_settings: the security group settings
322 :return: the security group object
324 logger.info('Creating security group with name - %s',
325 sec_grp_settings.name)
326 os_group = neutron.create_security_group(
327 sec_grp_settings.dict_for_neutron(keystone))
328 return SecurityGroup(
329 id=os_group['security_group']['id'],
330 name=os_group['security_group']['name'],
331 project_id=os_group['security_group'].get(
332 'project_id', os_group['security_group'].get('tenant_id')))
335 def delete_security_group(neutron, sec_grp):
337 Deletes a security group object from OpenStack
338 :param neutron: the client
339 :param sec_grp: the SNAPS SecurityGroup object to delete
341 logger.info('Deleting security group with name - %s', sec_grp.name)
342 neutron.delete_security_group(sec_grp.id)
345 def get_security_group(neutron, name):
347 Returns the first security group object of the given name else None
348 :param neutron: the client
349 :param name: the name of security group object to retrieve
351 logger.info('Retrieving security group with name - ' + name)
353 groups = neutron.list_security_groups(**{'name': name})
354 for group in groups['security_groups']:
355 if group['name'] == name:
356 return SecurityGroup(
357 id=group['id'], name=group['name'],
358 project_id=group.get('project_id', group.get('tenant_id')))
362 def get_security_group_by_id(neutron, sec_grp_id):
364 Returns the first security group object of the given name else None
365 :param neutron: the client
366 :param sec_grp_id: the id of the security group to retrieve
368 logger.info('Retrieving security group with ID - ' + sec_grp_id)
370 groups = neutron.list_security_groups(**{'id': sec_grp_id})
371 for group in groups['security_groups']:
372 if group['id'] == sec_grp_id:
373 return SecurityGroup(
374 id=group['id'], name=group['name'],
375 project_id=group.get('project_id', group.get('tenant_id')))
379 def create_security_group_rule(neutron, sec_grp_rule_settings):
381 Creates a security group object in OpenStack
382 :param neutron: the client
383 :param sec_grp_rule_settings: the security group rule settings
384 :return: the security group object
386 logger.info('Creating security group to security group - %s',
387 sec_grp_rule_settings.sec_grp_name)
388 os_rule = neutron.create_security_group_rule(
389 sec_grp_rule_settings.dict_for_neutron(neutron))
390 return SecurityGroupRule(**os_rule['security_group_rule'])
393 def delete_security_group_rule(neutron, sec_grp_rule):
395 Deletes a security group object from OpenStack
396 :param neutron: the client
397 :param sec_grp_rule: the SNAPS SecurityGroupRule object to delete
399 logger.info('Deleting security group rule with ID - %s',
401 neutron.delete_security_group_rule(sec_grp_rule.id)
404 def get_rules_by_security_group(neutron, sec_grp):
406 Retrieves all of the rules for a given security group
407 :param neutron: the client
408 :param sec_grp: the SNAPS SecurityGroup object
410 logger.info('Retrieving security group rules associate with the '
411 'security group - %s', sec_grp.name)
413 rules = neutron.list_security_group_rules(
414 **{'security_group_id': sec_grp.id})
415 for rule in rules['security_group_rules']:
416 if rule['security_group_id'] == sec_grp.id:
417 out.append(SecurityGroupRule(**rule))
421 def get_rule_by_id(neutron, sec_grp, rule_id):
423 Deletes a security group object from OpenStack
424 :param neutron: the client
425 :param sec_grp: the SNAPS SecurityGroup domain object
426 :param rule_id: the rule's ID
428 rules = neutron.list_security_group_rules(
429 **{'security_group_id': sec_grp.id})
430 for rule in rules['security_group_rules']:
431 if rule['id'] == rule_id:
432 return SecurityGroupRule(**rule)
436 def get_external_networks(neutron):
438 Returns a list of external OpenStack network object/dict for all external
440 :param neutron: the client
441 :return: a list of external networks (empty list if none configured)
444 for network in neutron.list_networks(
445 **{'router:external': True})['networks']:
446 out.append({'network': network})
450 def get_floating_ips(neutron):
452 Returns all of the floating IPs
453 :param neutron: the Neutron client
454 :return: a list of SNAPS FloatingIp objects
457 fips = neutron.list_floatingips()
458 for fip in fips['floatingips']:
459 out.append(FloatingIp(inst_id=fip['id'],
460 ip=fip['floating_ip_address']))
465 def create_floating_ip(neutron, ext_net_name):
467 Returns the floating IP object that was created with this call
468 :param neutron: the Neutron client
469 :param ext_net_name: the name of the external network on which to apply the
471 :return: the SNAPS FloatingIp object
473 logger.info('Creating floating ip to external network - ' + ext_net_name)
474 ext_net = get_network(neutron, ext_net_name)
476 fip = neutron.create_floatingip(
478 {'floating_network_id': ext_net['network']['id']}})
480 return FloatingIp(inst_id=fip['floatingip']['id'],
481 ip=fip['floatingip']['floating_ip_address'])
483 raise Exception('Cannot create floating IP, '
484 'external network not found')
487 def get_floating_ip(neutron, floating_ip):
489 Returns a floating IP object that should be identical to the floating_ip
491 :param neutron: the Neutron client
492 :param floating_ip: the SNAPS FloatingIp object
493 :return: hopefully the same floating IP object input
495 logger.debug('Attempting to retrieve existing floating ip with IP - %s',
497 os_fip = get_os_floating_ip(neutron, floating_ip)
500 inst_id=os_fip['id'], ip=os_fip['floating_ip_address'])
503 def get_os_floating_ip(neutron, floating_ip):
505 Returns an OpenStack floating IP object
507 :param neutron: the Neutron client
508 :param floating_ip: the SNAPS FloatingIp object
509 :return: hopefully the same floating IP object input
511 logger.debug('Attempting to retrieve existing floating ip with IP - %s',
513 fips = neutron.list_floatingips(ip=floating_ip.id)
515 for fip in fips['floatingips']:
516 if fip['id'] == floating_ip.id:
520 def delete_floating_ip(neutron, floating_ip):
522 Responsible for deleting a floating IP
523 :param neutron: the Neutron client
524 :param floating_ip: the SNAPS FloatingIp object
527 logger.debug('Attempting to delete existing floating ip with IP - %s',
529 return neutron.delete_floatingip(floating_ip.id)