3 ##############################################################################
4 # Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others.
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 ##############################################################################
22 import apex.virtual.configure_vm as vm_lib
23 import apex.virtual.utils as virt_utils
24 from apex import DeploySettings
25 from apex import Inventory
26 from apex import NetworkEnvironment
27 from apex import NetworkSettings
28 from apex.common import utils
29 from apex.common import constants
30 from apex.common import parsers
31 from apex.common.exceptions import ApexDeployException
32 from apex.network import jumphost
33 from apex.undercloud import undercloud as uc_lib
34 from apex.overcloud import config as oc_cfg
35 from apex.overcloud import overcloud_deploy
37 APEX_TEMP_DIR = tempfile.mkdtemp(prefix='apex_tmp')
38 ANSIBLE_PATH = 'ansible/playbooks'
39 SDN_IMAGE = 'overcloud-full-opendaylight.qcow2'
42 def deploy_quickstart(args, deploy_settings_file, network_settings_file,
47 def validate_cross_settings(deploy_settings, net_settings, inventory):
49 Used to validate compatibility across settings file.
50 :param deploy_settings: parsed settings for deployment
51 :param net_settings: parsed settings for network
52 :param inventory: parsed inventory file
56 if deploy_settings['deploy_options']['dataplane'] != 'ovs' and 'tenant' \
57 not in net_settings.enabled_network_list:
58 raise ApexDeployException("Setting a DPDK based dataplane requires"
59 "a dedicated NIC for tenant network")
61 if 'odl_vpp_routing_node' in deploy_settings['deploy_options']:
62 if deploy_settings['deploy_options']['dataplane'] != 'fdio':
63 raise ApexDeployException("odl_vpp_routing_node should only be set"
64 "when dataplane is set to fdio")
65 if deploy_settings['deploy_options'].get('dvr') is True:
66 raise ApexDeployException("odl_vpp_routing_node should only be set"
67 "when dvr is not enabled")
69 # TODO(trozet): add more checks here like RAM for ODL, etc
70 # check if odl_vpp_netvirt is true and vpp is set
71 # Check if fdio and nosdn:
72 # tenant_nic_mapping_controller_members" ==
73 # "$tenant_nic_mapping_compute_members
76 def build_vms(inventory, network_settings,
77 template_dir='/usr/share/opnfv-apex'):
79 Creates VMs and configures vbmc and host
81 :param network_settings:
85 for idx, node in enumerate(inventory['nodes']):
86 name = 'baremetal{}'.format(idx)
87 volume = name + ".qcow2"
88 volume_path = os.path.join(constants.LIBVIRT_VOLUME_PATH, volume)
89 # TODO(trozet): add error checking
92 baremetal_interfaces=network_settings.enabled_network_list,
93 memory=node['memory'], cpus=node['cpu'],
95 template_dir=template_dir)
96 virt_utils.host_setup({name: node['pm_port']})
99 def create_deploy_parser():
100 deploy_parser = argparse.ArgumentParser()
101 deploy_parser.add_argument('--debug', action='store_true', default=False,
102 help="Turn on debug messages")
103 deploy_parser.add_argument('-l', '--log-file',
104 default='./apex_deploy.log',
105 dest='log_file', help="Log file to log to")
106 deploy_parser.add_argument('-d', '--deploy-settings',
107 dest='deploy_settings_file',
109 help='File which contains Apex deploy settings')
110 deploy_parser.add_argument('-n', '--network-settings',
111 dest='network_settings_file',
113 help='File which contains Apex network '
115 deploy_parser.add_argument('-i', '--inventory-file',
116 dest='inventory_file',
118 help='Inventory file which contains POD '
120 deploy_parser.add_argument('-e', '--environment-file',
122 default='opnfv-environment.yaml',
123 help='Provide alternate base env file')
124 deploy_parser.add_argument('-v', '--virtual', action='store_true',
127 help='Enable virtual deployment')
128 deploy_parser.add_argument('--interactive', action='store_true',
130 help='Enable interactive deployment mode which '
131 'requires user to confirm steps of '
133 deploy_parser.add_argument('--virtual-computes',
134 dest='virt_compute_nodes',
137 help='Number of Virtual Compute nodes to create'
138 ' and use during deployment (defaults to 1'
139 ' for noha and 2 for ha)')
140 deploy_parser.add_argument('--virtual-cpus',
144 help='Number of CPUs to use per Overcloud VM in'
145 ' a virtual deployment (defaults to 4)')
146 deploy_parser.add_argument('--virtual-default-ram',
147 dest='virt_default_ram',
150 help='Amount of default RAM to use per '
151 'Overcloud VM in GB (defaults to 8).')
152 deploy_parser.add_argument('--virtual-compute-ram',
153 dest='virt_compute_ram',
156 help='Amount of RAM to use per Overcloud '
157 'Compute VM in GB (defaults to 8). '
158 'Overrides --virtual-default-ram arg for '
160 deploy_parser.add_argument('--deploy-dir',
161 default='/usr/share/opnfv-apex',
162 help='Directory to deploy from which contains '
163 'base config files for deployment')
164 deploy_parser.add_argument('--image-dir',
165 default='/var/opt/opnfv/images',
166 help='Directory which contains '
167 'base disk images for deployment')
168 deploy_parser.add_argument('--lib-dir',
169 default='/usr/share/opnfv-apex',
170 help='Directory path for apex ansible '
171 'and third party libs')
172 deploy_parser.add_argument('--quickstart', action='store_true',
174 help='Use tripleo-quickstart to deploy')
178 def validate_deploy_args(args):
180 Validates arguments for deploy
185 logging.debug('Validating arguments for deployment')
186 if args.virtual and args.inventory_file is not None:
187 logging.error("Virtual enabled but inventory file also given")
188 raise ApexDeployException('You should not specify an inventory file '
189 'with virtual deployments')
191 args.inventory_file = os.path.join(APEX_TEMP_DIR,
192 'inventory-virt.yaml')
193 elif os.path.isfile(args.inventory_file) is False:
194 logging.error("Specified inventory file does not exist: {}".format(
195 args.inventory_file))
196 raise ApexDeployException('Specified inventory file does not exist')
198 for settings_file in (args.deploy_settings_file,
199 args.network_settings_file):
200 if os.path.isfile(settings_file) is False:
201 logging.error("Specified settings file does not "
202 "exist: {}".format(settings_file))
203 raise ApexDeployException('Specified settings file does not '
204 'exist: {}'.format(settings_file))
208 parser = create_deploy_parser()
209 args = parser.parse_args(sys.argv[1:])
210 # FIXME (trozet): this is only needed as a workaround for CI. Remove
212 if os.getenv('IMAGES', False):
213 args.image_dir = os.getenv('IMAGES')
215 log_level = logging.DEBUG
217 log_level = logging.INFO
218 os.makedirs(os.path.dirname(args.log_file), exist_ok=True)
219 formatter = '%(asctime)s %(levelname)s: %(message)s'
220 logging.basicConfig(filename=args.log_file,
222 datefmt='%m/%d/%Y %I:%M:%S %p',
224 console = logging.StreamHandler()
225 console.setLevel(log_level)
226 console.setFormatter(logging.Formatter(formatter))
227 logging.getLogger('').addHandler(console)
228 validate_deploy_args(args)
230 deploy_settings = DeploySettings(args.deploy_settings_file)
231 logging.info("Deploy settings are:\n {}".format(pprint.pformat(
233 net_settings = NetworkSettings(args.network_settings_file)
234 logging.info("Network settings are:\n {}".format(pprint.pformat(
236 net_env_file = os.path.join(args.deploy_dir, constants.NET_ENV_FILE)
237 net_env = NetworkEnvironment(net_settings, net_env_file)
238 net_env_target = os.path.join(APEX_TEMP_DIR, constants.NET_ENV_FILE)
239 utils.dump_yaml(dict(net_env), net_env_target)
240 ha_enabled = deploy_settings['global_params']['ha_enabled']
242 if args.virt_compute_ram is None:
243 compute_ram = args.virt_default_ram
245 compute_ram = args.virt_compute_ram
246 if deploy_settings['deploy_options']['sdn_controller'] == \
247 'opendaylight' and args.virt_default_ram < 12:
249 logging.warning('RAM per controller is too low. OpenDaylight '
250 'requires at least 12GB per controller.')
251 logging.info('Increasing RAM per controller to 12GB')
252 elif args.virt_default_ram < 10:
254 logging.warning('RAM per controller is too low. nosdn '
255 'requires at least 10GB per controller.')
256 logging.info('Increasing RAM per controller to 10GB')
258 control_ram = args.virt_default_ram
259 if ha_enabled and args.virt_compute_nodes < 2:
260 logging.debug('HA enabled, bumping number of compute nodes to 2')
261 args.virt_compute_nodes = 2
262 virt_utils.generate_inventory(args.inventory_file, ha_enabled,
263 num_computes=args.virt_compute_nodes,
264 controller_ram=control_ram * 1024,
265 compute_ram=compute_ram * 1024,
268 inventory = Inventory(args.inventory_file, ha_enabled, args.virtual)
270 validate_cross_settings(deploy_settings, net_settings, inventory)
273 deploy_settings_file = os.path.join(APEX_TEMP_DIR,
274 'apex_deploy_settings.yaml')
275 utils.dump_yaml(utils.dict_objects_to_str(deploy_settings),
276 deploy_settings_file)
277 logging.info("File created: {}".format(deploy_settings_file))
278 network_settings_file = os.path.join(APEX_TEMP_DIR,
279 'apex_network_settings.yaml')
280 utils.dump_yaml(utils.dict_objects_to_str(net_settings),
281 network_settings_file)
282 logging.info("File created: {}".format(network_settings_file))
283 deploy_quickstart(args, deploy_settings_file, network_settings_file,
286 # TODO (trozet): add logic back from:
287 # Iedb75994d35b5dc1dd5d5ce1a57277c8f3729dfd (FDIO DVR)
289 'virsh_enabled_networks': net_settings.enabled_network_list
291 utils.run_ansible(ansible_args,
292 os.path.join(args.lib_dir, ANSIBLE_PATH,
293 'deploy_dependencies.yml'))
295 if 'external' in net_settings.enabled_network_list:
298 # create all overcloud VMs
299 build_vms(inventory, net_settings, args.deploy_dir)
301 # Attach interfaces to jumphost for baremetal deployment
302 jump_networks = ['admin']
304 jump_networks.append('external')
305 for network in jump_networks:
306 if network == 'external':
307 # TODO(trozet): enable vlan secondary external networks
308 iface = net_settings['networks'][network][0][
309 'installer_vm']['members'][0]
311 iface = net_settings['networks'][network]['installer_vm'][
313 bridge = "br-{}".format(network)
314 jumphost.attach_interface_to_ovs(bridge, iface, network)
315 # Dump all settings out to temp bash files to be sourced
316 instackenv_json = os.path.join(APEX_TEMP_DIR, 'instackenv.json')
317 with open(instackenv_json, 'w') as fh:
318 json.dump(inventory, fh)
320 # Create and configure undercloud
322 root_pw = constants.DEBUG_OVERCLOUD_PW
325 undercloud = uc_lib.Undercloud(args.image_dir,
328 external_network=uc_external)
331 # Generate nic templates
332 for role in 'compute', 'controller':
333 oc_cfg.create_nic_template(net_settings, deploy_settings, role,
334 args.deploy_dir, APEX_TEMP_DIR)
336 undercloud.configure(net_settings,
337 os.path.join(args.lib_dir, ANSIBLE_PATH,
338 'configure_undercloud.yml'),
341 # Prepare overcloud-full.qcow2
342 logging.info("Preparing Overcloud for deployment...")
343 sdn_image = os.path.join(args.image_dir, SDN_IMAGE)
344 overcloud_deploy.prep_image(deploy_settings, sdn_image, APEX_TEMP_DIR,
346 opnfv_env = os.path.join(args.deploy_dir, args.env_file)
347 overcloud_deploy.prep_env(deploy_settings, net_settings, inventory,
348 opnfv_env, net_env_target, APEX_TEMP_DIR)
349 overcloud_deploy.create_deploy_cmd(deploy_settings, net_settings,
350 inventory, APEX_TEMP_DIR,
351 args.virtual, args.env_file)
352 deploy_playbook = os.path.join(args.lib_dir, ANSIBLE_PATH,
353 'deploy_overcloud.yml')
354 virt_env = 'virtual-environment.yaml'
355 bm_env = 'baremetal-environment.yaml'
356 for p_env in virt_env, bm_env:
357 shutil.copyfile(os.path.join(args.deploy_dir, p_env),
358 os.path.join(APEX_TEMP_DIR, p_env))
360 # Start Overcloud Deployment
361 logging.info("Executing Overcloud Deployment...")
363 deploy_vars['virtual'] = args.virtual
364 deploy_vars['debug'] = args.debug
365 deploy_vars['aarch64'] = platform.machine() == 'aarch64'
366 deploy_vars['dns_server_args'] = ''
367 deploy_vars['apex_temp_dir'] = APEX_TEMP_DIR
368 deploy_vars['stackrc'] = 'source /home/stack/stackrc'
369 deploy_vars['overcloudrc'] = 'source /home/stack/overcloudrc'
370 for dns_server in net_settings['dns_servers']:
371 deploy_vars['dns_server_args'] += " --dns-nameserver {}".format(
374 utils.run_ansible(deploy_vars, deploy_playbook, host=undercloud.ip,
375 user='stack', tmp_dir=APEX_TEMP_DIR)
376 logging.info("Overcloud deployment complete")
378 logging.error("Deployment Failed. Please check log")
381 os.remove(os.path.join(APEX_TEMP_DIR, 'overcloud-full.qcow2'))
384 logging.info("Executing post deploy configuration")
385 jumphost.configure_bridges(net_settings)
386 nova_output = os.path.join(APEX_TEMP_DIR, 'nova_output')
387 deploy_vars['overcloud_nodes'] = parsers.parse_nova_output(
389 deploy_vars['SSH_OPTIONS'] = '-o StrictHostKeyChecking=no -o ' \
390 'GlobalKnownHostsFile=/dev/null -o ' \
391 'UserKnownHostsFile=/dev/null -o ' \
393 deploy_vars['external_network_cmds'] = \
394 overcloud_deploy.external_network_cmds(net_settings)
395 # TODO(trozet): just parse all ds_opts as deploy vars one time
396 ds_opts = deploy_settings['deploy_options']
397 deploy_vars['gluon'] = ds_opts['gluon']
398 deploy_vars['sdn'] = ds_opts['sdn_controller']
399 for dep_option in 'yardstick', 'dovetail', 'vsperf':
400 if dep_option in ds_opts:
401 deploy_vars[dep_option] = ds_opts[dep_option]
403 deploy_vars[dep_option] = False
404 deploy_vars['dataplane'] = ds_opts['dataplane']
405 overcloudrc = os.path.join(APEX_TEMP_DIR, 'overcloudrc')
406 if ds_opts['congress']:
407 deploy_vars['congress_datasources'] = \
408 overcloud_deploy.create_congress_cmds(overcloudrc)
409 deploy_vars['congress'] = True
411 deploy_vars['congress'] = False
412 deploy_vars['calipso'] = ds_opts.get('calipso', False)
413 deploy_vars['calipso_ip'] = net_settings['networks']['admin'][
414 'installer_vm']['ip']
415 # TODO(trozet): this is probably redundant with getting external
416 # network info from undercloud.py
417 if 'external' in net_settings.enabled_network_list:
418 ext_cidr = net_settings['networks']['external'][0]['cidr']
420 ext_cidr = net_settings['networks']['admin']['cidr']
421 deploy_vars['external_cidr'] = str(ext_cidr)
422 if ext_cidr.version == 6:
423 deploy_vars['external_network_ipv6'] = True
425 deploy_vars['external_network_ipv6'] = False
426 post_undercloud = os.path.join(args.lib_dir, ANSIBLE_PATH,
427 'post_deploy_undercloud.yml')
428 logging.info("Executing post deploy configuration undercloud playbook")
430 utils.run_ansible(deploy_vars, post_undercloud, host=undercloud.ip,
431 user='stack', tmp_dir=APEX_TEMP_DIR)
432 logging.info("Post Deploy Undercloud Configuration Complete")
434 logging.error("Post Deploy Undercloud Configuration failed. "
437 # Post deploy overcloud node configuration
438 # TODO(trozet): just parse all ds_opts as deploy vars one time
439 deploy_vars['sfc'] = ds_opts['sfc']
440 deploy_vars['vpn'] = ds_opts['vpn']
441 # TODO(trozet): pull all logs and store in tmp dir in overcloud
443 post_overcloud = os.path.join(args.lib_dir, ANSIBLE_PATH,
444 'post_deploy_overcloud.yml')
445 # Run per overcloud node
446 for node, ip in deploy_vars['overcloud_nodes'].items():
447 logging.info("Executing Post deploy overcloud playbook on "
448 "node {}".format(node))
450 utils.run_ansible(deploy_vars, post_overcloud, host=ip,
451 user='heat-admin', tmp_dir=APEX_TEMP_DIR)
452 logging.info("Post Deploy Overcloud Configuration Complete "
453 "for node {}".format(node))
455 logging.error("Post Deploy Overcloud Configuration failed "
456 "for node {}. Please check log".format(node))
458 logging.info("Apex deployment complete")
459 logging.info("Undercloud IP: {}, please connect by doing "
460 "'opnfv-util undercloud'".format(undercloud.ip))
461 # TODO(trozet): add logging here showing controller VIP and horizon url
462 if __name__ == '__main__':