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