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