Enable SFC scenarios for Gambia
[apex.git] / apex / deploy.py
1 #!/usr/bin/env python
2
3 ##############################################################################
4 # Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others.
5 #
6 # All rights reserved. This program and the accompanying materials
7 # are made available under the terms of the Apache License, Version 2.0
8 # which accompanies this distribution, and is available at
9 # http://www.apache.org/licenses/LICENSE-2.0
10 ##############################################################################
11
12 import argparse
13 import git
14 import json
15 import logging
16 import os
17 import platform
18 import pprint
19 import shutil
20 import sys
21 import tempfile
22 import yaml
23
24 import apex.virtual.configure_vm as vm_lib
25 import apex.virtual.utils as virt_utils
26 import apex.builders.common_builder as c_builder
27 import apex.builders.overcloud_builder as oc_builder
28 import apex.builders.undercloud_builder as uc_builder
29 from apex import DeploySettings
30 from apex import Inventory
31 from apex import NetworkEnvironment
32 from apex import NetworkSettings
33 from apex.common import utils
34 from apex.common import constants
35 from apex.common import parsers
36 from apex.common.exceptions import ApexDeployException
37 from apex.deployment.tripleo import ApexDeployment
38 from apex.network import jumphost
39 from apex.network import network_data
40 from apex.undercloud import undercloud as uc_lib
41 from apex.overcloud import config as oc_cfg
42 from apex.overcloud import deploy as oc_deploy
43
44 APEX_TEMP_DIR = tempfile.mkdtemp(prefix='apex_tmp')
45 SDN_IMAGE = 'overcloud-full-opendaylight.qcow2'
46
47
48 def deploy_quickstart(args, deploy_settings_file, network_settings_file,
49                       inventory_file=None):
50     pass
51
52
53 def validate_cross_settings(deploy_settings, net_settings, inventory):
54     """
55     Used to validate compatibility across settings file.
56     :param deploy_settings: parsed settings for deployment
57     :param net_settings: parsed settings for network
58     :param inventory: parsed inventory file
59     :return: None
60     """
61
62     if deploy_settings['deploy_options']['dataplane'] != 'ovs' and 'tenant' \
63             not in net_settings.enabled_network_list:
64         raise ApexDeployException("Setting a DPDK based dataplane requires"
65                                   "a dedicated NIC for tenant network")
66
67     if 'odl_vpp_routing_node' in deploy_settings['deploy_options']:
68         if deploy_settings['deploy_options']['dataplane'] != 'fdio':
69             raise ApexDeployException("odl_vpp_routing_node should only be set"
70                                       "when dataplane is set to fdio")
71         if deploy_settings['deploy_options'].get('dvr') is True:
72             raise ApexDeployException("odl_vpp_routing_node should only be set"
73                                       "when dvr is not enabled")
74
75     # TODO(trozet): add more checks here like RAM for ODL, etc
76     # check if odl_vpp_netvirt is true and vpp is set
77     # Check if fdio and nosdn:
78     # tenant_nic_mapping_controller_members" ==
79     # "$tenant_nic_mapping_compute_members
80
81
82 def build_vms(inventory, network_settings,
83               template_dir='/usr/share/opnfv-apex'):
84     """
85     Creates VMs and configures vbmc and host
86     :param inventory:
87     :param network_settings:
88     :return:
89     """
90
91     for idx, node in enumerate(inventory['nodes']):
92         name = 'baremetal{}'.format(idx)
93         volume = name + ".qcow2"
94         volume_path = os.path.join(constants.LIBVIRT_VOLUME_PATH, volume)
95         # TODO(trozet): add error checking
96         vm_lib.create_vm(
97             name, volume_path,
98             baremetal_interfaces=network_settings.enabled_network_list,
99             memory=node['memory'], cpus=node['cpu'],
100             macs=node['mac'],
101             template_dir=template_dir)
102         virt_utils.host_setup({name: node['pm_port']})
103
104
105 def create_deploy_parser():
106     deploy_parser = argparse.ArgumentParser()
107     deploy_parser.add_argument('--debug', action='store_true', default=False,
108                                help="Turn on debug messages")
109     deploy_parser.add_argument('-l', '--log-file',
110                                default='./apex_deploy.log',
111                                dest='log_file', help="Log file to log to")
112     deploy_parser.add_argument('-d', '--deploy-settings',
113                                dest='deploy_settings_file',
114                                required=True,
115                                help='File which contains Apex deploy settings')
116     deploy_parser.add_argument('-n', '--network-settings',
117                                dest='network_settings_file',
118                                required=True,
119                                help='File which contains Apex network '
120                                     'settings')
121     deploy_parser.add_argument('-i', '--inventory-file',
122                                dest='inventory_file',
123                                default=None,
124                                help='Inventory file which contains POD '
125                                     'definition')
126     deploy_parser.add_argument('-e', '--environment-file',
127                                dest='env_file',
128                                default='opnfv-environment.yaml',
129                                help='Provide alternate base env file located '
130                                     'in deploy_dir')
131     deploy_parser.add_argument('-v', '--virtual', action='store_true',
132                                default=False,
133                                dest='virtual',
134                                help='Enable virtual deployment')
135     deploy_parser.add_argument('--interactive', action='store_true',
136                                default=False,
137                                help='Enable interactive deployment mode which '
138                                     'requires user to confirm steps of '
139                                     'deployment')
140     deploy_parser.add_argument('--virtual-computes',
141                                dest='virt_compute_nodes',
142                                default=1,
143                                type=int,
144                                help='Number of Virtual Compute nodes to create'
145                                     ' and use during deployment (defaults to 1'
146                                     ' for noha and 2 for ha)')
147     deploy_parser.add_argument('--virtual-cpus',
148                                dest='virt_cpus',
149                                default=4,
150                                type=int,
151                                help='Number of CPUs to use per Overcloud VM in'
152                                     ' a virtual deployment (defaults to 4)')
153     deploy_parser.add_argument('--virtual-default-ram',
154                                dest='virt_default_ram',
155                                default=8,
156                                type=int,
157                                help='Amount of default RAM to use per '
158                                     'Overcloud VM in GB (defaults to 8).')
159     deploy_parser.add_argument('--virtual-compute-ram',
160                                dest='virt_compute_ram',
161                                default=None,
162                                type=int,
163                                help='Amount of RAM to use per Overcloud '
164                                     'Compute VM in GB (defaults to 8). '
165                                     'Overrides --virtual-default-ram arg for '
166                                     'computes')
167     deploy_parser.add_argument('--deploy-dir',
168                                default='/usr/share/opnfv-apex',
169                                help='Directory to deploy from which contains '
170                                     'base config files for deployment')
171     deploy_parser.add_argument('--image-dir',
172                                default='/var/opt/opnfv/images',
173                                help='Directory which contains '
174                                     'base disk images for deployment')
175     deploy_parser.add_argument('--lib-dir',
176                                default='/usr/share/opnfv-apex',
177                                help='Directory path for apex ansible '
178                                     'and third party libs')
179     deploy_parser.add_argument('--quickstart', action='store_true',
180                                default=False,
181                                help='Use tripleo-quickstart to deploy')
182     deploy_parser.add_argument('--upstream', action='store_true',
183                                default=True,
184                                help='Force deployment to use upstream '
185                                     'artifacts. This option is now '
186                                     'deprecated and only upstream '
187                                     'deployments are supported.')
188     deploy_parser.add_argument('--no-fetch', action='store_true',
189                                default=False,
190                                help='Ignore fetching latest upstream and '
191                                     'use what is in cache')
192     deploy_parser.add_argument('-p', '--patches',
193                                default='/etc/opnfv-apex/common-patches.yaml',
194                                dest='patches_file',
195                                help='File to include for common patches '
196                                     'which apply to all deployment scenarios')
197     return deploy_parser
198
199
200 def validate_deploy_args(args):
201     """
202     Validates arguments for deploy
203     :param args:
204     :return: None
205     """
206
207     logging.debug('Validating arguments for deployment')
208     if args.virtual and args.inventory_file is not None:
209         logging.error("Virtual enabled but inventory file also given")
210         raise ApexDeployException('You should not specify an inventory file '
211                                   'with virtual deployments')
212     elif args.virtual:
213         args.inventory_file = os.path.join(APEX_TEMP_DIR,
214                                            'inventory-virt.yaml')
215     elif os.path.isfile(args.inventory_file) is False:
216         logging.error("Specified inventory file does not exist: {}".format(
217             args.inventory_file))
218         raise ApexDeployException('Specified inventory file does not exist')
219
220     for settings_file in (args.deploy_settings_file,
221                           args.network_settings_file):
222         if os.path.isfile(settings_file) is False:
223             logging.error("Specified settings file does not "
224                           "exist: {}".format(settings_file))
225             raise ApexDeployException('Specified settings file does not '
226                                       'exist: {}'.format(settings_file))
227
228
229 def main():
230     parser = create_deploy_parser()
231     args = parser.parse_args(sys.argv[1:])
232     # FIXME (trozet): this is only needed as a workaround for CI.  Remove
233     # when CI is changed
234     if os.getenv('IMAGES', False):
235         args.image_dir = os.getenv('IMAGES')
236     if args.debug:
237         log_level = logging.DEBUG
238     else:
239         log_level = logging.INFO
240     os.makedirs(os.path.dirname(args.log_file), exist_ok=True)
241     formatter = '%(asctime)s %(levelname)s: %(message)s'
242     logging.basicConfig(filename=args.log_file,
243                         format=formatter,
244                         datefmt='%m/%d/%Y %I:%M:%S %p',
245                         level=log_level)
246     console = logging.StreamHandler()
247     console.setLevel(log_level)
248     console.setFormatter(logging.Formatter(formatter))
249     logging.getLogger('').addHandler(console)
250     utils.install_ansible()
251     validate_deploy_args(args)
252     # Parse all settings
253     deploy_settings = DeploySettings(args.deploy_settings_file)
254     logging.info("Deploy settings are:\n {}".format(pprint.pformat(
255         deploy_settings)))
256     net_settings = NetworkSettings(args.network_settings_file)
257     logging.info("Network settings are:\n {}".format(pprint.pformat(
258         net_settings)))
259     os_version = deploy_settings['deploy_options']['os_version']
260     net_env_file = os.path.join(args.deploy_dir, constants.NET_ENV_FILE)
261     net_env = NetworkEnvironment(net_settings, net_env_file,
262                                  os_version=os_version)
263     net_env_target = os.path.join(APEX_TEMP_DIR, constants.NET_ENV_FILE)
264     utils.dump_yaml(dict(net_env), net_env_target)
265
266     # get global deploy params
267     ha_enabled = deploy_settings['global_params']['ha_enabled']
268     introspect = deploy_settings['global_params'].get('introspect', True)
269
270     if args.virtual:
271         if args.virt_compute_ram is None:
272             compute_ram = args.virt_default_ram
273         else:
274             compute_ram = args.virt_compute_ram
275         if deploy_settings['deploy_options']['sdn_controller'] == \
276                 'opendaylight' and args.virt_default_ram < 12:
277             control_ram = 12
278             logging.warning('RAM per controller is too low.  OpenDaylight '
279                             'requires at least 12GB per controller.')
280             logging.info('Increasing RAM per controller to 12GB')
281         elif args.virt_default_ram < 10:
282             control_ram = 10
283             logging.warning('RAM per controller is too low.  nosdn '
284                             'requires at least 10GB per controller.')
285             logging.info('Increasing RAM per controller to 10GB')
286         else:
287             control_ram = args.virt_default_ram
288         if ha_enabled and args.virt_compute_nodes < 2:
289             logging.debug('HA enabled, bumping number of compute nodes to 2')
290             args.virt_compute_nodes = 2
291         virt_utils.generate_inventory(args.inventory_file, ha_enabled,
292                                       num_computes=args.virt_compute_nodes,
293                                       controller_ram=control_ram * 1024,
294                                       compute_ram=compute_ram * 1024,
295                                       vcpus=args.virt_cpus
296                                       )
297     inventory = Inventory(args.inventory_file, ha_enabled, args.virtual)
298     logging.info("Inventory is:\n {}".format(pprint.pformat(
299         inventory)))
300
301     validate_cross_settings(deploy_settings, net_settings, inventory)
302     ds_opts = deploy_settings['deploy_options']
303     if args.quickstart:
304         deploy_settings_file = os.path.join(APEX_TEMP_DIR,
305                                             'apex_deploy_settings.yaml')
306         utils.dump_yaml(utils.dict_objects_to_str(deploy_settings),
307                         deploy_settings_file)
308         logging.info("File created: {}".format(deploy_settings_file))
309         network_settings_file = os.path.join(APEX_TEMP_DIR,
310                                              'apex_network_settings.yaml')
311         utils.dump_yaml(utils.dict_objects_to_str(net_settings),
312                         network_settings_file)
313         logging.info("File created: {}".format(network_settings_file))
314         deploy_quickstart(args, deploy_settings_file, network_settings_file,
315                           args.inventory_file)
316     else:
317         deployment = ApexDeployment(deploy_settings, args.patches_file,
318                                     args.deploy_settings_file)
319         # TODO (trozet): add logic back from:
320         # Iedb75994d35b5dc1dd5d5ce1a57277c8f3729dfd (FDIO DVR)
321         ansible_args = {
322             'virsh_enabled_networks': net_settings.enabled_network_list
323         }
324         utils.run_ansible(ansible_args,
325                           os.path.join(args.lib_dir, constants.ANSIBLE_PATH,
326                                        'deploy_dependencies.yml'))
327         uc_external = False
328         if 'external' in net_settings.enabled_network_list:
329             uc_external = True
330         if args.virtual:
331             # create all overcloud VMs
332             build_vms(inventory, net_settings, args.deploy_dir)
333         else:
334             # Attach interfaces to jumphost for baremetal deployment
335             jump_networks = ['admin']
336             if uc_external:
337                 jump_networks.append('external')
338             for network in jump_networks:
339                 if network == 'external':
340                     # TODO(trozet): enable vlan secondary external networks
341                     iface = net_settings['networks'][network][0][
342                         'installer_vm']['members'][0]
343                 else:
344                     iface = net_settings['networks'][network]['installer_vm'][
345                         'members'][0]
346                 bridge = "br-{}".format(network)
347                 jumphost.attach_interface_to_ovs(bridge, iface, network)
348         instackenv_json = os.path.join(APEX_TEMP_DIR, 'instackenv.json')
349         with open(instackenv_json, 'w') as fh:
350             json.dump(inventory, fh)
351
352         # Create and configure undercloud
353         if args.debug:
354             root_pw = constants.DEBUG_OVERCLOUD_PW
355         else:
356             root_pw = None
357
358         if not args.upstream:
359             logging.warning("Using upstream is now required for Apex. "
360                             "Forcing upstream to true")
361         if os_version == 'master':
362             branch = 'master'
363         else:
364             branch = "stable/{}".format(os_version)
365
366         logging.info("Deploying with upstream artifacts for OpenStack "
367                      "{}".format(os_version))
368         args.image_dir = os.path.join(args.image_dir, os_version)
369         upstream_url = constants.UPSTREAM_RDO.replace(
370             constants.DEFAULT_OS_VERSION, os_version)
371         upstream_targets = ['overcloud-full.tar', 'undercloud.qcow2']
372         utils.fetch_upstream_and_unpack(args.image_dir, upstream_url,
373                                         upstream_targets,
374                                         fetch=not args.no_fetch)
375         sdn_image = os.path.join(args.image_dir, 'overcloud-full.qcow2')
376         # copy undercloud so we don't taint upstream fetch
377         uc_image = os.path.join(args.image_dir, 'undercloud_mod.qcow2')
378         uc_fetch_img = os.path.join(args.image_dir, 'undercloud.qcow2')
379         shutil.copyfile(uc_fetch_img, uc_image)
380         # prep undercloud with required packages
381         uc_builder.add_upstream_packages(uc_image)
382         # add patches from upstream to undercloud and overcloud
383         logging.info('Adding patches to undercloud')
384         patches = deployment.determine_patches()
385         c_builder.add_upstream_patches(patches['undercloud'], uc_image,
386                                        APEX_TEMP_DIR, branch)
387
388         # Create/Start Undercloud VM
389         undercloud = uc_lib.Undercloud(args.image_dir,
390                                        args.deploy_dir,
391                                        root_pw=root_pw,
392                                        external_network=uc_external,
393                                        image_name=os.path.basename(uc_image),
394                                        os_version=os_version)
395         undercloud.start()
396         undercloud_admin_ip = net_settings['networks'][
397             constants.ADMIN_NETWORK]['installer_vm']['ip']
398
399         if ds_opts['containers']:
400             tag = constants.DOCKER_TAG
401         else:
402             tag = None
403
404         # Generate nic templates
405         for role in 'compute', 'controller':
406             oc_cfg.create_nic_template(net_settings, deploy_settings, role,
407                                        args.deploy_dir, APEX_TEMP_DIR)
408         # Install Undercloud
409         undercloud.configure(net_settings, deploy_settings,
410                              os.path.join(args.lib_dir, constants.ANSIBLE_PATH,
411                                           'configure_undercloud.yml'),
412                              APEX_TEMP_DIR, virtual_oc=args.virtual)
413
414         # Prepare overcloud-full.qcow2
415         logging.info("Preparing Overcloud for deployment...")
416         if os_version != 'ocata':
417             net_data_file = os.path.join(APEX_TEMP_DIR, 'network_data.yaml')
418             net_data = network_data.create_network_data(net_settings,
419                                                         net_data_file)
420         else:
421             net_data = False
422
423         shutil.copyfile(os.path.join(args.deploy_dir, 'build_ovs_nsh.sh'),
424                         os.path.join(APEX_TEMP_DIR, 'build_ovs_nsh.sh'))
425
426         # TODO(trozet): Either fix opnfv env or default to use upstream env
427         if args.env_file == 'opnfv-environment.yaml':
428             # Override the env_file if it is defaulted to opnfv
429             # opnfv env file will not work with upstream
430             args.env_file = 'upstream-environment.yaml'
431         opnfv_env = os.path.join(args.deploy_dir, args.env_file)
432         oc_deploy.prep_env(deploy_settings, net_settings, inventory,
433                            opnfv_env, net_env_target, APEX_TEMP_DIR)
434         if not args.virtual:
435             oc_deploy.LOOP_DEVICE_SIZE = "50G"
436         patched_containers = oc_deploy.prep_image(
437             deploy_settings, net_settings, sdn_image, APEX_TEMP_DIR,
438             root_pw=root_pw, docker_tag=tag, patches=patches['overcloud'])
439
440         oc_deploy.create_deploy_cmd(deploy_settings, net_settings, inventory,
441                                     APEX_TEMP_DIR, args.virtual,
442                                     os.path.basename(opnfv_env),
443                                     net_data=net_data)
444         # Prepare undercloud with containers
445         docker_playbook = os.path.join(args.lib_dir, constants.ANSIBLE_PATH,
446                                        'prepare_overcloud_containers.yml')
447         if ds_opts['containers']:
448             logging.info("Preparing Undercloud with Docker containers")
449             sdn_env = oc_deploy.get_docker_sdn_files(ds_opts)
450             sdn_env_files = str()
451             for sdn_file in sdn_env:
452                 sdn_env_files += " -e {}".format(sdn_file)
453             if patched_containers:
454                 oc_builder.archive_docker_patches(APEX_TEMP_DIR)
455             container_vars = dict()
456             container_vars['apex_temp_dir'] = APEX_TEMP_DIR
457             container_vars['patched_docker_services'] = list(
458                 patched_containers)
459             container_vars['container_tag'] = constants.DOCKER_TAG
460             container_vars['stackrc'] = 'source /home/stack/stackrc'
461             container_vars['sdn'] = ds_opts['sdn_controller']
462             container_vars['undercloud_ip'] = undercloud_admin_ip
463             container_vars['os_version'] = os_version
464             container_vars['aarch64'] = platform.machine() == 'aarch64'
465             container_vars['sdn_env_file'] = sdn_env_files
466             try:
467                 utils.run_ansible(container_vars, docker_playbook,
468                                   host=undercloud.ip, user='stack',
469                                   tmp_dir=APEX_TEMP_DIR)
470                 logging.info("Container preparation complete")
471             except Exception:
472                 logging.error("Unable to complete container prep on "
473                               "Undercloud")
474                 os.remove(os.path.join(APEX_TEMP_DIR, 'overcloud-full.qcow2'))
475                 raise
476
477         deploy_playbook = os.path.join(args.lib_dir, constants.ANSIBLE_PATH,
478                                        'deploy_overcloud.yml')
479         virt_env = 'virtual-environment.yaml'
480         bm_env = 'baremetal-environment.yaml'
481         k8s_env = 'kubernetes-environment.yaml'
482         for p_env in virt_env, bm_env, k8s_env:
483             shutil.copyfile(os.path.join(args.deploy_dir, p_env),
484                             os.path.join(APEX_TEMP_DIR, p_env))
485
486         # Start Overcloud Deployment
487         logging.info("Executing Overcloud Deployment...")
488         deploy_vars = dict()
489         deploy_vars['virtual'] = args.virtual
490         deploy_vars['debug'] = args.debug
491         deploy_vars['aarch64'] = platform.machine() == 'aarch64'
492         deploy_vars['introspect'] = not (args.virtual or
493                                          deploy_vars['aarch64'] or
494                                          not introspect)
495         deploy_vars['dns_server_args'] = ''
496         deploy_vars['apex_temp_dir'] = APEX_TEMP_DIR
497         deploy_vars['apex_env_file'] = os.path.basename(opnfv_env)
498         deploy_vars['stackrc'] = 'source /home/stack/stackrc'
499         deploy_vars['overcloudrc'] = 'source /home/stack/overcloudrc'
500         deploy_vars['undercloud_ip'] = undercloud_admin_ip
501         deploy_vars['ha_enabled'] = ha_enabled
502         deploy_vars['os_version'] = os_version
503         deploy_vars['http_proxy'] = net_settings.get('http_proxy', '')
504         deploy_vars['https_proxy'] = net_settings.get('https_proxy', '')
505         deploy_vars['vim'] = ds_opts['vim']
506         for dns_server in net_settings['dns_servers']:
507             deploy_vars['dns_server_args'] += " --dns-nameserver {}".format(
508                 dns_server)
509         try:
510             utils.run_ansible(deploy_vars, deploy_playbook, host=undercloud.ip,
511                               user='stack', tmp_dir=APEX_TEMP_DIR)
512             logging.info("Overcloud deployment complete")
513         except Exception:
514             logging.error("Deployment Failed.  Please check deploy log as "
515                           "well as mistral logs in "
516                           "{}".format(os.path.join(APEX_TEMP_DIR,
517                                                    'mistral_logs.tar.gz')))
518             raise
519         finally:
520             os.remove(os.path.join(APEX_TEMP_DIR, 'overcloud-full.qcow2'))
521
522         # Post install
523         logging.info("Executing post deploy configuration")
524         jumphost.configure_bridges(net_settings)
525         nova_output = os.path.join(APEX_TEMP_DIR, 'nova_output')
526         deploy_vars['overcloud_nodes'] = parsers.parse_nova_output(
527             nova_output)
528         deploy_vars['SSH_OPTIONS'] = '-o StrictHostKeyChecking=no -o ' \
529                                      'GlobalKnownHostsFile=/dev/null -o ' \
530                                      'UserKnownHostsFile=/dev/null -o ' \
531                                      'LogLevel=error'
532         deploy_vars['external_network_cmds'] = \
533             oc_deploy.external_network_cmds(net_settings, deploy_settings)
534         # TODO(trozet): just parse all ds_opts as deploy vars one time
535         deploy_vars['gluon'] = ds_opts['gluon']
536         deploy_vars['sdn'] = ds_opts['sdn_controller']
537         for dep_option in 'yardstick', 'dovetail', 'vsperf':
538             if dep_option in ds_opts:
539                 deploy_vars[dep_option] = ds_opts[dep_option]
540             else:
541                 deploy_vars[dep_option] = False
542         deploy_vars['dataplane'] = ds_opts['dataplane']
543         overcloudrc = os.path.join(APEX_TEMP_DIR, 'overcloudrc')
544         if ds_opts['congress']:
545             deploy_vars['congress_datasources'] = \
546                 oc_deploy.create_congress_cmds(overcloudrc)
547             deploy_vars['congress'] = True
548         else:
549             deploy_vars['congress'] = False
550         deploy_vars['calipso'] = ds_opts.get('calipso', False)
551         deploy_vars['calipso_ip'] = undercloud_admin_ip
552         # overcloudrc.v3 removed and set as default in queens and later
553         if os_version == 'pike':
554             deploy_vars['overcloudrc_files'] = ['overcloudrc',
555                                                 'overcloudrc.v3']
556         else:
557             deploy_vars['overcloudrc_files'] = ['overcloudrc']
558
559         post_undercloud = os.path.join(args.lib_dir,
560                                        constants.ANSIBLE_PATH,
561                                        'post_deploy_undercloud.yml')
562         logging.info("Executing post deploy configuration undercloud "
563                      "playbook")
564         try:
565             utils.run_ansible(deploy_vars, post_undercloud,
566                               host=undercloud.ip, user='stack',
567                               tmp_dir=APEX_TEMP_DIR)
568             logging.info("Post Deploy Undercloud Configuration Complete")
569         except Exception:
570             logging.error("Post Deploy Undercloud Configuration failed.  "
571                           "Please check log")
572             raise
573
574         # Deploy kubernetes if enabled
575         # (TODO)zshi move handling of kubernetes deployment
576         # to its own deployment class
577         if deploy_vars['vim'] == 'k8s':
578             # clone kubespray repo
579             git.Repo.clone_from(constants.KUBESPRAY_URL,
580                                 os.path.join(APEX_TEMP_DIR, 'kubespray'))
581             shutil.copytree(
582                 os.path.join(APEX_TEMP_DIR, 'kubespray', 'inventory',
583                              'sample'),
584                 os.path.join(APEX_TEMP_DIR, 'kubespray', 'inventory',
585                              'apex'))
586             k8s_node_inventory = {
587                 'all':
588                     {'hosts': {},
589                      'children': {
590                          'k8s-cluster': {
591                              'children': {
592                                  'kube-master': {
593                                      'hosts': {}
594                                  },
595                                  'kube-node': {
596                                      'hosts': {}
597                                  }
598                              }
599                          },
600                          'etcd': {
601                              'hosts': {}
602                          }
603                     }
604                     }
605             }
606             for node, ip in deploy_vars['overcloud_nodes'].items():
607                 k8s_node_inventory['all']['hosts'][node] = {
608                     'ansible_become': True,
609                     'ansible_ssh_host': ip,
610                     'ansible_become_user': 'root',
611                     'ip': ip
612                 }
613                 if 'controller' in node:
614                     k8s_node_inventory['all']['children']['k8s-cluster'][
615                         'children']['kube-master']['hosts'][node] = None
616                     k8s_node_inventory['all']['children']['etcd'][
617                         'hosts'][node] = None
618                 elif 'compute' in node:
619                     k8s_node_inventory['all']['children']['k8s-cluster'][
620                         'children']['kube-node']['hosts'][node] = None
621
622             kubespray_dir = os.path.join(APEX_TEMP_DIR, 'kubespray')
623             with open(os.path.join(kubespray_dir, 'inventory', 'apex',
624                                    'apex.yaml'), 'w') as invfile:
625                 yaml.dump(k8s_node_inventory, invfile,
626                           default_flow_style=False)
627             k8s_deploy_vars = {}
628             # Add kubespray ansible control variables in k8s_deploy_vars,
629             # example: 'kube_network_plugin': 'flannel'
630             k8s_deploy = os.path.join(kubespray_dir, 'cluster.yml')
631             k8s_deploy_inv_file = os.path.join(kubespray_dir, 'inventory',
632                                                'apex', 'apex.yaml')
633
634             k8s_remove_pkgs = os.path.join(args.lib_dir,
635                                            constants.ANSIBLE_PATH,
636                                            'k8s_remove_pkgs.yml')
637             try:
638                 logging.debug("Removing any existing overcloud docker "
639                               "packages")
640                 utils.run_ansible(k8s_deploy_vars, k8s_remove_pkgs,
641                                   host=k8s_deploy_inv_file,
642                                   user='heat-admin', tmp_dir=APEX_TEMP_DIR)
643                 logging.info("k8s Deploy Remove Existing Docker Related "
644                              "Packages Complete")
645             except Exception:
646                 logging.error("k8s Deploy Remove Existing Docker Related "
647                               "Packages failed. Please check log")
648                 raise
649
650             try:
651                 utils.run_ansible(k8s_deploy_vars, k8s_deploy,
652                                   host=k8s_deploy_inv_file,
653                                   user='heat-admin', tmp_dir=APEX_TEMP_DIR)
654                 logging.info("k8s Deploy Overcloud Configuration Complete")
655             except Exception:
656                 logging.error("k8s Deploy Overcloud Configuration failed."
657                               "Please check log")
658                 raise
659
660         # Post deploy overcloud node configuration
661         # TODO(trozet): just parse all ds_opts as deploy vars one time
662         deploy_vars['sfc'] = ds_opts['sfc']
663         deploy_vars['vpn'] = ds_opts['vpn']
664         deploy_vars['l2gw'] = ds_opts.get('l2gw')
665         deploy_vars['sriov'] = ds_opts.get('sriov')
666         deploy_vars['tacker'] = ds_opts.get('tacker')
667         # TODO(trozet): pull all logs and store in tmp dir in overcloud
668         # playbook
669         post_overcloud = os.path.join(args.lib_dir, constants.ANSIBLE_PATH,
670                                       'post_deploy_overcloud.yml')
671         # Run per overcloud node
672         for node, ip in deploy_vars['overcloud_nodes'].items():
673             logging.info("Executing Post deploy overcloud playbook on "
674                          "node {}".format(node))
675             try:
676                 utils.run_ansible(deploy_vars, post_overcloud, host=ip,
677                                   user='heat-admin', tmp_dir=APEX_TEMP_DIR)
678                 logging.info("Post Deploy Overcloud Configuration Complete "
679                              "for node {}".format(node))
680             except Exception:
681                 logging.error("Post Deploy Overcloud Configuration failed "
682                               "for node {}. Please check log".format(node))
683                 raise
684         logging.info("Apex deployment complete")
685         logging.info("Undercloud IP: {}, please connect by doing "
686                      "'opnfv-util undercloud'".format(undercloud.ip))
687         # TODO(trozet): add logging here showing controller VIP and horizon url
688
689
690 if __name__ == '__main__':
691     main()