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