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