Fixed bug with regards to subnet lookups.
[snaps.git] / snaps / openstack / utils / launch_utils.py
1 #
2 # Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
3 #                    and others.  All rights reserved.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at:
8 #
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17 # This utility makes it easy to create OpenStack objects
18 import logging
19 import re
20 import socket
21 import struct
22
23 import os
24 import time
25 from keystoneauth1.exceptions import Unauthorized
26
27 from snaps.config.flavor import FlavorConfig
28 from snaps.config.image import ImageConfig
29 from snaps.config.keypair import KeypairConfig
30 from snaps.config.network import PortConfig, NetworkConfig
31 from snaps.config.project import ProjectConfig
32 from snaps.config.qos import QoSConfig
33 from snaps.config.router import RouterConfig
34 from snaps.config.security_group import SecurityGroupConfig
35 from snaps.config.user import UserConfig
36 from snaps.config.vm_inst import VmInstanceConfig
37 from snaps.config.volume import VolumeConfig
38 from snaps.config.volume_type import VolumeTypeConfig
39 from snaps.openstack.create_flavor import OpenStackFlavor
40 from snaps.openstack.create_image import OpenStackImage
41 from snaps.openstack.create_keypairs import OpenStackKeypair
42 from snaps.openstack.create_network import OpenStackNetwork
43 from snaps.openstack.create_project import OpenStackProject
44 from snaps.openstack.create_qos import OpenStackQoS
45 from snaps.openstack.create_router import OpenStackRouter
46 from snaps.openstack.create_security_group import OpenStackSecurityGroup
47 from snaps.openstack.create_user import OpenStackUser
48 from snaps.openstack.create_volume import OpenStackVolume
49 from snaps.openstack.create_volume_type import OpenStackVolumeType
50 from snaps.openstack.os_credentials import OSCreds, ProxySettings
51 from snaps.openstack.utils import deploy_utils, neutron_utils, keystone_utils
52 from snaps.provisioning import ansible_utils
53
54 logger = logging.getLogger('lanuch_utils')
55 DEFAULT_CREDS_KEY = 'admin'
56
57
58 def launch_config(config, tmplt_file, deploy, clean, clean_image):
59     """
60     Launches all objects and applies any configured ansible playbooks
61     :param config: the environment configuration dict object
62     :param tmplt_file: the path to the SNAPS-OO template file
63     :param deploy: when True deploy
64     :param clean: when True clean
65     :param clean_image: when True clean the image when clean is True
66     """
67     os_config = config.get('openstack')
68
69     creators = list()
70     vm_dict = dict()
71     images_dict = dict()
72     flavors_dict = dict()
73     networks_dict = dict()
74     routers_dict = dict()
75     os_creds_dict = dict()
76
77     if os_config:
78         os_creds_dict = __get_creds_dict(os_config)
79
80         # Create projects
81         projects_dict = __create_instances(
82             os_creds_dict, OpenStackProject, ProjectConfig,
83             os_config.get('projects'), 'project', clean)
84         creators.append(projects_dict)
85
86         # Create users
87         users_dict = __create_instances(
88             os_creds_dict, OpenStackUser, UserConfig,
89             os_config.get('users'), 'user', clean)
90         creators.append(users_dict)
91
92         # Associate new users to projects
93         if not clean:
94             for project_creator in projects_dict.values():
95                 users = project_creator.project_settings.users
96                 for user_name in users:
97                     user_creator = users_dict.get(user_name)
98                     if user_creator:
99                         project_creator.assoc_user(
100                             user_creator.get_user())
101
102         # Create flavors
103         flavors_dict = __create_instances(
104             os_creds_dict, OpenStackFlavor, FlavorConfig,
105             os_config.get('flavors'), 'flavor', clean, users_dict)
106         creators.append(flavors_dict)
107
108         # Create QoS specs
109         qos_dict = __create_instances(
110             os_creds_dict, OpenStackQoS, QoSConfig,
111             os_config.get('qos_specs'), 'qos_spec', clean, users_dict)
112         creators.append(qos_dict)
113
114         # Create volume types
115         vol_type_dict = __create_instances(
116             os_creds_dict, OpenStackVolumeType, VolumeTypeConfig,
117             os_config.get('volume_types'), 'volume_type', clean,
118             users_dict)
119         creators.append(vol_type_dict)
120
121         # Create volumes
122         vol_dict = __create_instances(
123             os_creds_dict, OpenStackVolume, VolumeConfig,
124             os_config.get('volumes'), 'volume', clean, users_dict)
125         creators.append(vol_dict)
126
127         # Create images
128         images_dict = __create_instances(
129             os_creds_dict, OpenStackImage, ImageConfig,
130             os_config.get('images'), 'image', clean, users_dict)
131         creators.append(images_dict)
132
133         # Create networks
134         networks_dict = __create_instances(
135             os_creds_dict, OpenStackNetwork, NetworkConfig,
136             os_config.get('networks'), 'network', clean, users_dict)
137         creators.append(networks_dict)
138
139         # Create routers
140         routers_dict = __create_instances(
141             os_creds_dict, OpenStackRouter, RouterConfig,
142             os_config.get('routers'), 'router', clean, users_dict)
143         creators.append(routers_dict)
144
145         # Create keypairs
146         keypairs_dict = __create_instances(
147             os_creds_dict, OpenStackKeypair, KeypairConfig,
148             os_config.get('keypairs'), 'keypair', clean, users_dict)
149         creators.append(keypairs_dict)
150
151         # Create security groups
152         creators.append(__create_instances(
153             os_creds_dict, OpenStackSecurityGroup,
154             SecurityGroupConfig,
155             os_config.get('security_groups'), 'security_group', clean,
156             users_dict))
157
158         # Create instance
159         vm_dict = __create_vm_instances(
160             os_creds_dict, users_dict, os_config.get('instances'),
161             images_dict, keypairs_dict, clean)
162         creators.append(vm_dict)
163         logger.info(
164             'Completed creating/retrieving all configured instances')
165
166     # Must enter either block
167     if clean:
168         # Cleanup Environment
169         __cleanup(creators, clean_image)
170     elif deploy:
171         # Provision VMs
172         ansible_config = config.get('ansible')
173         if ansible_config and vm_dict:
174             if not __apply_ansible_playbooks(
175                     ansible_config, os_creds_dict, vm_dict, images_dict,
176                     flavors_dict, networks_dict, routers_dict, tmplt_file):
177                 logger.error("Problem applying ansible playbooks")
178
179
180 def __get_creds_dict(os_conn_config):
181     """
182     Returns a dict of OSCreds where the key is the creds name.
183     For backwards compatibility, credentials not contained in a list (only
184     one) will be returned with the key of None
185     :param os_conn_config: the credential configuration
186     :return: a dict of OSCreds objects
187     """
188     if 'connection' in os_conn_config:
189         return {DEFAULT_CREDS_KEY: __get_os_credentials(os_conn_config)}
190     elif 'connections' in os_conn_config:
191         out = dict()
192         for os_conn_dict in os_conn_config['connections']:
193             config = os_conn_dict.get('connection')
194             if not config:
195                 raise Exception('Invalid connection format')
196
197             name = config.get('name')
198             if not name:
199                 raise Exception('Connection config requires a name field')
200
201             out[name] = __get_os_credentials(os_conn_dict)
202         return out
203
204
205 def __get_creds(os_creds_dict, os_user_dict, inst_config):
206     """
207     Returns the appropriate credentials
208     :param os_creds_dict: a dictionary of OSCreds objects where the name is the
209                           key
210     :param os_user_dict: a dictionary of OpenStackUser objects where the name
211                          is the key
212     :param inst_config:
213     :return: an OSCreds instance or None
214     """
215     os_creds = os_creds_dict.get(DEFAULT_CREDS_KEY)
216     if 'os_user' in inst_config:
217         os_user_conf = inst_config['os_user']
218         if 'name' in os_user_conf:
219             user_creator = os_user_dict.get(os_user_conf['name'])
220             if user_creator:
221                 return user_creator.get_os_creds(
222                     project_name=os_user_conf.get('project_name'))
223     elif 'os_creds_name' in inst_config:
224         if 'os_creds_name' in inst_config:
225             os_creds = os_creds_dict[inst_config['os_creds_name']]
226     return os_creds
227
228
229 def __get_os_credentials(os_conn_config):
230     """
231     Returns an object containing all of the information required to access
232     OpenStack APIs
233     :param os_conn_config: The configuration holding the credentials
234     :return: an OSCreds instance
235     """
236     config = os_conn_config.get('connection')
237     if not config:
238         raise Exception('Invalid connection configuration')
239
240     proxy_settings = None
241     http_proxy = config.get('http_proxy')
242     if http_proxy:
243         tokens = re.split(':', http_proxy)
244         ssh_proxy_cmd = config.get('ssh_proxy_cmd')
245         proxy_settings = ProxySettings(host=tokens[0], port=tokens[1],
246                                        ssh_proxy_cmd=ssh_proxy_cmd)
247     else:
248         if 'proxy_settings' in config:
249             host = config['proxy_settings'].get('host')
250             port = config['proxy_settings'].get('port')
251             if host and host != 'None' and port and port != 'None':
252                 proxy_settings = ProxySettings(**config['proxy_settings'])
253
254     if proxy_settings:
255         config['proxy_settings'] = proxy_settings
256     else:
257         if config.get('proxy_settings'):
258             del config['proxy_settings']
259
260     return OSCreds(**config)
261
262
263 def __parse_ports_config(config):
264     """
265     Parses the "ports" configuration
266     :param config: The dictionary to parse
267     :return: a list of PortConfig objects
268     """
269     out = list()
270     for port_config in config:
271         out.append(PortConfig(**port_config.get('port')))
272     return out
273
274
275 def __create_instances(os_creds_dict, creator_class, config_class, config,
276                        config_key, cleanup=False, os_users_dict=None):
277     """
278     Returns a dictionary of SNAPS creator objects where the key is the name
279     :param os_creds_dict: Dictionary of OSCreds objects where the key is the
280                           name
281     :param config: The list of configurations for the same type
282     :param config_key: The list of configurations for the same type
283     :param cleanup: Denotes whether or not this is being called for cleanup
284     :return: dictionary
285     """
286     out = {}
287
288     if config:
289         for config_dict in config:
290             inst_config = config_dict.get(config_key)
291             if inst_config:
292                 creds = __get_creds(os_creds_dict, os_users_dict, inst_config)
293                 if creds:
294                     creator = creator_class(
295                         creds,
296                         config_class(**inst_config))
297
298                     if creator:
299                         if cleanup:
300                             try:
301                                 creator.initialize()
302                             except Unauthorized as e:
303                                 logger.warn(
304                                     'Unable to initialize creator [%s] - %s',
305                                     creator, e)
306                         else:
307                             creator.create()
308
309                         out[inst_config['name']] = creator
310                     else:
311                         raise Exception('Unable to instantiate creator')
312
313         logger.info('Initialized configured %ss', config_key)
314
315     return out
316
317
318 def __create_vm_instances(os_creds_dict, os_users_dict, instances_config,
319                           image_dict, keypairs_dict, cleanup=False):
320     """
321     Returns a dictionary of OpenStackVmInstance objects where the key is the
322     instance name
323     :param os_creds_dict: Dictionary of OSCreds objects where the key is the
324                           name
325     :param os_users_dict: Dictionary of OpenStackUser objects where the key is
326                           the username
327     :param instances_config: The list of VM instance configurations
328     :param image_dict: A dictionary of images that will probably be used to
329                        instantiate the VM instance
330     :param keypairs_dict: A dictionary of keypairs that will probably be used
331                           to instantiate the VM instance
332     :param cleanup: Denotes whether or not this is being called for cleanup
333     :return: dictionary
334     """
335     vm_dict = {}
336
337     if instances_config:
338         for instance_config in instances_config:
339             conf = instance_config.get('instance')
340             if conf:
341                 if image_dict:
342                     image_creator = image_dict.get(conf.get('imageName'))
343                     if image_creator:
344                         instance_settings = VmInstanceConfig(
345                             **instance_config['instance'])
346                         kp_creator = keypairs_dict.get(
347                             conf.get('keypair_name'))
348
349                         try:
350                             vm_dict[conf[
351                                 'name']] = deploy_utils.create_vm_instance(
352                                 __get_creds(
353                                     os_creds_dict, os_users_dict, conf),
354                                 instance_settings,
355                                 image_creator.image_settings,
356                                 keypair_creator=kp_creator,
357                                 init_only=cleanup)
358                         except Unauthorized as e:
359                             if not cleanup:
360                                 logger.warn('Unable to initialize VM - %s', e)
361                                 raise
362                     else:
363                         raise Exception('Image creator instance not found.'
364                                         ' Cannot instantiate')
365                 else:
366                     if not cleanup:
367                         raise Exception('Image dictionary is None. Cannot '
368                                         'instantiate')
369             else:
370                 raise Exception('Instance configuration is None. Cannot '
371                                 'instantiate')
372         logger.info('Created configured instances')
373
374     return vm_dict
375
376
377 def __apply_ansible_playbooks(ansible_configs, os_creds_dict, vm_dict,
378                               image_dict, flavor_dict, networks_dict,
379                               routers_dict, tmplt_file):
380     """
381     Applies ansible playbooks to running VMs with floating IPs
382     :param ansible_configs: a list of Ansible configurations
383     :param os_creds_dict: Dictionary of OSCreds objects where the key is the
384                           name
385     :param vm_dict: the dictionary of newly instantiated VMs where the name is
386                     the key
387     :param image_dict: the dictionary of newly instantiated images where the
388                        name is the key
389     :param flavor_dict: the dictionary of newly instantiated flavors where the
390                         name is the key
391     :param networks_dict: the dictionary of newly instantiated networks where
392                           the name is the key
393     :param routers_dict: the dictionary of newly instantiated routers where
394                           the name is the key
395     :param tmplt_file: the path of the SNAPS-OO template file for setting the
396                        CWD so playbook location is relative to the deployment
397                        file
398     :return: t/f - true if successful
399     """
400     logger.info("Applying Ansible Playbooks")
401     if ansible_configs:
402         # Set CWD so the deployment file's playbook location can leverage
403         # relative paths
404         orig_cwd = os.getcwd()
405         env_dir = os.path.dirname(tmplt_file)
406         os.chdir(env_dir)
407
408         # Apply playbooks
409         for ansible_config in ansible_configs:
410             # Ensure all hosts are accepting SSH session requests
411             for vm_name in ansible_config['hosts']:
412                 vm_inst = vm_dict.get(vm_name)
413                 if vm_inst:
414                     if not vm_inst.vm_ssh_active(block=True):
415                         logger.warning(
416                             'Timeout waiting for instance to respond to '
417                             'SSH requests')
418                         return False
419
420             os_creds = os_creds_dict.get('admin-creds')
421             __apply_ansible_playbook(
422                 ansible_config, os_creds, vm_dict, image_dict, flavor_dict,
423                 networks_dict, routers_dict)
424
425         # Return to original directory
426         os.chdir(orig_cwd)
427
428     return True
429
430
431 def __apply_ansible_playbook(ansible_config, os_creds, vm_dict, image_dict,
432                              flavor_dict, networks_dict, routers_dict):
433     """
434     Applies an Ansible configuration setting
435     :param ansible_config: the configuration settings
436     :param os_creds: the OpenStack admin credentials object
437     :param vm_dict: the dictionary of newly instantiated VMs where the name is
438                     the key
439     :param image_dict: the dictionary of newly instantiated images where the
440                        name is the key
441     :param flavor_dict: the dictionary of newly instantiated flavors where the
442                         name is the key
443     :param networks_dict: the dictionary of newly instantiated networks where
444                           the name is the key
445     :param routers_dict: the dictionary of newly instantiated routers where
446                           the name is the key
447     """
448     if ansible_config:
449         (remote_user, floating_ips, private_key_filepath,
450          proxy_settings) = __get_connection_info(
451             ansible_config, vm_dict)
452         if floating_ips:
453             for key, vm_creator in vm_dict.items():
454                 fip = vm_creator.get_floating_ip()
455                 if fip and fip.ip in floating_ips:
456                     if not vm_creator.cloud_init_complete(block=True):
457                         raise Exception(
458                             'Cannot apply playbooks as cloud-init has not '
459                             'completed')
460
461             variables = __get_variables(
462                 ansible_config.get('variables'), os_creds, vm_dict, image_dict,
463                 flavor_dict, networks_dict, routers_dict)
464
465             retval = ansible_utils.apply_playbook(
466                 ansible_config['playbook_location'], floating_ips, remote_user,
467                 ssh_priv_key_file_path=private_key_filepath,
468                 variables=variables,
469                 proxy_setting=proxy_settings)
470             if retval != 0:
471                 # Not a fatal type of event
472                 raise Exception(
473                     'Error applying playbook found at location - %s',
474                     ansible_config.get('playbook_location'))
475             elif ansible_config.get('post_processing'):
476                 post_proc_config = ansible_config['post_processing']
477                 if 'sleep' in post_proc_config:
478                     time.sleep(post_proc_config['sleep'])
479                 if 'reboot' in post_proc_config:
480                     for vm_name in post_proc_config['reboot']:
481                         if vm_name in vm_dict:
482                             logger.info('Rebooting VM - %s', vm_name)
483                             vm_dict[vm_name].reboot()
484
485             return retval
486
487
488 def __get_connection_info(ansible_config, vm_dict):
489     """
490     Returns a tuple of data required for connecting to the running VMs
491     (remote_user, [floating_ips], private_key_filepath, proxy_settings)
492     :param ansible_config: the configuration settings
493     :param vm_dict: the dictionary of VMs where the VM name is the key
494     :return: tuple where the first element is the user and the second is a list
495              of floating IPs and the third is the
496     private key file location and the fourth is an instance of the
497     snaps.ProxySettings class
498     (note: in order to work, each of the hosts need to have the same sudo_user
499     and private key file location values)
500     """
501     if ansible_config.get('hosts'):
502         hosts = ansible_config['hosts']
503         if len(hosts) > 0:
504             floating_ips = list()
505             remote_user = None
506             pk_file = None
507             proxy_settings = None
508             for host in hosts:
509                 vm = vm_dict.get(host)
510                 if vm:
511                     fip = vm.get_floating_ip()
512                     if fip:
513                         remote_user = vm.get_image_user()
514
515                         if fip:
516                             floating_ips.append(fip.ip)
517                         else:
518                             raise Exception(
519                                 'Could not find floating IP for VM - ' +
520                                 vm.name)
521
522                         pk_file = vm.keypair_settings.private_filepath
523                         proxy_settings = vm.get_os_creds().proxy_settings
524                 else:
525                     logger.error('Could not locate VM with name - ' + host)
526
527             return remote_user, floating_ips, pk_file, proxy_settings
528     return None
529
530
531 def __get_variables(var_config, os_creds, vm_dict, image_dict, flavor_dict,
532                     networks_dict, routers_dict):
533     """
534     Returns a dictionary of substitution variables to be used for Ansible
535     templates
536     :param var_config: the variable configuration settings
537     :param os_creds: the OpenStack admin credentials object
538     :param vm_dict: the dictionary of newly instantiated VMs where the name is
539                     the key
540     :param image_dict: the dictionary of newly instantiated images where the
541                        name is the key
542     :param flavor_dict: the dictionary of newly instantiated flavors where the
543                         name is the key
544     :param networks_dict: the dictionary of newly instantiated networks where
545                           the name is the key
546     :param routers_dict: the dictionary of newly instantiated routers where
547                           the name is the key
548     :return: dictionary or None
549     """
550     if var_config and vm_dict and len(vm_dict) > 0:
551         variables = dict()
552         for key, value in var_config.items():
553             value = __get_variable_value(
554                 value, os_creds, vm_dict, image_dict, flavor_dict,
555                 networks_dict, routers_dict)
556             if key and value:
557                 variables[key] = value
558                 logger.info(
559                     "Set Jinga2 variable with key [%s] the value [%s]",
560                     key, value)
561             else:
562                 raise Exception(
563                     'Key - [' + str(key) + '] or Value [' + str(value)
564                     + '] must not be None')
565         return variables
566     return None
567
568
569 def __get_variable_value(var_config_values, os_creds, vm_dict, image_dict,
570                          flavor_dict, networks_dict, routers_dict):
571     """
572     Returns the associated variable value for use by Ansible for substitution
573     purposes
574     :param var_config_values: the configuration dictionary
575     :param os_creds: the OpenStack admin credentials object
576     :param vm_dict: the dictionary of newly instantiated VMs where the name is
577                     the key
578     :param image_dict: the dictionary of newly instantiated images where the
579                        name is the key
580     :param flavor_dict: the dictionary of newly instantiated flavors where the
581                         name is the key
582     :param networks_dict: the dictionary of newly instantiated networks where
583                           the name is the key
584     :param routers_dict: the dictionary of newly instantiated routers where
585                           the name is the key
586     :return:
587     """
588     if var_config_values['type'] == 'string':
589         return __get_string_variable_value(var_config_values)
590     if var_config_values['type'] == 'vm-attr':
591         return __get_vm_attr_variable_value(var_config_values, vm_dict)
592     if var_config_values['type'] == 'os_creds':
593         return __get_os_creds_variable_value(var_config_values, os_creds)
594     if var_config_values['type'] == 'network':
595         return __get_network_variable_value(var_config_values, networks_dict)
596     if var_config_values['type'] == 'router':
597         return __get_router_variable_value(var_config_values, routers_dict,
598                                            os_creds)
599     if var_config_values['type'] == 'port':
600         return __get_vm_port_variable_value(var_config_values, vm_dict)
601     if var_config_values['type'] == 'floating_ip':
602         return __get_vm_fip_variable_value(var_config_values, vm_dict)
603     if var_config_values['type'] == 'image':
604         return __get_image_variable_value(var_config_values, image_dict)
605     if var_config_values['type'] == 'flavor':
606         return __get_flavor_variable_value(var_config_values, flavor_dict)
607     return None
608
609
610 def __get_string_variable_value(var_config_values):
611     """
612     Returns the associated string value
613     :param var_config_values: the configuration dictionary
614     :return: the value contained in the dictionary with the key 'value'
615     """
616     return var_config_values['value']
617
618
619 def __get_vm_attr_variable_value(var_config_values, vm_dict):
620     """
621     Returns the associated value contained on a VM instance
622     :param var_config_values: the configuration dictionary
623     :param vm_dict: the dictionary containing all VMs where the key is the VM's
624                     name
625     :return: the value
626     """
627     vm = vm_dict.get(var_config_values['vm_name'])
628     if vm:
629         if var_config_values['value'] == 'floating_ip':
630             return vm.get_floating_ip().ip
631         if var_config_values['value'] == 'image_user':
632             return vm.get_image_user()
633
634
635 def __get_os_creds_variable_value(var_config_values, os_creds):
636     """
637     Returns the associated OS credentials value
638     :param var_config_values: the configuration dictionary
639     :param os_creds: the admin OpenStack OSCreds object
640     :return: the value
641     """
642     if os_creds:
643         if var_config_values['value'] == 'username':
644             logger.info("Returning OS username")
645             return os_creds.username
646         elif var_config_values['value'] == 'password':
647             logger.info("Returning OS password")
648             return os_creds.password
649         elif var_config_values['value'] == 'auth_url':
650             logger.info("Returning OS auth_url")
651             return os_creds.auth_url
652         elif var_config_values['value'] == 'project_name':
653             logger.info("Returning OS project_name")
654             return os_creds.project_name
655
656
657 def __get_network_variable_value(var_config_values, networks_dict):
658     """
659     Returns the associated network value
660     :param var_config_values: the configuration dictionary
661     :param networks_dict: the dictionary containing all networks where the key
662                           is the network name
663     :return: the value
664     """
665     net_name = var_config_values.get('network_name')
666
667     if net_name and networks_dict.get(net_name):
668         network_creator = networks_dict[net_name]
669
670         if 'subnet_name' in var_config_values:
671             subnet_name = var_config_values.get('subnet_name')
672             if subnet_name:
673                 for subnet in network_creator.get_network().subnets:
674                     if subnet_name == subnet.name:
675                         if 'value' in var_config_values:
676                             if 'gateway_ip' == var_config_values['value']:
677                                 return subnet.gateway_ip
678                             if 'ip_range' == var_config_values['value']:
679                                 return subnet.start + ' ' + subnet.end
680                             if 'ip_range_start' == var_config_values['value']:
681                                 return subnet.start
682                             if 'ip_range_end' == var_config_values['value']:
683                                 return subnet.end
684                             if 'cidr' == var_config_values['value']:
685                                 return subnet.cidr
686                             if 'cidr_ip' == var_config_values['value']:
687                                 cidr_split = subnet.cidr.split('/')
688                                 return cidr_split[0]
689                             if 'netmask' == var_config_values['value']:
690                                 cidr_split = subnet.cidr.split('/')
691                                 cidr_bits = 32 - int(cidr_split[1])
692                                 netmask = socket.inet_ntoa(
693                                     struct.pack(
694                                         '!I', (1 << 32) - (1 << cidr_bits)))
695                                 return netmask
696                             if 'broadcast_ip' == var_config_values['value']:
697                                 end_split = subnet.end.split('.')
698                                 broadcast_ip = (
699                                     end_split[0] + '.' + end_split[1] + '.'
700                                     + end_split[2] + '.255')
701                                 return broadcast_ip
702
703
704 def __get_router_variable_value(var_config_values, routers_dict, os_creds):
705     """
706     Returns the associated network value
707     :param var_config_values: the configuration dictionary
708     :param routers_dict: the dictionary containing all networks where the key
709                           is the network name
710     :param os_creds: the admin OpenStack credentials
711     :return: the value
712     """
713     router_name = var_config_values.get('router_name')
714     router_creator = routers_dict[router_name]
715
716     if router_creator:
717         if 'external_fixed_ip' == var_config_values.get('attr'):
718             session = keystone_utils.keystone_session(os_creds)
719             neutron = neutron_utils.neutron_client(os_creds, session)
720             try:
721                 ext_nets = neutron_utils.get_external_networks(neutron)
722
723                 subnet_name = var_config_values.get('subnet_name')
724
725                 for ext_net in ext_nets:
726                     for subnet in ext_net.subnets:
727                         if subnet_name == subnet.name:
728                             router = router_creator.get_router()
729                             for fixed_ips in router.external_fixed_ips:
730                                 if subnet.id == fixed_ips['subnet_id']:
731                                     return fixed_ips['ip_address']
732             finally:
733                 keystone_utils.close_session(session)
734
735
736 def __get_vm_port_variable_value(var_config_values, vm_dict):
737     """
738     Returns the associated OS credentials value
739     :param var_config_values: the configuration dictionary
740     :param vm_dict: the dictionary containing all VMs where the key is the VM's
741                     name
742     :return: the value
743     """
744     port_name = var_config_values.get('port_name')
745     vm_name = var_config_values.get('vm_name')
746
747     if port_name and vm_name:
748         vm = vm_dict.get(vm_name)
749         if vm:
750             for vm_port in vm.get_vm_inst().ports:
751                 if vm_port.name == port_name:
752                     port_value_id = var_config_values.get('port_value')
753                     if port_value_id:
754                         if port_value_id == 'mac_address':
755                             return vm_port.mac_address
756                         if port_value_id == 'ip_address':
757                             return vm_port.ips[0]['ip_address']
758
759
760 def __get_vm_fip_variable_value(var_config_values, vm_dict):
761     """
762     Returns the floating IP value if found
763     :param var_config_values: the configuration dictionary
764     :param vm_dict: the dictionary containing all VMs where the key is the VM's
765                     name
766     :return: the floating IP string value or None
767     """
768     fip_name = var_config_values.get('fip_name')
769     vm_name = var_config_values.get('vm_name')
770
771     if vm_name:
772         vm = vm_dict.get(vm_name)
773         if vm:
774             fip = vm.get_floating_ip(fip_name)
775             if fip:
776                 return fip.ip
777
778
779 def __get_image_variable_value(var_config_values, image_dict):
780     """
781     Returns the associated image value
782     :param var_config_values: the configuration dictionary
783     :param image_dict: the dictionary containing all images where the key is
784                        the name
785     :return: the value
786     """
787     if image_dict:
788         if var_config_values.get('image_name'):
789             image_creator = image_dict.get(var_config_values['image_name'])
790             if image_creator:
791                 if (var_config_values.get('value')
792                         and var_config_values['value'] == 'id'):
793                     return image_creator.get_image().id
794                 if (var_config_values.get('value')
795                         and var_config_values['value'] == 'user'):
796                     return image_creator.image_settings.image_user
797
798
799 def __get_flavor_variable_value(var_config_values, flavor_dict):
800     """
801     Returns the associated flavor value
802     :param var_config_values: the configuration dictionary
803     :param flavor_dict: the dictionary containing all flavor creators where the
804                         key is the name
805     :return: the value or None
806     """
807     if flavor_dict:
808         if var_config_values.get('flavor_name'):
809             flavor_creator = flavor_dict.get(var_config_values['flavor_name'])
810             if flavor_creator:
811                 if (var_config_values.get('value')
812                         and var_config_values['value'] == 'id'):
813                     return flavor_creator.get_flavor().id
814
815
816 def __cleanup(creators, clean_image=False):
817     """
818     Cleans up environment
819     :param creators: the list of creators by type
820     :param clean_image: when true
821     :return:
822     """
823     for creator_dict in reversed(creators):
824         for key, creator in creator_dict.items():
825             if ((isinstance(creator, OpenStackImage) and clean_image)
826                     or not isinstance(creator, OpenStackImage)):
827                 creator.clean()