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