f2ee648e89c438109f29c4ca5f53ca7e2a1ba104
[snaps.git] / snaps / openstack / utils / neutron_utils.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
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:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
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.
15 import logging
16
17 from neutronclient.common.exceptions import NotFound
18 from neutronclient.neutron.client import Client
19
20 from snaps.domain.network import (
21     Port, SecurityGroup, SecurityGroupRule, Router, InterfaceRouter, Subnet,
22     Network)
23 from snaps.domain.project import NetworkQuotas
24 from snaps.domain.vm_inst import FloatingIp
25 from snaps.openstack.utils import keystone_utils
26
27 __author__ = 'spisarski'
28
29 logger = logging.getLogger('neutron_utils')
30
31 """
32 Utilities for basic neutron API calls
33 """
34
35
36 def neutron_client(os_creds):
37     """
38     Instantiates and returns a client for communications with OpenStack's
39     Neutron server
40     :param os_creds: the credentials for connecting to the OpenStack remote API
41     :return: the client object
42     """
43     return Client(api_version=os_creds.network_api_version,
44                   session=keystone_utils.keystone_session(os_creds),
45                   region_name=os_creds.region_name)
46
47
48 def create_network(neutron, os_creds, network_settings):
49     """
50     Creates a network for OpenStack
51     :param neutron: the client
52     :param os_creds: the OpenStack credentials
53     :param network_settings: A dictionary containing the network configuration
54                              and is responsible for creating the network
55                             request JSON body
56     :return: a SNAPS-OO Network domain object if found else None
57     """
58     logger.info('Creating network with name ' + network_settings.name)
59     json_body = network_settings.dict_for_neutron(os_creds)
60     os_network = neutron.create_network(body=json_body)
61
62     if os_network:
63         network = get_network_by_id(neutron, os_network['network']['id'])
64
65         subnets = list()
66         for subnet_settings in network_settings.subnet_settings:
67             try:
68                 subnets.append(
69                     create_subnet(neutron, subnet_settings, os_creds, network))
70             except:
71                 logger.error(
72                     'Unexpected error creating subnet [%s]  for network [%s]',
73                     subnet_settings.name, network.name)
74
75                 for subnet in subnets:
76                     delete_subnet(neutron, subnet)
77
78                 delete_network(neutron, network)
79
80                 raise
81
82         return get_network_by_id(neutron, network.id)
83
84
85 def delete_network(neutron, network):
86     """
87     Deletes a network for OpenStack
88     :param neutron: the client
89     :param network: a SNAPS-OO Network domain object
90     """
91     if neutron and network:
92         if network.subnets:
93             for subnet in network.subnets:
94                 logger.info('Deleting subnet with name ' + subnet.name)
95                 try:
96                     delete_subnet(neutron, subnet)
97                 except NotFound:
98                     pass
99
100         logger.info('Deleting network with name ' + network.name)
101         neutron.delete_network(network.id)
102
103
104 def get_network(neutron, keystone, network_settings=None, network_name=None,
105                 project_name=None):
106     """
107     Returns Network SNAPS-OO domain object the first network found with
108     either the given attributes from the network_settings object if not None,
109     else the query will use just the name from the network_name parameter.
110     When the project_name is included, that will be added to the query filter.
111     :param neutron: the Neutron client
112     :param keystone: the Keystone client
113     :param network_settings: the NetworkConfig object used to create filter
114     :param network_name: the name of the network to retrieve
115     :param project_name: the name of the network's project
116     :return: a SNAPS-OO Network domain object
117     """
118     net_filter = dict()
119     if network_settings:
120         net_filter['name'] = network_settings.name
121     elif network_name:
122         net_filter['name'] = network_name
123
124     networks = neutron.list_networks(**net_filter)
125     for network, netInsts in networks.items():
126         for inst in netInsts:
127             if project_name:
128                 project = keystone_utils.get_project_by_id(
129                     keystone, inst['project_id'])
130                 if project and project.name == project_name:
131                     return __map_network(neutron, inst)
132             else:
133                 return __map_network(neutron, inst)
134
135
136 def __get_os_network_by_id(neutron, network_id):
137     """
138     Returns the OpenStack network object (dictionary) with the given ID else
139     None
140     :param neutron: the client
141     :param network_id: the id of the network to retrieve
142     :return: a SNAPS-OO Network domain object
143     """
144     networks = neutron.list_networks(**{'id': network_id})
145     for network in networks['networks']:
146         if network['id'] == network_id:
147             return network
148
149
150 def get_network_by_id(neutron, network_id):
151     """
152     Returns the SNAPS Network domain object for the given ID else None
153     :param neutron: the client
154     :param network_id: the id of the network to retrieve
155     :return: a SNAPS-OO Network domain object
156     """
157     os_network = __get_os_network_by_id(neutron, network_id)
158     if os_network:
159         return __map_network(neutron, os_network)
160
161
162 def __map_network(neutron, os_network):
163     """
164     Returns the network object (dictionary) with the given ID else None
165     :param neutron: the client
166     :param os_network: the OpenStack Network dict
167     :return: a SNAPS-OO Network domain object
168     """
169     subnets = get_subnets_by_network_id(neutron, os_network['id'])
170     os_network['subnets'] = subnets
171     return Network(**os_network)
172
173
174 def create_subnet(neutron, subnet_settings, os_creds, network):
175     """
176     Creates a network subnet for OpenStack
177     :param neutron: the client
178     :param subnet_settings: A dictionary containing the subnet configuration
179                             and is responsible for creating the subnet request
180                             JSON body
181     :param os_creds: the OpenStack credentials
182     :param network: the network object
183     :return: a SNAPS-OO Subnet domain object
184     """
185     if neutron and network and subnet_settings:
186         json_body = {'subnets': [subnet_settings.dict_for_neutron(
187             os_creds, network=network)]}
188         logger.info('Creating subnet with name ' + subnet_settings.name)
189         subnets = neutron.create_subnet(body=json_body)
190         return Subnet(**subnets['subnets'][0])
191     else:
192         raise NeutronException('Failed to create subnet')
193
194
195 def delete_subnet(neutron, subnet):
196     """
197     Deletes a network subnet for OpenStack
198     :param neutron: the client
199     :param subnet: a SNAPS-OO Subnet domain object
200     """
201     if neutron and subnet:
202         logger.info('Deleting subnet with name ' + subnet.name)
203         neutron.delete_subnet(subnet.id)
204
205
206 def get_subnet(neutron, subnet_settings=None, subnet_name=None):
207     """
208     Returns the first subnet object that fits the query else None including
209     if subnet_settings or subnet_name parameters are None.
210     :param neutron: the client
211     :param subnet_settings: the subnet settings of the object to retrieve
212     :param subnet_name: the name of the subnet to retrieve
213     :return: a SNAPS-OO Subnet domain object or None
214     """
215     sub_filter = dict()
216     if subnet_settings:
217         sub_filter['name'] = subnet_settings.name
218         sub_filter['cidr'] = subnet_settings.cidr
219         if subnet_settings.gateway_ip:
220             sub_filter['gateway_ip'] = subnet_settings.gateway_ip
221
222         if subnet_settings.enable_dhcp is not None:
223             sub_filter['enable_dhcp'] = subnet_settings.enable_dhcp
224
225         if subnet_settings.destination:
226             sub_filter['destination'] = subnet_settings.destination
227
228         if subnet_settings.nexthop:
229             sub_filter['nexthop'] = subnet_settings.nexthop
230
231         if subnet_settings.ipv6_ra_mode:
232             sub_filter['ipv6_ra_mode'] = subnet_settings.ipv6_ra_mode
233
234         if subnet_settings.ipv6_address_mode:
235             sub_filter['ipv6_address_mode'] = subnet_settings.ipv6_address_mode
236     elif subnet_name:
237         sub_filter['name'] = subnet_name
238     else:
239         return None
240
241     subnets = neutron.list_subnets(**sub_filter)
242     for subnet in subnets['subnets']:
243         return Subnet(**subnet)
244
245
246 def get_subnet_by_id(neutron, subnet_id):
247     """
248     Returns a SNAPS-OO Subnet domain object for a given ID
249     :param neutron: the OpenStack neutron client
250     :param subnet_id: the subnet ID
251     :return: a Subnet object
252     """
253     os_subnet = neutron.show_subnet(subnet_id)
254     if os_subnet and 'subnet' in os_subnet:
255         return Subnet(**os_subnet['subnet'])
256
257
258 def get_subnets_by_network(neutron, network):
259     """
260     Returns a list of SNAPS-OO Subnet domain objects
261     :param neutron: the OpenStack neutron client
262     :param network: the SNAPS-OO Network domain object
263     :return: a list of Subnet objects
264     """
265     return get_subnets_by_network_id(neutron, network.id)
266
267
268 def get_subnets_by_network_id(neutron, network_id):
269     """
270     Returns a list of SNAPS-OO Subnet domain objects
271     :param neutron: the OpenStack neutron client
272     :param network_id: the subnet's ID
273     :return: a list of Subnet objects
274     """
275     out = list()
276
277     os_subnets = neutron.list_subnets(network_id=network_id)
278
279     for os_subnet in os_subnets['subnets']:
280         out.append(Subnet(**os_subnet))
281
282     return out
283
284
285 def create_router(neutron, os_creds, router_settings):
286     """
287     Creates a router for OpenStack
288     :param neutron: the client
289     :param os_creds: the OpenStack credentials
290     :param router_settings: A dictionary containing the router configuration
291                             and is responsible for creating the subnet request
292                             JSON body
293     :return: a SNAPS-OO Router domain object
294     """
295     if neutron:
296         json_body = router_settings.dict_for_neutron(neutron, os_creds)
297         logger.info('Creating router with name - ' + router_settings.name)
298         os_router = neutron.create_router(json_body)
299         return __map_router(neutron, os_router['router'])
300     else:
301         logger.error("Failed to create router.")
302         raise NeutronException('Failed to create router')
303
304
305 def delete_router(neutron, router):
306     """
307     Deletes a router for OpenStack
308     :param neutron: the client
309     :param router: a SNAPS-OO Router domain object
310     """
311     if neutron and router:
312         logger.info('Deleting router with name - ' + router.name)
313         neutron.delete_router(router=router.id)
314
315
316 def get_router_by_id(neutron, router_id):
317     """
318     Returns a router with a given ID, else None if not found
319     :param neutron: the client
320     :param router_id: the Router ID
321     :return: a SNAPS-OO Router domain object
322     """
323     router = neutron.show_router(router_id)
324     if router:
325         return __map_router(neutron, router['router'])
326
327
328 def get_router(neutron, router_settings=None, router_name=None):
329     """
330     Returns the first router object (dictionary) found the given the settings
331     values if not None, else finds the first with the value of the router_name
332     parameter, else None
333     :param neutron: the client
334     :param router_settings: the RouterConfig object
335     :param router_name: the name of the network to retrieve
336     :return: a SNAPS-OO Router domain object
337     """
338     router_filter = dict()
339     if router_settings:
340         router_filter['name'] = router_settings.name
341         if router_settings.admin_state_up is not None:
342             router_filter['admin_state_up'] = router_settings.admin_state_up
343     elif router_name:
344         router_filter['name'] = router_name
345     else:
346         return None
347
348     routers = neutron.list_routers(**router_filter)
349
350     for routerInst in routers['routers']:
351         return __map_router(neutron, routerInst)
352
353     return None
354
355
356 def __map_router(neutron, os_router):
357     """
358     Takes an OpenStack router instance and maps it to a SNAPS Router domain
359     object
360     :param neutron: the neutron client
361     :param os_router: the OpenStack Router object
362     :return:
363     """
364     device_ports = neutron.list_ports(
365         **{'device_id': os_router['id']})['ports']
366     port_subnets = list()
367
368     # Order by create date
369     sorted_ports = sorted(
370         device_ports, key=lambda dev_port: dev_port['created_at'])
371
372     for port in sorted_ports:
373         subnets = list()
374         for fixed_ip in port['fixed_ips']:
375             subnet = get_subnet_by_id(neutron, fixed_ip['subnet_id'])
376             if subnet and subnet.network_id == port['network_id']:
377                 subnets.append(subnet)
378         port_subnets.append((Port(**port), subnets))
379
380     os_router['port_subnets'] = port_subnets
381     return Router(**os_router)
382
383
384 def add_interface_router(neutron, router, subnet=None, port=None):
385     """
386     Adds an interface router for OpenStack for either a subnet or port.
387     Exception will be raised if requesting for both.
388     :param neutron: the client
389     :param router: the router object
390     :param subnet: the subnet object
391     :param port: the port object
392     :return: the interface router object
393     """
394     if subnet and port:
395         raise NeutronException(
396             'Cannot add interface to the router. Both subnet and '
397             'port were sent in. Either or please.')
398
399     if neutron and router and (router or subnet):
400         logger.info('Adding interface to router with name ' + router.name)
401         os_intf_router = neutron.add_interface_router(
402             router=router.id, body=__create_port_json_body(subnet, port))
403         return InterfaceRouter(**os_intf_router)
404     else:
405         raise NeutronException(
406             'Unable to create interface router as neutron client,'
407             ' router or subnet were not created')
408
409
410 def remove_interface_router(neutron, router, subnet=None, port=None):
411     """
412     Removes an interface router for OpenStack
413     :param neutron: the client
414     :param router: the SNAPS-OO Router domain object
415     :param subnet: the subnet object (either subnet or port, not both)
416     :param port: the port object
417     """
418     if router:
419         try:
420             logger.info('Removing router interface from router named ' +
421                         router.name)
422             neutron.remove_interface_router(
423                 router=router.id,
424                 body=__create_port_json_body(subnet, port))
425         except NotFound as e:
426             logger.warning('Could not remove router interface. NotFound - %s',
427                            e)
428             pass
429     else:
430         logger.warning('Could not remove router interface, No router object')
431
432
433 def __create_port_json_body(subnet=None, port=None):
434     """
435     Returns the dictionary required for creating and deleting router
436     interfaces. Will only work on a subnet or port object. Will throw and
437     exception if parameters contain both or neither
438     :param subnet: the subnet object
439     :param port: the port object
440     :return: the dict
441     """
442     if subnet and port:
443         raise NeutronException(
444             'Cannot create JSON body with both subnet and port')
445     if not subnet and not port:
446         raise NeutronException(
447             'Cannot create JSON body without subnet or port')
448
449     if subnet:
450         return {"subnet_id": subnet.id}
451     else:
452         return {"port_id": port.id}
453
454
455 def create_port(neutron, os_creds, port_settings):
456     """
457     Creates a port for OpenStack
458     :param neutron: the client
459     :param os_creds: the OpenStack credentials
460     :param port_settings: the settings object for port configuration
461     :return: the SNAPS-OO Port domain object
462     """
463     json_body = port_settings.dict_for_neutron(neutron, os_creds)
464     logger.info('Creating port for network with name - %s',
465                 port_settings.network_name)
466     os_port = neutron.create_port(body=json_body)['port']
467     return Port(**os_port)
468
469
470 def delete_port(neutron, port):
471     """
472     Removes an OpenStack port
473     :param neutron: the client
474     :param port: the SNAPS-OO Port domain object
475     """
476     logger.info('Deleting port with name ' + port.name)
477     neutron.delete_port(port.id)
478
479
480 def get_port(neutron, keystone, port_settings=None, port_name=None,
481              project_name=None):
482     """
483     Returns the first port object (dictionary) found for the given query
484     :param neutron: the Neutron client
485     :param keystone: the Keystone client
486     :param port_settings: the PortConfig object used for generating the query
487     :param port_name: if port_settings is None, this name is the value to place
488                       into the query
489     :param project_name: the associated project name
490     :return: a SNAPS-OO Port domain object
491     """
492     port_filter = dict()
493
494     if port_settings:
495         if port_settings.name and len(port_settings.name) > 0:
496             port_filter['name'] = port_settings.name
497         if port_settings.admin_state_up:
498             port_filter['admin_state_up'] = port_settings.admin_state_up
499         if port_settings.device_id:
500             port_filter['device_id'] = port_settings.device_id
501         if port_settings.mac_address:
502             port_filter['mac_address'] = port_settings.mac_address
503         if port_settings.project_name:
504             project_name = port_settings.project_name
505         if port_settings.network_name:
506             network = get_network(
507                 neutron, keystone, network_name=port_settings.network_name,
508                 project_name=project_name)
509             if network:
510                 port_filter['network_id'] = network.id
511     elif port_name:
512         port_filter['name'] = port_name
513
514     ports = neutron.list_ports(**port_filter)
515     for port in ports['ports']:
516         if project_name:
517             project = keystone_utils.get_project_by_id(
518                 keystone, port['project_id'])
519             if project and project.name == project_name:
520                 return Port(**port)
521         else:
522             return Port(**port)
523     return None
524
525
526 def get_port_by_id(neutron, port_id):
527     """
528     Returns a SNAPS-OO Port domain object for the given ID or none if not found
529     :param neutron: the client
530     :param port_id: the to query
531     :return: a SNAPS-OO Port domain object or None
532     """
533     port = neutron.show_port(port_id)
534     if port:
535         return Port(**port['port'])
536     return None
537
538
539 def get_ports(neutron, network, ips=None):
540     """
541     Returns a list of SNAPS-OO Port objects for all OpenStack Port objects that
542     are associated with the 'network' parameter
543     :param neutron: the client
544     :param network: SNAPS-OO Network domain object
545     :param ips: the IPs to lookup if not None
546     :return: a SNAPS-OO Port domain object or None if not found
547     """
548     out = list()
549     ports = neutron.list_ports(**{'network_id': network.id})
550     for port in ports['ports']:
551         if ips:
552             for fixed_ips in port['fixed_ips']:
553                 if ('ip_address' in fixed_ips and
554                         fixed_ips['ip_address'] in ips) or ips is None:
555                     out.append(Port(**port))
556                     break
557         else:
558             out.append(Port(**port))
559
560     return out
561
562
563 def create_security_group(neutron, keystone, sec_grp_settings):
564     """
565     Creates a security group object in OpenStack
566     :param neutron: the Neutron client
567     :param keystone: the Keystone client
568     :param sec_grp_settings: the security group settings
569     :return: a SNAPS-OO SecurityGroup domain object
570     """
571     logger.info('Creating security group with name - %s',
572                 sec_grp_settings.name)
573     os_group = neutron.create_security_group(
574         sec_grp_settings.dict_for_neutron(keystone))
575     return __map_os_security_group(neutron, os_group['security_group'])
576
577
578 def delete_security_group(neutron, sec_grp):
579     """
580     Deletes a security group object from OpenStack
581     :param neutron: the client
582     :param sec_grp: the SNAPS SecurityGroup object to delete
583     """
584     logger.info('Deleting security group with name - %s', sec_grp.name)
585     neutron.delete_security_group(sec_grp.id)
586
587
588 def get_security_group(neutron, keystone, sec_grp_settings=None,
589                        sec_grp_name=None, project_name=None):
590     """
591     Returns the first security group for a given query. The query gets built
592     from the sec_grp_settings parameter if not None, else only the name of
593     the security group will be used, else if the query parameters are None then
594     None will be returned
595     :param neutron: the neutron client
596     :param keystone: the keystone client
597     :param sec_grp_settings: an instance of SecurityGroupConfig object
598     :param sec_grp_name: the name of security group object to retrieve
599     :param project_name: the name of the project/tentant object that owns the
600                        secuity group to retrieve
601     :return: a SNAPS-OO SecurityGroup domain object or None if not found
602     """
603
604     sec_grp_filter = dict()
605
606     if sec_grp_settings:
607         sec_grp_filter['name'] = sec_grp_settings.name
608
609         if sec_grp_settings.description:
610             sec_grp_filter['description'] = sec_grp_settings.description
611         if sec_grp_settings.project_name:
612             project_name = sec_grp_settings.project_name
613     elif sec_grp_name:
614         sec_grp_filter['name'] = sec_grp_name
615     else:
616         return None
617
618     groups = neutron.list_security_groups(**sec_grp_filter)
619     group = None
620     for group in groups['security_groups']:
621         if project_name:
622             project = keystone_utils.get_project_by_id(
623                 keystone, group['tenant_id'])
624             if project and project_name == project.name:
625                 break
626         else:
627             break
628     if group:
629         return __map_os_security_group(neutron, group)
630
631
632 def __map_os_security_group(neutron, os_sec_grp):
633     """
634     Creates a SecurityGroup SNAPS domain object from an OpenStack Security
635     Group dict
636     :param neutron: the neutron client for performing rule lookups
637     :param os_sec_grp: the OpenStack Security Group dict object
638     :return: a SecurityGroup object
639     """
640     os_sec_grp['rules'] = get_rules_by_security_group_id(
641         neutron, os_sec_grp['id'])
642     return SecurityGroup(**os_sec_grp)
643
644
645 def get_security_group_by_id(neutron, sec_grp_id):
646     """
647     Returns the first security group object of the given name else None
648     :param neutron: the client
649     :param sec_grp_id: the id of the security group to retrieve
650     :return: a SNAPS-OO SecurityGroup domain object or None if not found
651     """
652     logger.info('Retrieving security group with ID - ' + sec_grp_id)
653
654     groups = neutron.list_security_groups(**{'id': sec_grp_id})
655     for group in groups['security_groups']:
656         if group['id'] == sec_grp_id:
657             return __map_os_security_group(neutron, group)
658     return None
659
660
661 def create_security_group_rule(neutron, keystone, sec_grp_rule_settings,
662                                proj_name):
663     """
664     Creates a security group rule in OpenStack
665     :param neutron: the neutron client
666     :param keystone: the keystone client
667     :param sec_grp_rule_settings: the security group rule settings
668     :param proj_name: the default project name
669     :return: a SNAPS-OO SecurityGroupRule domain object
670     """
671     logger.info('Creating security group to security group - %s',
672                 sec_grp_rule_settings.sec_grp_name)
673     os_rule = neutron.create_security_group_rule(
674         sec_grp_rule_settings.dict_for_neutron(neutron, keystone, proj_name))
675     return SecurityGroupRule(**os_rule['security_group_rule'])
676
677
678 def delete_security_group_rule(neutron, sec_grp_rule):
679     """
680     Deletes a security group rule object from OpenStack
681     :param neutron: the client
682     :param sec_grp_rule: the SNAPS SecurityGroupRule object to delete
683     """
684     logger.info('Deleting security group rule with ID - %s',
685                 sec_grp_rule.id)
686     neutron.delete_security_group_rule(sec_grp_rule.id)
687
688
689 def get_rules_by_security_group(neutron, sec_grp):
690     """
691     Retrieves all of the rules for a given security group
692     :param neutron: the client
693     :param sec_grp: a list of SNAPS SecurityGroupRule domain objects
694     """
695     return get_rules_by_security_group_id(neutron, sec_grp.id)
696
697
698 def get_rules_by_security_group_id(neutron, sec_grp_id):
699     """
700     Retrieves all of the rules for a given security group by it's ID
701     :param neutron: the client
702     :param sec_grp_id: the ID of the associated security group
703     """
704     logger.info('Retrieving security group rules associate with the '
705                 'security group with ID - %s', sec_grp_id)
706     out = list()
707     rules = neutron.list_security_group_rules(
708         **{'security_group_id': sec_grp_id})
709     for rule in rules['security_group_rules']:
710         if rule['security_group_id'] == sec_grp_id:
711             out.append(SecurityGroupRule(**rule))
712     return out
713
714
715 def get_rule_by_id(neutron, sec_grp, rule_id):
716     """
717     Returns a SecurityGroupRule object from OpenStack
718     :param neutron: the client
719     :param sec_grp: the SNAPS SecurityGroup domain object
720     :param rule_id: the rule's ID
721     :param sec_grp: a SNAPS SecurityGroupRule domain object
722     """
723     rules = neutron.list_security_group_rules(
724         **{'security_group_id': sec_grp.id})
725     for rule in rules['security_group_rules']:
726         if rule['id'] == rule_id:
727             return SecurityGroupRule(**rule)
728     return None
729
730
731 def get_external_networks(neutron):
732     """
733     Returns a list of external OpenStack network object/dict for all external
734     networks
735     :param neutron: the client
736     :return: a list of external networks of Type SNAPS-OO domain class Network
737     """
738     out = list()
739     for network in neutron.list_networks(
740             **{'router:external': True})['networks']:
741         out.append(__map_network(neutron, network))
742     return out
743
744
745 def get_port_floating_ips(neutron, ports):
746     """
747     Returns all of the floating IPs associated with the ports returned in a
748     list of tuples where the port object is in the first position and the
749     floating IP object is in the second
750     :param neutron: the Neutron client
751     :param ports: a list of tuple 2 where index 0 is the port name and index 1
752                   is the SNAPS-OO Port object
753     :return: a list of tuple 2 (port_id, SNAPS FloatingIp) objects when ports
754              is not None else a list of FloatingIp objects
755     """
756     out = list()
757     fips = neutron.list_floatingips()
758     for fip in fips['floatingips']:
759         for port_name, port in ports:
760             if port and port.id == fip['port_id']:
761                 out.append((port.id, FloatingIp(**fip)))
762                 break
763     return out
764
765
766 def get_floating_ips(neutron):
767     """
768     Returns a list of all of the floating IPs
769     :param neutron: the Neutron client
770     """
771     out = list()
772     fips = neutron.list_floatingips()
773     for fip in fips['floatingips']:
774         out.append(FloatingIp(**fip))
775     return out
776
777
778 def create_floating_ip(neutron, keystone, ext_net_name, port_id=None):
779     """
780     Returns the floating IP object that was created with this call
781     :param neutron: the Neutron client
782     :param keystone: the Keystone client
783     :param ext_net_name: the name of the external network on which to apply the
784                          floating IP address
785     :param port_id: the ID of the port to which the floating IP will be
786                     associated
787     :return: the SNAPS FloatingIp object
788     """
789     logger.info('Creating floating ip to external network - ' + ext_net_name)
790     ext_net = get_network(neutron, keystone, network_name=ext_net_name)
791     if ext_net:
792         body = {'floatingip': {'floating_network_id': ext_net.id}}
793         if port_id:
794             body['floatingip']['port_id'] = port_id
795
796         fip = neutron.create_floatingip(body=body)
797
798         return FloatingIp(id=fip['floatingip']['id'],
799                           ip=fip['floatingip']['floating_ip_address'])
800     else:
801         raise NeutronException(
802             'Cannot create floating IP, external network not found')
803
804
805 def get_floating_ip(neutron, floating_ip):
806     """
807     Returns a floating IP object that should be identical to the floating_ip
808     parameter
809     :param neutron: the Neutron client
810     :param floating_ip: the SNAPS FloatingIp object
811     :return: hopefully the same floating IP object input
812     """
813     logger.debug('Attempting to retrieve existing floating ip with IP - %s',
814                  floating_ip.ip)
815     os_fip = __get_os_floating_ip(neutron, floating_ip)
816     if os_fip:
817         return FloatingIp(id=os_fip['id'], ip=os_fip['floating_ip_address'])
818
819
820 def __get_os_floating_ip(neutron, floating_ip):
821     """
822     Returns an OpenStack floating IP object
823     parameter
824     :param neutron: the Neutron client
825     :param floating_ip: the SNAPS FloatingIp object
826     :return: hopefully the same floating IP object input
827     """
828     logger.debug('Attempting to retrieve existing floating ip with IP - %s',
829                  floating_ip.ip)
830     fips = neutron.list_floatingips(ip=floating_ip.id)
831
832     for fip in fips['floatingips']:
833         if fip['id'] == floating_ip.id:
834             return fip
835
836
837 def delete_floating_ip(neutron, floating_ip):
838     """
839     Responsible for deleting a floating IP
840     :param neutron: the Neutron client
841     :param floating_ip: the SNAPS FloatingIp object
842     :return:
843     """
844     logger.debug('Attempting to delete existing floating ip with IP - %s',
845                  floating_ip.ip)
846     return neutron.delete_floatingip(floating_ip.id)
847
848
849 def get_network_quotas(neutron, project_id):
850     """
851     Returns a list of NetworkQuotas objects
852     :param neutron: the neutron client
853     :param project_id: the project's ID of the quotas to lookup
854     :return: an object of type NetworkQuotas or None if not found
855     """
856     quota = neutron.show_quota(project_id)
857     if quota:
858         return NetworkQuotas(**quota['quota'])
859
860
861 def update_quotas(neutron, project_id, network_quotas):
862     """
863     Updates the networking quotas for a given project
864     :param neutron: the Neutron client
865     :param project_id: the project's ID that requires quota updates
866     :param network_quotas: an object of type NetworkQuotas containing the
867                            values to update
868     :return:
869     """
870     update_body = dict()
871     update_body['security_group'] = network_quotas.security_group
872     update_body['security_group_rule'] = network_quotas.security_group_rule
873     update_body['floatingip'] = network_quotas.floatingip
874     update_body['network'] = network_quotas.network
875     update_body['port'] = network_quotas.port
876     update_body['router'] = network_quotas.router
877     update_body['subnet'] = network_quotas.subnet
878
879     return neutron.update_quota(project_id, {'quota': update_body})
880
881
882 class NeutronException(Exception):
883     """
884     Exception when calls to the Keystone client cannot be served properly
885     """