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