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