a2957a6dbf3451c97be82dd76a8a2228b6ceb89c
[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, os_creds=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     :param os_creds: the OpenStack credentials for retrieving the 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     if network_settings and network_settings.project_name and os_creds:
125         net_filter['project_id'] = keystone_utils.get_project(
126             os_creds=os_creds, project_name=network_settings.project_name).id
127     elif project_id:
128         net_filter['project_id'] = project_id
129
130     networks = neutron.list_networks(**net_filter)
131     for network, netInsts in networks.items():
132         for inst in netInsts:
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, project_id):
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     :param project_id: the associated project ID
294     :return: a SNAPS-OO Router domain object
295     """
296     if neutron:
297         if router_settings and router_settings.project_name:
298             keystone = keystone_utils.keystone_client(os_creds)
299             project_id = keystone_utils.get_project(
300                 keystone=keystone, project_name=router_settings.project_name)
301         json_body = router_settings.dict_for_neutron(
302             neutron, os_creds, project_id)
303         logger.info('Creating router with name - ' + router_settings.name)
304         os_router = neutron.create_router(json_body)
305         return __map_router(neutron, os_router['router'])
306     else:
307         logger.error("Failed to create router.")
308         raise NeutronException('Failed to create router')
309
310
311 def delete_router(neutron, router):
312     """
313     Deletes a router for OpenStack
314     :param neutron: the client
315     :param router: a SNAPS-OO Router domain object
316     """
317     if neutron and router:
318         logger.info('Deleting router with name - ' + router.name)
319         neutron.delete_router(router=router.id)
320
321
322 def get_router_by_id(neutron, router_id):
323     """
324     Returns a router with a given ID, else None if not found
325     :param neutron: the client
326     :param router_id: the Router ID
327     :return: a SNAPS-OO Router domain object
328     """
329     router = neutron.show_router(router_id)
330     if router:
331         return __map_router(neutron, router['router'])
332
333
334 def get_router(neutron, router_settings=None, router_name=None):
335     """
336     Returns the first router object (dictionary) found the given the settings
337     values if not None, else finds the first with the value of the router_name
338     parameter, else None
339     :param neutron: the client
340     :param router_settings: the RouterConfig object
341     :param router_name: the name of the network to retrieve
342     :return: a SNAPS-OO Router domain object
343     """
344     router_filter = dict()
345     if router_settings:
346         router_filter['name'] = router_settings.name
347         if router_settings.admin_state_up is not None:
348             router_filter['admin_state_up'] = router_settings.admin_state_up
349     elif router_name:
350         router_filter['name'] = router_name
351     else:
352         return None
353
354     routers = neutron.list_routers(**router_filter)
355
356     for routerInst in routers['routers']:
357         return __map_router(neutron, routerInst)
358
359     return None
360
361
362 def __map_router(neutron, os_router):
363     """
364     Takes an OpenStack router instance and maps it to a SNAPS Router domain
365     object
366     :param neutron: the neutron client
367     :param os_router: the OpenStack Router object
368     :return:
369     """
370     device_ports = neutron.list_ports(
371         **{'device_id': os_router['id']})['ports']
372     port_subnets = list()
373
374     # Order by create date
375     sorted_ports = sorted(
376         device_ports, key=lambda dev_port: dev_port['created_at'])
377
378     for port in sorted_ports:
379         subnets = list()
380         for fixed_ip in port['fixed_ips']:
381             subnet = get_subnet_by_id(neutron, fixed_ip['subnet_id'])
382             if subnet and subnet.network_id == port['network_id']:
383                 subnets.append(subnet)
384         port_subnets.append((Port(**port), subnets))
385
386     os_router['port_subnets'] = port_subnets
387     return Router(**os_router)
388
389
390 def add_interface_router(neutron, router, subnet=None, port=None):
391     """
392     Adds an interface router for OpenStack for either a subnet or port.
393     Exception will be raised if requesting for both.
394     :param neutron: the client
395     :param router: the router object
396     :param subnet: the subnet object
397     :param port: the port object
398     :return: the interface router object
399     """
400     if subnet and port:
401         raise NeutronException(
402             'Cannot add interface to the router. Both subnet and '
403             'port were sent in. Either or please.')
404
405     if neutron and router and (router or subnet):
406         logger.info('Adding interface to router with name ' + router.name)
407         os_intf_router = neutron.add_interface_router(
408             router=router.id, body=__create_port_json_body(subnet, port))
409         return InterfaceRouter(**os_intf_router)
410     else:
411         raise NeutronException(
412             'Unable to create interface router as neutron client,'
413             ' router or subnet were not created')
414
415
416 def remove_interface_router(neutron, router, subnet=None, port=None):
417     """
418     Removes an interface router for OpenStack
419     :param neutron: the client
420     :param router: the SNAPS-OO Router domain object
421     :param subnet: the subnet object (either subnet or port, not both)
422     :param port: the port object
423     """
424     if router:
425         try:
426             logger.info('Removing router interface from router named ' +
427                         router.name)
428             neutron.remove_interface_router(
429                 router=router.id,
430                 body=__create_port_json_body(subnet, port))
431         except NotFound as e:
432             logger.warning('Could not remove router interface. NotFound - %s',
433                            e)
434             pass
435     else:
436         logger.warning('Could not remove router interface, No router object')
437
438
439 def __create_port_json_body(subnet=None, port=None):
440     """
441     Returns the dictionary required for creating and deleting router
442     interfaces. Will only work on a subnet or port object. Will throw and
443     exception if parameters contain both or neither
444     :param subnet: the subnet object
445     :param port: the port object
446     :return: the dict
447     """
448     if subnet and port:
449         raise NeutronException(
450             'Cannot create JSON body with both subnet and port')
451     if not subnet and not port:
452         raise NeutronException(
453             'Cannot create JSON body without subnet or port')
454
455     if subnet:
456         return {"subnet_id": subnet.id}
457     else:
458         return {"port_id": port.id}
459
460
461 def create_port(neutron, os_creds, port_settings):
462     """
463     Creates a port for OpenStack
464     :param neutron: the client
465     :param os_creds: the OpenStack credentials
466     :param port_settings: the settings object for port configuration
467     :return: the SNAPS-OO Port domain object
468     """
469     json_body = port_settings.dict_for_neutron(neutron, os_creds)
470     logger.info('Creating port for network with name - %s',
471                 port_settings.network_name)
472     os_port = neutron.create_port(body=json_body)['port']
473     return Port(**os_port)
474
475
476 def delete_port(neutron, port):
477     """
478     Removes an OpenStack port
479     :param neutron: the client
480     :param port: the SNAPS-OO Port domain object
481     """
482     logger.info('Deleting port with name ' + port.name)
483     neutron.delete_port(port.id)
484
485
486 def get_port(neutron, port_settings=None, port_name=None, project_id=None):
487     """
488     Returns the first port object (dictionary) found for the given query
489     :param neutron: the client
490     :param port_settings: the PortConfig object used for generating the query
491     :param port_name: if port_settings is None, this name is the value to place
492                       into the query
493     :param project_id: the associated project ID
494     :return: a SNAPS-OO Port domain object
495     """
496     port_filter = dict()
497
498     if port_settings:
499         if port_settings.name and len(port_settings.name) > 0:
500             port_filter['name'] = port_settings.name
501         if port_settings.admin_state_up:
502             port_filter['admin_state_up'] = port_settings.admin_state_up
503         if port_settings.device_id:
504             port_filter['device_id'] = port_settings.device_id
505         if port_settings.mac_address:
506             port_filter['mac_address'] = port_settings.mac_address
507         if port_settings.network_name:
508             network = get_network(
509                 neutron, network_name=port_settings.network_name,
510                 project_id=project_id)
511             if network:
512                 port_filter['network_id'] = network.id
513     elif port_name:
514         port_filter['name'] = port_name
515
516     if project_id:
517         port_filter['project_id'] = project_id
518
519     ports = neutron.list_ports(**port_filter)
520     for port in ports['ports']:
521         return Port(**port)
522     return None
523
524
525 def get_port_by_id(neutron, port_id):
526     """
527     Returns a SNAPS-OO Port domain object for the given ID or none if not found
528     :param neutron: the client
529     :param port_id: the to query
530     :return: a SNAPS-OO Port domain object or None
531     """
532     port = neutron.show_port(port_id)
533     if port:
534         return Port(**port['port'])
535     return None
536
537
538 def get_ports(neutron, network, ips=None):
539     """
540     Returns a list of SNAPS-OO Port objects for all OpenStack Port objects that
541     are associated with the 'network' parameter
542     :param neutron: the client
543     :param network: SNAPS-OO Network domain object
544     :param ips: the IPs to lookup if not None
545     :return: a SNAPS-OO Port domain object or None if not found
546     """
547     out = list()
548     ports = neutron.list_ports(**{'network_id': network.id})
549     for port in ports['ports']:
550         if ips:
551             for fixed_ips in port['fixed_ips']:
552                 if ('ip_address' in fixed_ips and
553                         fixed_ips['ip_address'] in ips) or ips is None:
554                     out.append(Port(**port))
555                     break
556         else:
557             out.append(Port(**port))
558
559     return out
560
561
562 def create_security_group(neutron, keystone, sec_grp_settings, project_id):
563     """
564     Creates a security group object in OpenStack
565     :param neutron: the Neutron client
566     :param keystone: the Keystone client
567     :param sec_grp_settings: the security group settings
568     :param project_id: the default project to associated the security group
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, project_id))
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, sec_grp_settings=None, sec_grp_name=None,
589                        project_id=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 client
596     :param sec_grp_settings: an instance of SecurityGroupConfig object
597     :param sec_grp_name: the name of security group object to retrieve
598     :param project_id: the ID of the project/tentant object that owns the
599                        secuity group to retrieve
600     :return: a SNAPS-OO SecurityGroup domain object or None if not found
601     """
602
603     sec_grp_filter = dict()
604     if project_id:
605         sec_grp_filter['tenant_id'] = project_id
606
607     if sec_grp_settings:
608         sec_grp_filter['name'] = sec_grp_settings.name
609
610         if sec_grp_settings.description:
611             sec_grp_filter['description'] = sec_grp_settings.description
612     elif sec_grp_name:
613         sec_grp_filter['name'] = sec_grp_name
614     else:
615         return None
616
617     groups = neutron.list_security_groups(**sec_grp_filter)
618     for group in groups['security_groups']:
619         return __map_os_security_group(neutron, group)
620
621
622 def __map_os_security_group(neutron, os_sec_grp):
623     """
624     Creates a SecurityGroup SNAPS domain object from an OpenStack Security
625     Group dict
626     :param neutron: the neutron client for performing rule lookups
627     :param os_sec_grp: the OpenStack Security Group dict object
628     :return: a SecurityGroup object
629     """
630     os_sec_grp['rules'] = get_rules_by_security_group_id(
631         neutron, os_sec_grp['id'])
632     return SecurityGroup(**os_sec_grp)
633
634
635 def get_security_group_by_id(neutron, sec_grp_id):
636     """
637     Returns the first security group object of the given name else None
638     :param neutron: the client
639     :param sec_grp_id: the id of the security group to retrieve
640     :return: a SNAPS-OO SecurityGroup domain object or None if not found
641     """
642     logger.info('Retrieving security group with ID - ' + sec_grp_id)
643
644     groups = neutron.list_security_groups(**{'id': sec_grp_id})
645     for group in groups['security_groups']:
646         if group['id'] == sec_grp_id:
647             return __map_os_security_group(neutron, group)
648     return None
649
650
651 def create_security_group_rule(neutron, sec_grp_rule_settings, proj_id):
652     """
653     Creates a security group rule in OpenStack
654     :param neutron: the client
655     :param sec_grp_rule_settings: the security group rule settings
656     :param proj_id: the default project to apply to the rule settings
657     :return: a SNAPS-OO SecurityGroupRule domain object
658     """
659     logger.info('Creating security group to security group - %s',
660                 sec_grp_rule_settings.sec_grp_name)
661     os_rule = neutron.create_security_group_rule(
662         sec_grp_rule_settings.dict_for_neutron(neutron, proj_id))
663     return SecurityGroupRule(**os_rule['security_group_rule'])
664
665
666 def delete_security_group_rule(neutron, sec_grp_rule):
667     """
668     Deletes a security group rule object from OpenStack
669     :param neutron: the client
670     :param sec_grp_rule: the SNAPS SecurityGroupRule object to delete
671     """
672     logger.info('Deleting security group rule with ID - %s',
673                 sec_grp_rule.id)
674     neutron.delete_security_group_rule(sec_grp_rule.id)
675
676
677 def get_rules_by_security_group(neutron, sec_grp):
678     """
679     Retrieves all of the rules for a given security group
680     :param neutron: the client
681     :param sec_grp: a list of SNAPS SecurityGroupRule domain objects
682     """
683     return get_rules_by_security_group_id(neutron, sec_grp.id)
684
685
686 def get_rules_by_security_group_id(neutron, sec_grp_id):
687     """
688     Retrieves all of the rules for a given security group by it's ID
689     :param neutron: the client
690     :param sec_grp_id: the ID of the associated security group
691     """
692     logger.info('Retrieving security group rules associate with the '
693                 'security group with ID - %s', sec_grp_id)
694     out = list()
695     rules = neutron.list_security_group_rules(
696         **{'security_group_id': sec_grp_id})
697     for rule in rules['security_group_rules']:
698         if rule['security_group_id'] == sec_grp_id:
699             out.append(SecurityGroupRule(**rule))
700     return out
701
702
703 def get_rule_by_id(neutron, sec_grp, rule_id):
704     """
705     Returns a SecurityGroupRule object from OpenStack
706     :param neutron: the client
707     :param sec_grp: the SNAPS SecurityGroup domain object
708     :param rule_id: the rule's ID
709     :param sec_grp: a SNAPS SecurityGroupRule domain object
710     """
711     rules = neutron.list_security_group_rules(
712         **{'security_group_id': sec_grp.id})
713     for rule in rules['security_group_rules']:
714         if rule['id'] == rule_id:
715             return SecurityGroupRule(**rule)
716     return None
717
718
719 def get_external_networks(neutron):
720     """
721     Returns a list of external OpenStack network object/dict for all external
722     networks
723     :param neutron: the client
724     :return: a list of external networks of Type SNAPS-OO domain class Network
725     """
726     out = list()
727     for network in neutron.list_networks(
728             **{'router:external': True})['networks']:
729         out.append(__map_network(neutron, network))
730     return out
731
732
733 def get_port_floating_ips(neutron, ports):
734     """
735     Returns all of the floating IPs associated with the ports returned in a
736     list of tuples where the port object is in the first position and the
737     floating IP object is in the second
738     :param neutron: the Neutron client
739     :param ports: a list of tuple 2 where index 0 is the port name and index 1
740                   is the SNAPS-OO Port object
741     :return: a list of tuple 2 (port_id, SNAPS FloatingIp) objects when ports
742              is not None else a list of FloatingIp objects
743     """
744     out = list()
745     fips = neutron.list_floatingips()
746     for fip in fips['floatingips']:
747         for port_name, port in ports:
748             if port and port.id == fip['port_id']:
749                 out.append((port.id, FloatingIp(**fip)))
750                 break
751     return out
752
753
754 def get_floating_ips(neutron):
755     """
756     Returns a list of all of the floating IPs
757     :param neutron: the Neutron client
758     """
759     out = list()
760     fips = neutron.list_floatingips()
761     for fip in fips['floatingips']:
762         out.append(FloatingIp(**fip))
763     return out
764
765
766 def create_floating_ip(neutron, ext_net_name, port_id=None):
767     """
768     Returns the floating IP object that was created with this call
769     :param neutron: the Neutron client
770     :param ext_net_name: the name of the external network on which to apply the
771                          floating IP address
772     :param port_id: the ID of the port to which the floating IP will be
773                     associated
774     :return: the SNAPS FloatingIp object
775     """
776     logger.info('Creating floating ip to external network - ' + ext_net_name)
777     ext_net = get_network(neutron, network_name=ext_net_name)
778     if ext_net:
779         body = {'floatingip': {'floating_network_id': ext_net.id}}
780         if port_id:
781             body['floatingip']['port_id'] = port_id
782
783         fip = neutron.create_floatingip(body=body)
784
785         return FloatingIp(id=fip['floatingip']['id'],
786                           ip=fip['floatingip']['floating_ip_address'])
787     else:
788         raise NeutronException(
789             'Cannot create floating IP, external network not found')
790
791
792 def get_floating_ip(neutron, floating_ip):
793     """
794     Returns a floating IP object that should be identical to the floating_ip
795     parameter
796     :param neutron: the Neutron client
797     :param floating_ip: the SNAPS FloatingIp object
798     :return: hopefully the same floating IP object input
799     """
800     logger.debug('Attempting to retrieve existing floating ip with IP - %s',
801                  floating_ip.ip)
802     os_fip = __get_os_floating_ip(neutron, floating_ip)
803     if os_fip:
804         return FloatingIp(id=os_fip['id'], ip=os_fip['floating_ip_address'])
805
806
807 def __get_os_floating_ip(neutron, floating_ip):
808     """
809     Returns an OpenStack floating IP object
810     parameter
811     :param neutron: the Neutron client
812     :param floating_ip: the SNAPS FloatingIp object
813     :return: hopefully the same floating IP object input
814     """
815     logger.debug('Attempting to retrieve existing floating ip with IP - %s',
816                  floating_ip.ip)
817     fips = neutron.list_floatingips(ip=floating_ip.id)
818
819     for fip in fips['floatingips']:
820         if fip['id'] == floating_ip.id:
821             return fip
822
823
824 def delete_floating_ip(neutron, floating_ip):
825     """
826     Responsible for deleting a floating IP
827     :param neutron: the Neutron client
828     :param floating_ip: the SNAPS FloatingIp object
829     :return:
830     """
831     logger.debug('Attempting to delete existing floating ip with IP - %s',
832                  floating_ip.ip)
833     return neutron.delete_floatingip(floating_ip.id)
834
835
836 def get_network_quotas(neutron, project_id):
837     """
838     Returns a list of NetworkQuotas objects
839     :param neutron: the neutron client
840     :param project_id: the project's ID of the quotas to lookup
841     :return: an object of type NetworkQuotas or None if not found
842     """
843     quota = neutron.show_quota(project_id)
844     if quota:
845         return NetworkQuotas(**quota['quota'])
846
847
848 def update_quotas(neutron, project_id, network_quotas):
849     """
850     Updates the networking quotas for a given project
851     :param neutron: the Neutron client
852     :param project_id: the project's ID that requires quota updates
853     :param network_quotas: an object of type NetworkQuotas containing the
854                            values to update
855     :return:
856     """
857     update_body = dict()
858     update_body['security_group'] = network_quotas.security_group
859     update_body['security_group_rule'] = network_quotas.security_group_rule
860     update_body['floatingip'] = network_quotas.floatingip
861     update_body['network'] = network_quotas.network
862     update_body['port'] = network_quotas.port
863     update_body['router'] = network_quotas.router
864     update_body['subnet'] = network_quotas.subnet
865
866     return neutron.update_quota(project_id, {'quota': update_body})
867
868
869 class NeutronException(Exception):
870     """
871     Exception when calls to the Keystone client cannot be served properly
872     """