From f4d388ea508ba00771e43a219ac64e0d430b73bd Mon Sep 17 00:00:00 2001 From: Tim Rozet Date: Sun, 25 Jun 2017 21:25:36 -0400 Subject: [PATCH] Migrates Apex to Python Removes all bash libraries and converts almost all of the code to a mixture of Python and Ansible. utils.sh and clean.sh still exist. clean.sh will be migrated fully to clean.py in another patch. The Apex Python package is now built into the opnfv-apex-common RPM. To install locally do 'pip3 install .'. To deploy: opnfv-deploy -d -n --image-dir /root/apex/.build -v --debug Non-python files (THT yaml, settings files, ansible playbooks) are all installed into /usr/share/opnfv-apex/. The RPM will copy settings files into /etc/opnfv-apex/. JIRA: APEX-317 Change-Id: I3232f0329bcd13bce5a28da6a8c9c84d0b048024 Signed-off-by: Tim Rozet --- .gitignore | 7 + {lib/python/apex => apex}/__init__.py | 8 +- {ci => apex}/build.py | 6 +- {lib/python/apex/common => apex/build}/__init__.py | 0 {lib/python => apex/build}/build_utils.py | 0 {lib/python/apex => apex}/clean.py | 26 + apex/common/__init__.py | 0 {lib/python/apex => apex}/common/constants.py | 16 + apex/common/exceptions.py | 12 + apex/common/parsers.py | 73 +++ apex/common/utils.py | 107 ++++ apex/deploy.py | 441 ++++++++++++++++ apex/inventory/__init__.py | 0 {lib/python/apex => apex/inventory}/inventory.py | 19 +- apex/network/__init__.py | 0 {lib/python/apex => apex/network}/ip_utils.py | 0 apex/network/jumphost.py | 172 +++++++ .../apex => apex/network}/network_environment.py | 13 +- apex/overcloud/__init__.py | 0 apex/overcloud/config.py | 76 +++ apex/overcloud/overcloud_deploy.py | 556 +++++++++++++++++++++ apex/settings/__init__.py | 0 .../apex => apex/settings}/deploy_settings.py | 29 +- .../apex => apex/settings}/network_settings.py | 57 +-- apex/tests/__init__.py | 0 {tests => apex/tests}/config/inventory.yaml | 0 apex/tests/constants.py | 12 + apex/tests/playbooks/test_playbook.yaml | 5 + .../tests}/smoke_tests/execute_smoke_tests.sh | 0 .../tests}/smoke_tests/execute_tests.yml | 0 .../tests}/smoke_tests/prepare_undercloud.yml | 0 {tests => apex/tests}/smoke_tests/smoke_tests.yml | 0 {tests => apex/tests}/test_apex_clean.py | 6 +- {tests => apex/tests}/test_apex_common_utils.py | 26 +- {tests => apex/tests}/test_apex_deploy_settings.py | 22 +- {tests => apex/tests}/test_apex_inventory.py | 34 +- {tests => apex/tests}/test_apex_ip_utils.py | 27 +- .../tests}/test_apex_network_environment.py | 63 ++- .../tests}/test_apex_network_settings.py | 52 +- apex/undercloud/__init__.py | 0 apex/undercloud/undercloud.py | 206 ++++++++ apex/virtual/__init__.py | 0 apex/virtual/configure_vm.py | 206 ++++++++ apex/virtual/virtual_utils.py | 140 ++++++ build/Makefile | 14 +- {lib/installer => build}/domain.xml | 0 build/rpm_specs/opnfv-apex-common.spec | 99 +--- build/variables.sh | 4 +- ci/build.sh | 2 +- ci/clean.sh | 118 ++++- ci/deploy.sh | 248 +-------- ci/run_smoke_tests.sh | 2 - ci/util.sh | 84 +++- lib/ansible/playbooks/build_dependencies.yml | 10 +- lib/ansible/playbooks/configure_undercloud.yml | 116 +++++ lib/ansible/playbooks/deploy_dependencies.yml | 66 +++ lib/ansible/playbooks/deploy_overcloud.yml | 68 +++ lib/ansible/playbooks/post_deploy_overcloud.yml | 45 ++ lib/ansible/playbooks/post_deploy_undercloud.yml | 118 +++++ .../playbooks/templates/external_vlan_ifcfg.yml.j2 | 9 + .../templates/virsh_network_default.xml.j2 | 10 + .../playbooks/templates/virsh_network_ovs.xml.j2 | 6 + lib/ansible/playbooks/templates/virsh_pool.xml.j2 | 6 + lib/ansible/playbooks/undercloud_aarch64.yml | 49 ++ lib/common-functions.sh | 308 ------------ lib/configure-deps-functions.sh | 173 ------- lib/configure-vm | 203 -------- lib/overcloud-deploy-functions.sh | 503 ------------------- lib/parse-functions.sh | 70 --- lib/post-install-functions.sh | 281 ----------- lib/python/apex/common/utils.py | 31 -- lib/python/apex_python_utils.py | 265 ---------- lib/undercloud-functions.sh | 291 ----------- lib/utility-functions.sh | 85 ---- lib/virtual-setup-functions.sh | 164 ------ requirements.txt | 13 + setup.cfg | 48 ++ setup.py | 19 + test-requirements.txt | 6 + tests/test_apex_python_utils_py.py | 91 ---- tox.ini | 26 + 81 files changed, 3038 insertions(+), 3030 deletions(-) rename {lib/python/apex => apex}/__init__.py (68%) rename {ci => apex}/build.py (98%) rename {lib/python/apex/common => apex/build}/__init__.py (100%) rename {lib/python => apex/build}/build_utils.py (100%) rename {lib/python/apex => apex}/clean.py (64%) create mode 100644 apex/common/__init__.py rename {lib/python/apex => apex}/common/constants.py (74%) create mode 100644 apex/common/exceptions.py create mode 100644 apex/common/parsers.py create mode 100644 apex/common/utils.py create mode 100644 apex/deploy.py create mode 100644 apex/inventory/__init__.py rename {lib/python/apex => apex/inventory}/inventory.py (88%) create mode 100644 apex/network/__init__.py rename {lib/python/apex => apex/network}/ip_utils.py (100%) create mode 100644 apex/network/jumphost.py rename {lib/python/apex => apex/network}/network_environment.py (98%) create mode 100644 apex/overcloud/__init__.py create mode 100644 apex/overcloud/config.py create mode 100644 apex/overcloud/overcloud_deploy.py create mode 100644 apex/settings/__init__.py rename {lib/python/apex => apex/settings}/deploy_settings.py (92%) rename {lib/python/apex => apex/settings}/network_settings.py (88%) create mode 100644 apex/tests/__init__.py rename {tests => apex/tests}/config/inventory.yaml (100%) create mode 100644 apex/tests/constants.py create mode 100644 apex/tests/playbooks/test_playbook.yaml rename {tests => apex/tests}/smoke_tests/execute_smoke_tests.sh (100%) rename {tests => apex/tests}/smoke_tests/execute_tests.yml (100%) rename {tests => apex/tests}/smoke_tests/prepare_undercloud.yml (100%) rename {tests => apex/tests}/smoke_tests/smoke_tests.yml (100%) rename {tests => apex/tests}/test_apex_clean.py (95%) rename {tests => apex/tests}/test_apex_common_utils.py (58%) rename {tests => apex/tests}/test_apex_deploy_settings.py (83%) rename {tests => apex/tests}/test_apex_inventory.py (72%) rename {tests => apex/tests}/test_apex_ip_utils.py (94%) rename {tests => apex/tests}/test_apex_network_environment.py (77%) rename {tests => apex/tests}/test_apex_network_settings.py (81%) create mode 100644 apex/undercloud/__init__.py create mode 100644 apex/undercloud/undercloud.py create mode 100644 apex/virtual/__init__.py create mode 100755 apex/virtual/configure_vm.py create mode 100644 apex/virtual/virtual_utils.py rename {lib/installer => build}/domain.xml (100%) create mode 100644 lib/ansible/playbooks/configure_undercloud.yml create mode 100644 lib/ansible/playbooks/deploy_dependencies.yml create mode 100644 lib/ansible/playbooks/deploy_overcloud.yml create mode 100644 lib/ansible/playbooks/post_deploy_overcloud.yml create mode 100644 lib/ansible/playbooks/post_deploy_undercloud.yml create mode 100644 lib/ansible/playbooks/templates/external_vlan_ifcfg.yml.j2 create mode 100644 lib/ansible/playbooks/templates/virsh_network_default.xml.j2 create mode 100644 lib/ansible/playbooks/templates/virsh_network_ovs.xml.j2 create mode 100644 lib/ansible/playbooks/templates/virsh_pool.xml.j2 create mode 100644 lib/ansible/playbooks/undercloud_aarch64.yml delete mode 100644 lib/common-functions.sh delete mode 100755 lib/configure-deps-functions.sh delete mode 100755 lib/configure-vm delete mode 100755 lib/overcloud-deploy-functions.sh delete mode 100755 lib/parse-functions.sh delete mode 100755 lib/post-install-functions.sh delete mode 100644 lib/python/apex/common/utils.py delete mode 100755 lib/python/apex_python_utils.py delete mode 100755 lib/undercloud-functions.sh delete mode 100644 lib/utility-functions.sh delete mode 100755 lib/virtual-setup-functions.sh create mode 100644 requirements.txt create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 test-requirements.txt delete mode 100644 tests/test_apex_python_utils_py.py create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 511a0de1..47eaef64 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,10 @@ .build/ .cache/ ci/apex_build.log +ci/apex_deploy.log +.tox/ +apex.egg-info/ +/apex/tests/playbooks/*.retry +coverage.xml +nosetests.xml +ci/apex_clean.log diff --git a/lib/python/apex/__init__.py b/apex/__init__.py similarity index 68% rename from lib/python/apex/__init__.py rename to apex/__init__.py index b2a45f7d..4db820d9 100644 --- a/lib/python/apex/__init__.py +++ b/apex/__init__.py @@ -8,8 +8,8 @@ ############################################################################## -from .network_settings import NetworkSettings -from .deploy_settings import DeploySettings -from .network_environment import NetworkEnvironment +from apex.network.network_environment import NetworkEnvironment +from apex.settings.deploy_settings import DeploySettings +from apex.settings.network_settings import NetworkSettings from .clean import clean_nodes -from .inventory import Inventory +from .inventory.inventory import Inventory diff --git a/ci/build.py b/apex/build.py similarity index 98% rename from ci/build.py rename to apex/build.py index a17b21bd..cda4e061 100644 --- a/ci/build.py +++ b/apex/build.py @@ -20,6 +20,7 @@ TMP_CACHE = '.cache' BUILD_ROOT = 'build' BUILD_LOG_FILE = './apex_build.log' + class ApexBuildException(Exception): pass @@ -216,9 +217,12 @@ if __name__ == '__main__': console.setFormatter(logging.Formatter(formatter)) logging.getLogger('').addHandler(console) apex_root = os.path.split(os.getcwd())[0] + if 'apex/apex' in apex_root: + apex_root = os.path.split(apex_root)[0] for root, dirs, files in os.walk(apex_root): - if BUILD_ROOT in dirs: + if BUILD_ROOT in dirs and 'apex/apex' not in root: apex_root = root + break apex_build_root = os.path.join(apex_root, BUILD_ROOT) if os.path.isdir(apex_build_root): cache_tmp_dir = os.path.join(apex_root, TMP_CACHE) diff --git a/lib/python/apex/common/__init__.py b/apex/build/__init__.py similarity index 100% rename from lib/python/apex/common/__init__.py rename to apex/build/__init__.py diff --git a/lib/python/build_utils.py b/apex/build/build_utils.py similarity index 100% rename from lib/python/build_utils.py rename to apex/build/build_utils.py diff --git a/lib/python/apex/clean.py b/apex/clean.py similarity index 64% rename from lib/python/apex/clean.py rename to apex/clean.py index 184b5ec9..af9e8ce0 100644 --- a/lib/python/apex/clean.py +++ b/apex/clean.py @@ -9,7 +9,9 @@ # Clean will eventually be migrated to this file +import argparse import logging +import os import pyipmi import pyipmi.interfaces import sys @@ -37,3 +39,27 @@ def clean_nodes(inventory): except Exception as e: logging.error("Failure while shutting down node {}".format(e)) sys.exit(1) + + +def main(): + clean_parser = argparse.ArgumentParser() + clean_parser.add_argument('-f', + dest='inv_file', + required=True, + help='File which contains inventory') + args = clean_parser.parse_args(sys.argv[1:]) + os.makedirs(os.path.dirname('./apex_clean.log'), exist_ok=True) + formatter = '%(asctime)s %(levelname)s: %(message)s' + logging.basicConfig(filename='./apex_clean.log', + format=formatter, + datefmt='%m/%d/%Y %I:%M:%S %p', + level=logging.DEBUG) + console = logging.StreamHandler() + console.setLevel(logging.DEBUG) + console.setFormatter(logging.Formatter(formatter)) + logging.getLogger('').addHandler(console) + clean_nodes(args.inv_file) + + +if __name__ == '__main__': + main() diff --git a/apex/common/__init__.py b/apex/common/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lib/python/apex/common/constants.py b/apex/common/constants.py similarity index 74% rename from lib/python/apex/common/constants.py rename to apex/common/constants.py index 3aa28eab..0df71526 100644 --- a/lib/python/apex/common/constants.py +++ b/apex/common/constants.py @@ -7,6 +7,8 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## +import os + ADMIN_NETWORK = 'admin' TENANT_NETWORK = 'tenant' EXTERNAL_NETWORK = 'external' @@ -28,3 +30,17 @@ CONTROLLER_PRE = "OS::TripleO::ControllerExtraConfigPre" PRE_CONFIG_DIR = "/usr/share/openstack-tripleo-heat-templates/puppet/" \ "extraconfig/pre_deploy/" DEFAULT_ROOT_DEV = 'sda' +LIBVIRT_VOLUME_PATH = '/var/lib/libvirt/images' + +VIRT_UPLOAD = '--upload' +VIRT_INSTALL = '-install' +VIRT_RUN_CMD = '--run-command' +VIRT_PW = '--root-password' + +THT_DIR = '/usr/share/openstack-tripleo-heat-templates' +THT_ENV_DIR = os.path.join(THT_DIR, 'environments') + +DEFAULT_ODL_VERSION = 'carbon' +DEBUG_OVERCLOUD_PW = 'opnfvapex' +NET_ENV_FILE = 'network-environment.yaml' +DEPLOY_TIMEOUT = 90 diff --git a/apex/common/exceptions.py b/apex/common/exceptions.py new file mode 100644 index 00000000..c660213f --- /dev/null +++ b/apex/common/exceptions.py @@ -0,0 +1,12 @@ +############################################################################## +# Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + + +class ApexDeployException(Exception): + pass diff --git a/apex/common/parsers.py b/apex/common/parsers.py new file mode 100644 index 00000000..8744c862 --- /dev/null +++ b/apex/common/parsers.py @@ -0,0 +1,73 @@ +############################################################################## +# Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import json +import logging +import pprint +import os +import re + +from apex.common.exceptions import ApexDeployException + +"""Parser functions for overcloud/openstack output""" + + +def parse_nova_output(in_file): + """ + Parses nova list output into a dictionary format for node name and ip + :param in_file: json format from openstack server list + :return: dictionary format for {"node name": "node ip"} + """ + if not os.path.isfile(in_file): + raise FileNotFoundError(in_file) + node_dict = dict() + with open(in_file, 'r') as fh: + nova_list = json.load(fh) + + for server in nova_list: + ip_match = re.search('([0-9]+\.){3}[0-9]+', server['Networks']) + if ip_match is None: + logging.error("Unable to find IP in nova output " + "{}".format(pprint.pformat(server, indent=4))) + raise ApexDeployException("Unable to parse IP from nova output") + else: + node_dict[server['Name']] = ip_match.group(0) + + if not node_dict: + raise ApexDeployException("No overcloud nodes found in: {}".format( + in_file)) + return node_dict + + +def parse_overcloudrc(in_file): + """ + Parses overcloudrc into a dictionary format for key and value + :param in_file: + :return: dictionary format for {"variable": "value"} + """ + logging.debug("Parsing overcloudrc file {}".format(in_file)) + if not os.path.isfile(in_file): + raise FileNotFoundError(in_file) + creds = {} + with open(in_file, 'r') as fh: + lines = fh.readlines() + kv_pattern = re.compile('^export\s+([^\s]+)=([^\s]+)$') + for line in lines: + if 'export' not in line: + continue + else: + res = re.search(kv_pattern, line.strip()) + if res: + creds[res.group(1)] = res.group(2) + logging.debug("os cred found: {}, {}".format(res.group(1), + res.group(2))) + else: + logging.debug("os cred not found in: {}".format(line)) + + return creds diff --git a/apex/common/utils.py b/apex/common/utils.py new file mode 100644 index 00000000..848f2644 --- /dev/null +++ b/apex/common/utils.py @@ -0,0 +1,107 @@ +############################################################################## +# Copyright (c) 2016 Tim Rozet (trozet@redhat.com) and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import json +import logging +import os +import pprint +import subprocess +import yaml + + +def str2bool(var): + if isinstance(var, bool): + return var + else: + return var.lower() in ("true", "yes") + + +def parse_yaml(yaml_file): + with open(yaml_file) as f: + parsed_dict = yaml.safe_load(f) + return parsed_dict + + +def dump_yaml(data, file): + """ + Dumps data to a file as yaml + :param data: yaml to be written to file + :param file: filename to write to + :return: + """ + logging.debug("Writing file {} with " + "yaml data:\n{}".format(file, yaml.safe_dump(data))) + with open(file, "w") as fh: + yaml.safe_dump(data, fh, default_flow_style=False) + + +def dict_objects_to_str(dictionary): + if isinstance(dictionary, list): + tmp_list = [] + for element in dictionary: + if isinstance(element, dict): + tmp_list.append(dict_objects_to_str(element)) + else: + tmp_list.append(str(element)) + return tmp_list + elif not isinstance(dictionary, dict): + if not isinstance(dictionary, bool): + return str(dictionary) + else: + return dictionary + return dict((k, dict_objects_to_str(v)) for + k, v in dictionary.items()) + + +def run_ansible(ansible_vars, playbook, host='localhost', user='root', + tmp_dir=None, dry_run=False): + """ + Executes ansible playbook and checks for errors + :param ansible_vars: dictionary of variables to inject into ansible run + :param playbook: playbook to execute + :param tmp_dir: temp directory to store ansible command + :param dry_run: Do not actually apply changes + :return: None + """ + logging.info("Executing ansible playbook: {}".format(playbook)) + inv_host = "{},".format(host) + if host == 'localhost': + conn_type = 'local' + else: + conn_type = 'smart' + ansible_command = ['ansible-playbook', '--become', '-i', inv_host, + '-u', user, '-c', conn_type, playbook, '-vvv'] + if dry_run: + ansible_command.append('--check') + + if isinstance(ansible_vars, dict) and ansible_vars: + logging.debug("Ansible variables to be set:\n{}".format( + pprint.pformat(ansible_vars))) + ansible_command.append('--extra-vars') + ansible_command.append(json.dumps(ansible_vars)) + if tmp_dir: + ansible_tmp = os.path.join(tmp_dir, + os.path.basename(playbook) + '.rerun') + # FIXME(trozet): extra vars are printed without single quotes + # so a dev has to add them manually to the command to rerun + # the playbook. Need to test if we can just add the single quotes + # to the json dumps to the ansible command and see if that works + with open(ansible_tmp, 'w') as fh: + fh.write("ANSIBLE_HOST_KEY_CHECKING=FALSE {}".format( + ' '.join(ansible_command))) + try: + my_env = os.environ.copy() + my_env['ANSIBLE_HOST_KEY_CHECKING'] = 'False' + logging.info("Executing playbook...this may take some time") + logging.debug(subprocess.check_output(ansible_command, env=my_env, + stderr=subprocess.STDOUT).decode('utf-8')) + except subprocess.CalledProcessError as e: + logging.error("Error executing ansible: {}".format( + pprint.pformat(e.output.decode('utf-8')))) + raise diff --git a/apex/deploy.py b/apex/deploy.py new file mode 100644 index 00000000..76708e96 --- /dev/null +++ b/apex/deploy.py @@ -0,0 +1,441 @@ +#!/usr/bin/env python + +############################################################################## +# Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import argparse +import json +import logging +import os +import pprint +import shutil +import sys +import tempfile + +import apex.virtual.configure_vm as vm_lib +import apex.virtual.virtual_utils as virt_utils +from apex import DeploySettings +from apex import Inventory +from apex import NetworkEnvironment +from apex import NetworkSettings +from apex.common import utils +from apex.common import constants +from apex.common import parsers +from apex.common.exceptions import ApexDeployException +from apex.network import jumphost +from apex.undercloud import undercloud as uc_lib +from apex.overcloud import config as oc_cfg +from apex.overcloud import overcloud_deploy + +APEX_TEMP_DIR = tempfile.mkdtemp() +ANSIBLE_PATH = 'ansible/playbooks' +SDN_IMAGE = 'overcloud-full-opendaylight.qcow2' + + +def deploy_quickstart(args, deploy_settings_file, network_settings_file, + inventory_file=None): + pass + + +def validate_cross_settings(deploy_settings, net_settings, inventory): + """ + Used to validate compatibility across settings file. + :param deploy_settings: parsed settings for deployment + :param net_settings: parsed settings for network + :param inventory: parsed inventory file + :return: None + """ + + if deploy_settings['deploy_options']['dataplane'] != 'ovs' and 'tenant' \ + not in net_settings.enabled_network_list: + raise ApexDeployException("Setting a DPDK based dataplane requires" + "a dedicated NIC for tenant network") + + # TODO(trozet): add more checks here like RAM for ODL, etc + # check if odl_vpp_netvirt is true and vpp is set + # Check if fdio and nosdn: + # tenant_nic_mapping_controller_members" == + # "$tenant_nic_mapping_compute_members + + +def build_vms(inventory, network_settings): + """ + Creates VMs and configures vbmc and host + :param inventory: + :param network_settings: + :return: + """ + + for idx, node in enumerate(inventory['nodes']): + name = 'baremetal{}'.format(idx) + volume = name + ".qcow2" + volume_path = os.path.join(constants.LIBVIRT_VOLUME_PATH, volume) + # TODO(trozet): add back aarch64 + # TODO(trozet): add error checking + vm_lib.create_vm( + name, volume_path, + baremetal_interfaces=network_settings.enabled_network_list, + memory=node['memory'], cpus=node['cpu'], + macs=[node['mac_address']]) + virt_utils.host_setup({name: node['pm_port']}) + + +def create_deploy_parser(): + deploy_parser = argparse.ArgumentParser() + deploy_parser.add_argument('--debug', action='store_true', default=False, + help="Turn on debug messages") + deploy_parser.add_argument('-l', '--log-file', + default='./apex_deploy.log', + dest='log_file', help="Log file to log to") + deploy_parser.add_argument('-d', '--deploy-settings', + dest='deploy_settings_file', + required=True, + help='File which contains Apex deploy settings') + deploy_parser.add_argument('-n', '--network-settings', + dest='network_settings_file', + required=True, + help='File which contains Apex network ' + 'settings') + deploy_parser.add_argument('-i', '--inventory-file', + dest='inventory_file', + default=None, + help='Inventory file which contains POD ' + 'definition') + deploy_parser.add_argument('-e', '--environment-file', + dest='env_file', + default='opnfv-environment.yaml', + help='Provide alternate base env file') + deploy_parser.add_argument('-v', '--virtual', action='store_true', + default=False, + dest='virtual', + help='Enable virtual deployment') + deploy_parser.add_argument('--interactive', action='store_true', + default=False, + help='Enable interactive deployment mode which ' + 'requires user to confirm steps of ' + 'deployment') + deploy_parser.add_argument('--virtual-computes', + dest='virt_compute_nodes', + default=1, + help='Number of Virtual Compute nodes to create' + ' and use during deployment (defaults to 1' + ' for noha and 2 for ha)') + deploy_parser.add_argument('--virtual-cpus', + dest='virt_cpus', + default=4, + help='Number of CPUs to use per Overcloud VM in' + ' a virtual deployment (defaults to 4)') + deploy_parser.add_argument('--virtual-default-ram', + dest='virt_default_ram', + default=8, + help='Amount of default RAM to use per ' + 'Overcloud VM in GB (defaults to 8).') + deploy_parser.add_argument('--virtual-compute-ram', + dest='virt_compute_ram', + default=None, + help='Amount of RAM to use per Overcloud ' + 'Compute VM in GB (defaults to 8). ' + 'Overrides --virtual-default-ram arg for ' + 'computes') + deploy_parser.add_argument('--deploy-dir', + default='/usr/share/opnfv-apex', + help='Directory to deploy from which contains ' + 'base config files for deployment') + deploy_parser.add_argument('--image-dir', + default='/var/opt/opnfv/images', + help='Directory which contains ' + 'base disk images for deployment') + deploy_parser.add_argument('--lib-dir', + default='/usr/share/opnfv-apex', + help='Directory path for apex ansible ' + 'and third party libs') + deploy_parser.add_argument('--quickstart', action='store_true', + default=False, + help='Use tripleo-quickstart to deploy') + return deploy_parser + + +def validate_deploy_args(args): + """ + Validates arguments for deploy + :param args: + :return: None + """ + + logging.debug('Validating arguments for deployment') + if args.virtual and args.inventory_file is not None: + logging.error("Virtual enabled but inventory file also given") + raise ApexDeployException('You should not specify an inventory file ' + 'with virtual deployments') + elif args.virtual: + args.inventory_file = os.path.join(APEX_TEMP_DIR, + 'inventory-virt.yaml') + elif os.path.isfile(args.inventory_file) is False: + logging.error("Specified inventory file does not exist: {}".format( + args.inventory_file)) + raise ApexDeployException('Specified inventory file does not exist') + + for settings_file in (args.deploy_settings_file, + args.network_settings_file): + if os.path.isfile(settings_file) is False: + logging.error("Specified settings file does not " + "exist: {}".format(settings_file)) + raise ApexDeployException('Specified settings file does not ' + 'exist: {}'.format(settings_file)) + + +def main(): + parser = create_deploy_parser() + args = parser.parse_args(sys.argv[1:]) + # FIXME (trozet): this is only needed as a workaround for CI. Remove + # when CI is changed + if os.getenv('IMAGES', False): + args.image_dir = os.getenv('IMAGES') + if args.debug: + log_level = logging.DEBUG + else: + log_level = logging.INFO + os.makedirs(os.path.dirname(args.log_file), exist_ok=True) + formatter = '%(asctime)s %(levelname)s: %(message)s' + logging.basicConfig(filename=args.log_file, + format=formatter, + datefmt='%m/%d/%Y %I:%M:%S %p', + level=log_level) + console = logging.StreamHandler() + console.setLevel(log_level) + console.setFormatter(logging.Formatter(formatter)) + logging.getLogger('').addHandler(console) + validate_deploy_args(args) + # Parse all settings + deploy_settings = DeploySettings(args.deploy_settings_file) + logging.info("Deploy settings are:\n {}".format(pprint.pformat( + deploy_settings))) + net_settings = NetworkSettings(args.network_settings_file) + logging.info("Network settings are:\n {}".format(pprint.pformat( + net_settings))) + net_env_file = os.path.join(args.deploy_dir, constants.NET_ENV_FILE) + net_env = NetworkEnvironment(net_settings, net_env_file) + net_env_target = os.path.join(APEX_TEMP_DIR, constants.NET_ENV_FILE) + utils.dump_yaml(dict(net_env), net_env_target) + ha_enabled = deploy_settings['global_params']['ha_enabled'] + if args.virtual: + if args.virt_compute_ram is None: + compute_ram = args.virt_default_ram + else: + compute_ram = args.virt_compute_ram + if deploy_settings['deploy_options']['sdn_controller'] == \ + 'opendaylight' and args.virt_default_ram < 12: + control_ram = 12 + logging.warning('RAM per controller is too low. OpenDaylight ' + 'requires at least 12GB per controller.') + logging.info('Increasing RAM per controller to 12GB') + elif args.virt_default_ram < 10: + control_ram = 10 + logging.warning('RAM per controller is too low. nosdn ' + 'requires at least 10GB per controller.') + logging.info('Increasing RAM per controller to 10GB') + else: + control_ram = args.virt_default_ram + if ha_enabled and args.virt_compute_nodes < 2: + logging.debug('HA enabled, bumping number of compute nodes to 2') + args.virt_compute_nodes = 2 + virt_utils.generate_inventory(args.inventory_file, ha_enabled, + num_computes=args.virt_compute_nodes, + controller_ram=control_ram * 1024, + compute_ram=compute_ram * 1024, + vcpus=args.virt_cpus + ) + inventory = Inventory(args.inventory_file, ha_enabled, args.virtual) + + validate_cross_settings(deploy_settings, net_settings, inventory) + + if args.quickstart: + deploy_settings_file = os.path.join(APEX_TEMP_DIR, + 'apex_deploy_settings.yaml') + utils.dump_yaml(utils.dict_objects_to_str(deploy_settings), + deploy_settings_file) + logging.info("File created: {}".format(deploy_settings_file)) + network_settings_file = os.path.join(APEX_TEMP_DIR, + 'apex_network_settings.yaml') + utils.dump_yaml(utils.dict_objects_to_str(net_settings), + network_settings_file) + logging.info("File created: {}".format(network_settings_file)) + deploy_quickstart(args, deploy_settings_file, network_settings_file, + args.inventory_file) + else: + # TODO (trozet): add logic back from: + # Iedb75994d35b5dc1dd5d5ce1a57277c8f3729dfd (FDIO DVR) + ansible_args = { + 'virsh_enabled_networks': net_settings.enabled_network_list + } + ansible_path = os.path.join(args.lib_dir, ANSIBLE_PATH) + utils.run_ansible(ansible_args, + os.path.join(args.lib_dir, + ansible_path, + 'deploy_dependencies.yml')) + uc_external = False + if 'external' in net_settings.enabled_network_list: + uc_external = True + if args.virtual: + # create all overcloud VMs + build_vms(inventory, net_settings) + else: + # Attach interfaces to jumphost for baremetal deployment + jump_networks = ['admin'] + if uc_external: + jump_networks.append('external') + for network in jump_networks: + iface = net_settings['network'][network]['installer_vm'][ + 'members'](0) + bridge = "br-{}".format(network) + jumphost.attach_interface_to_ovs(bridge, iface, network) + # Dump all settings out to temp bash files to be sourced + instackenv_json = os.path.join(APEX_TEMP_DIR, 'instackenv.json') + with open(instackenv_json, 'w') as fh: + json.dump(inventory, fh) + + # Create and configure undercloud + if args.debug: + root_pw = constants.DEBUG_OVERCLOUD_PW + else: + root_pw = None + undercloud = uc_lib.Undercloud(args.image_dir, + root_pw=root_pw, + external_network=uc_external) + undercloud.start() + + # Generate nic templates + for role in 'compute', 'controller': + oc_cfg.create_nic_template(net_settings, deploy_settings, role, + args.deploy_dir, APEX_TEMP_DIR) + # Install Undercloud + undercloud.configure(net_settings, + os.path.join(args.lib_dir, + ansible_path, + 'configure_undercloud.yml'), + APEX_TEMP_DIR) + + # Prepare overcloud-full.qcow2 + logging.info("Preparing Overcloud for deployment...") + sdn_image = os.path.join(args.image_dir, SDN_IMAGE) + overcloud_deploy.prep_image(deploy_settings, sdn_image, APEX_TEMP_DIR, + root_pw=root_pw) + opnfv_env = os.path.join(args.deploy_dir, args.env_file) + overcloud_deploy.prep_env(deploy_settings, net_settings, opnfv_env, + net_env_target, APEX_TEMP_DIR) + overcloud_deploy.create_deploy_cmd(deploy_settings, net_settings, + inventory, APEX_TEMP_DIR, + args.virtual, args.env_file) + deploy_playbook = os.path.join(args.lib_dir, ansible_path, + 'deploy_overcloud.yml') + virt_env = 'virtual-environment.yaml' + bm_env = 'baremetal-environment.yaml' + for p_env in virt_env, bm_env: + shutil.copyfile(os.path.join(args.deploy_dir, p_env), + os.path.join(APEX_TEMP_DIR, p_env)) + + # Start Overcloud Deployment + logging.info("Executing Overcloud Deployment...") + deploy_vars = dict() + deploy_vars['virtual'] = args.virtual + deploy_vars['debug'] = args.debug + deploy_vars['dns_server_args'] = '' + deploy_vars['apex_temp_dir'] = APEX_TEMP_DIR + deploy_vars['stackrc'] = 'source /home/stack/stackrc' + deploy_vars['overcloudrc'] = 'source /home/stack/overcloudrc' + for dns_server in net_settings['dns_servers']: + deploy_vars['dns_server_args'] += " --dns-nameserver {}".format( + dns_server) + try: + utils.run_ansible(deploy_vars, deploy_playbook, host=undercloud.ip, + user='stack', tmp_dir=APEX_TEMP_DIR) + logging.info("Overcloud deployment complete") + os.remove(os.path.join(APEX_TEMP_DIR, 'overcloud-full.qcow2')) + except Exception: + logging.error("Deployment Failed. Please check log") + raise + + # Post install + logging.info("Executing post deploy configuration") + jumphost.configure_bridges(net_settings) + nova_output = os.path.join(APEX_TEMP_DIR, 'nova_output') + deploy_vars['overcloud_nodes'] = parsers.parse_nova_output( + nova_output) + deploy_vars['SSH_OPTIONS'] = '-o StrictHostKeyChecking=no -o ' \ + 'GlobalKnownHostsFile=/dev/null -o ' \ + 'UserKnownHostsFile=/dev/null -o ' \ + 'LogLevel=error' + deploy_vars['external_network_cmds'] = \ + overcloud_deploy.external_network_cmds(net_settings) + # TODO(trozet): just parse all ds_opts as deploy vars one time + ds_opts = deploy_settings['deploy_options'] + deploy_vars['gluon'] = ds_opts['gluon'] + deploy_vars['sdn'] = ds_opts['sdn_controller'] + for dep_option in 'yardstick', 'dovetail', 'vsperf': + if dep_option in ds_opts: + deploy_vars[dep_option] = ds_opts[dep_option] + else: + deploy_vars[dep_option] = False + deploy_vars['dataplane'] = ds_opts['dataplane'] + overcloudrc = os.path.join(APEX_TEMP_DIR, 'overcloudrc') + if ds_opts['congress']: + deploy_vars['congress_datasources'] = \ + overcloud_deploy.create_congress_cmds(overcloudrc) + deploy_vars['congress'] = True + else: + deploy_vars['congress'] = False + # TODO(trozet): this is probably redundant with getting external + # network info from undercloud.py + if 'external' in net_settings.enabled_network_list: + ext_cidr = net_settings['networks']['external'][0]['cidr'] + else: + ext_cidr = net_settings['networks']['admin']['cidr'] + deploy_vars['external_cidr'] = str(ext_cidr) + if ext_cidr.version == 6: + deploy_vars['external_network_ipv6'] = True + else: + deploy_vars['external_network_ipv6'] = False + post_undercloud = os.path.join(args.lib_dir, ansible_path, + 'post_deploy_undercloud.yml') + logging.info("Executing post deploy configuration undercloud playbook") + try: + utils.run_ansible(deploy_vars, post_undercloud, host=undercloud.ip, + user='stack', tmp_dir=APEX_TEMP_DIR) + logging.info("Post Deploy Undercloud Configuration Complete") + except Exception: + logging.error("Post Deploy Undercloud Configuration failed. " + "Please check log") + raise + # Post deploy overcloud node configuration + # TODO(trozet): just parse all ds_opts as deploy vars one time + deploy_vars['sfc'] = ds_opts['sfc'] + deploy_vars['vpn'] = ds_opts['vpn'] + # TODO(trozet): pull all logs and store in tmp dir in overcloud + # playbook + post_overcloud = os.path.join(args.lib_dir, ansible_path, + 'post_deploy_overcloud.yml') + # Run per overcloud node + for node, ip in deploy_vars['overcloud_nodes'].items(): + logging.info("Executing Post deploy overcloud playbook on " + "node {}".format(node)) + try: + utils.run_ansible(deploy_vars, post_overcloud, host=ip, + user='heat-admin', tmp_dir=APEX_TEMP_DIR) + logging.info("Post Deploy Overcloud Configuration Complete " + "for node {}".format(node)) + except Exception: + logging.error("Post Deploy Overcloud Configuration failed " + "for node {}. Please check log".format(node)) + raise + logging.info("Apex deployment complete") + logging.info("Undercloud IP: {}, please connect by doing " + "'opnfv-util undercloud'".format(undercloud.ip)) + # TODO(trozet): add logging here showing controller VIP and horizon url +if __name__ == '__main__': + main() diff --git a/apex/inventory/__init__.py b/apex/inventory/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lib/python/apex/inventory.py b/apex/inventory/inventory.py similarity index 88% rename from lib/python/apex/inventory.py rename to apex/inventory/inventory.py index 64f47b49..dd731a83 100644 --- a/lib/python/apex/inventory.py +++ b/apex/inventory/inventory.py @@ -7,12 +7,13 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -import yaml import json import platform -from .common import constants -from .common import utils +import yaml + +from apex.common import constants +from apex.common import utils class Inventory(dict): @@ -60,7 +61,7 @@ class Inventory(dict): super().__init__({'nodes': list(map(munge_nodes, init_dict['nodes']))}) # verify number of nodes - if ha and len(self['nodes']) < 5: + if ha and len(self['nodes']) < 5 and not virtual: raise InventoryException('You must provide at least 5 ' 'nodes for HA baremetal deployment') elif len(self['nodes']) < 2: @@ -79,16 +80,6 @@ class Inventory(dict): def dump_instackenv_json(self): print(json.dumps(dict(self), sort_keys=True, indent=4)) - def dump_bash(self, path=None): - """ - Prints settings for bash consumption. - - If optional path is provided, bash string will be written to the file - instead of stdout. - """ - bash_str = "{}={}\n".format('root_disk_list', str(self.root_device)) - utils.write_str(bash_str, path) - class InventoryException(Exception): def __init__(self, value): diff --git a/apex/network/__init__.py b/apex/network/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lib/python/apex/ip_utils.py b/apex/network/ip_utils.py similarity index 100% rename from lib/python/apex/ip_utils.py rename to apex/network/ip_utils.py diff --git a/apex/network/jumphost.py b/apex/network/jumphost.py new file mode 100644 index 00000000..81562c7a --- /dev/null +++ b/apex/network/jumphost.py @@ -0,0 +1,172 @@ +############################################################################## +# Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import logging +import os +import re +import shutil +import subprocess + +from apex.common.exceptions import ApexDeployException +from apex.network import ip_utils + +NET_MAP = { + 'admin': 'br-admin', + 'tenant': 'br-tenant', + 'external': 'br-external', + 'storage': 'br-storage', + 'api': 'br-api' +} + + +def configure_bridges(ns): + """ + Configures IP on jumphost bridges + :param ns: network_settings + :return: None + """ + bridge_networks = ['admin'] + if 'external' in ns.enabled_network_list: + bridge_networks.append('external') + for network in bridge_networks: + if network == 'external': + net_config = ns['networks'][network][0] + else: + net_config = ns['networks'][network] + cidr = net_config['cidr'] + interface = ip_utils.get_interface(NET_MAP[network], cidr.version) + + if interface: + logging.info("Bridge {} already configured with IP: {}".format( + NET_MAP[network], interface.ip)) + else: + logging.info("Will configure IP for {}".format(NET_MAP[network])) + ovs_ip = net_config['overcloud_ip_range'][1] + if cidr.version == 6: + ipv6_br_path = "/proc/sys/net/ipv6/conf/{}/disable_" \ + "ipv6".format(NET_MAP[network]) + try: + subprocess.check_call('echo', 0, '>', ipv6_br_path) + except subprocess.CalledProcessError: + logging.error("Unable to enable ipv6 on " + "bridge {}".format(NET_MAP[network])) + raise + try: + ip_prefix = "{}/{}".format(ovs_ip, cidr.prefixlen) + subprocess.check_call(['ip', 'addr', 'add', ip_prefix, 'dev', + NET_MAP[network]]) + subprocess.check_call(['ip', 'link', 'set', 'up', NET_MAP[ + network]]) + logging.info("IP configured: {} on bridge {}".format(ovs_ip, + NET_MAP[network])) + except subprocess.CalledProcessError: + logging.error("Unable to configure IP address on " + "bridge {}".format(NET_MAP[network])) + + +def attach_interface_to_ovs(bridge, interface, network): + """ + Attaches jumphost interface to OVS for baremetal deployments + :param bridge: bridge to attach to + :param interface: interface to attach to bridge + :param network: Apex network type for these interfaces + :return: None + """ + + net_cfg_path = '/etc/sysconfig/network-scripts' + if_file = os.path.join(net_cfg_path, "ifcfg-{}".format(interface)) + ovs_file = os.path.join(net_cfg_path, "ifcfg-{}".format(bridge)) + + logging.info("Attaching interface: {} to bridge: {} on network {}".format( + bridge, interface, network + )) + + try: + output = subprocess.check_output(['ovs-vsctl', 'list-ports', bridge], + stderr=subprocess.STDOUT) + if bridge in output: + logging.debug("Interface already attached to bridge") + return + except subprocess.CalledProcessError as e: + logging.error("Unable to dump ports for bridge: {}".format(bridge)) + logging.error("Error output: {}".format(e.output)) + raise + + if not os.path.isfile(if_file): + logging.error("Interface ifcfg not found: {}".format(if_file)) + raise FileNotFoundError("Interface file missing: {}".format(if_file)) + + ifcfg_params = { + 'IPADDR': '', + 'NETMASK': '', + 'GATEWAY': '', + 'METRIC': '', + 'DNS1': '', + 'DNS2': '', + 'PREFIX': '' + } + with open(if_file, 'r') as fh: + interface_output = fh.read() + + for param in ifcfg_params.keys(): + match = re.search("{}=(.*)\n".format(param), interface_output) + if match: + ifcfg_params[param] = match.group(1) + + if not ifcfg_params['IPADDR']: + logging.error("IPADDR missing in {}".format(if_file)) + raise ApexDeployException("IPADDR missing in {}".format(if_file)) + if not (ifcfg_params['NETMASK'] or ifcfg_params['PREFIX']): + logging.error("NETMASK/PREFIX missing in {}".format(if_file)) + raise ApexDeployException("NETMASK/PREFIX missing in {}".format( + if_file)) + if network == 'external' and not ifcfg_params['GATEWAY']: + logging.error("GATEWAY is required to be in {} for external " + "network".format(if_file)) + raise ApexDeployException("GATEWAY is required to be in {} for " + "external network".format(if_file)) + + shutil.move(if_file, "{}.orig".format(if_file)) + if_content = """DEVICE={} +DEVICETYPE=ovs +TYPE=OVSPort +PEERDNS=no +BOOTPROTO=static +NM_CONTROLLED=no +ONBOOT=yes +OVS_BRIDGE={} +PROMISC=yes""".format(interface, bridge) + + bridge_content = """DEVICE={} +DEVICETYPE=ovs +BOOTPROTO=static +ONBOOT=yes +TYPE=OVSBridge +PROMISC=yes""".format(bridge) + peer_dns = 'no' + for param, value in ifcfg_params.items(): + if value: + bridge_content += "\n{}={}".format(param, value) + if param == 'DNS1' or param == 'DNS2': + peer_dns = 'yes' + bridge_content += "\n{}={}".format('PEERDNS', peer_dns) + + logging.debug("New interface file content:\n{}".format(if_content)) + logging.debug("New bridge file content:\n{}".format(bridge_content)) + with open(if_file, 'w') as fh: + fh.write(if_content) + with open(ovs_file, 'w') as fh: + fh.write(bridge_content) + logging.info("New network ifcfg files written") + logging.info("Restarting Linux networking") + try: + subprocess.check_call(['systemctl', 'restart', 'network']) + except subprocess.CalledProcessError: + logging.error("Failed to restart Linux networking") + raise diff --git a/lib/python/apex/network_environment.py b/apex/network/network_environment.py similarity index 98% rename from lib/python/apex/network_environment.py rename to apex/network/network_environment.py index dd9530b8..c2e9991a 100644 --- a/lib/python/apex/network_environment.py +++ b/apex/network/network_environment.py @@ -7,21 +7,20 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -import yaml import re -from .common.constants import ( + +import yaml + +from apex.settings.network_settings import NetworkSettings +from apex.common.constants import ( CONTROLLER, COMPUTE, ADMIN_NETWORK, TENANT_NETWORK, STORAGE_NETWORK, EXTERNAL_NETWORK, - API_NETWORK, - CONTROLLER_PRE, - COMPUTE_PRE, - PRE_CONFIG_DIR + API_NETWORK ) -from .network_settings import NetworkSettings HEAT_NONE = 'OS::Heat::None' PORTS = '/ports' diff --git a/apex/overcloud/__init__.py b/apex/overcloud/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apex/overcloud/config.py b/apex/overcloud/config.py new file mode 100644 index 00000000..6e116de2 --- /dev/null +++ b/apex/overcloud/config.py @@ -0,0 +1,76 @@ +############################################################################## +# Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +""" +Utilities for generating overcloud configuration +""" + +import logging +import os + +from jinja2 import Environment +from jinja2 import FileSystemLoader +from apex.common.exceptions import ApexDeployException + + +def create_nic_template(network_settings, deploy_settings, role, template_dir, + target_dir): + """ + Creates NIC heat template files + :param ns: Network settings + :param ds: Deploy Settings + :param role: controller or compute + :param template_dir: directory where base templates are stored + :param target_dir: to store rendered nic template + :return: + """ + # TODO(trozet): rather than use Jinja2 to build these files, use with py + if role not in ['controller', 'compute']: + raise ApexDeployException("Invalid type for overcloud node: {" + "}".format(type)) + logging.info("Creating template for {}".format(role)) + template_file = 'nics-template.yaml.jinja2' + nets = network_settings.get('networks') + env = Environment(loader=FileSystemLoader(template_dir), autoescape=True) + template = env.get_template(template_file) + ds = deploy_settings.get('deploy_options') + ext_net = 'br-ex' + ovs_dpdk_br = '' + if ds['dataplane'] == 'fdio': + nets['tenant']['nic_mapping'][role]['phys_type'] = 'vpp_interface' + if ds['sdn_controller'] == 'opendaylight': + nets['external'][0]['nic_mapping'][role]['phys_type'] = \ + 'vpp_interface' + ext_net = 'vpp_interface' + elif ds['dataplane'] == 'ovs_dpdk': + ovs_dpdk_br = 'br-phy' + if (ds.get('performance', {}).get(role.title(), {}).get('vpp', {}) + .get('uio-driver')): + nets['tenant']['nic_mapping'][role]['uio-driver'] =\ + ds['performance'][role.title()]['vpp']['uio-driver'] + if ds['sdn_controller'] == 'opendaylight': + nets['external'][0]['nic_mapping'][role]['uio-driver'] =\ + ds['performance'][role.title()]['vpp']['uio-driver'] + if (ds.get('performance', {}).get(role.title(), {}).get('vpp', {}) + .get('interface-options')): + nets['tenant']['nic_mapping'][role]['interface-options'] =\ + ds['performance'][role.title()]['vpp']['interface-options'] + + template_output = template.render( + nets=nets, + role=role, + external_net_af=network_settings.get_ip_addr_family(), + external_net_type=ext_net, + ovs_dpdk_bridge=ovs_dpdk_br) + + logging.debug("Template output: {}".format(template_output)) + target = os.path.join(target_dir, "{}.yaml".format(role)) + with open(target, "w") as f: + f.write(template_output) + logging.info("Wrote template {}".format(target)) diff --git a/apex/overcloud/overcloud_deploy.py b/apex/overcloud/overcloud_deploy.py new file mode 100644 index 00000000..3c108464 --- /dev/null +++ b/apex/overcloud/overcloud_deploy.py @@ -0,0 +1,556 @@ +############################################################################## +# Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import base64 +import fileinput +import logging +import os +import re +import shutil +import uuid +import struct +import time + +from apex.common import constants as con +from apex.common.exceptions import ApexDeployException +from apex.common import parsers +from apex.virtual import virtual_utils as virt_utils +from cryptography.hazmat.primitives import serialization as \ + crypto_serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.backends import default_backend as \ + crypto_default_backend + + +SDN_FILE_MAP = { + 'opendaylight': { + 'sfc': 'opendaylight_sfc.yaml', + 'vpn': 'neutron-bgpvpn-opendaylight.yaml', + 'gluon': 'gluon.yaml', + 'vpp': { + 'odl_vpp_netvirt': 'neutron-opendaylight-netvirt-vpp.yaml', + 'default': 'neutron-opendaylight-honeycomb.yaml' + }, + 'default': 'neutron-opendaylight.yaml', + }, + 'onos': { + 'sfc': 'neutron-onos-sfc.yaml', + 'default': 'neutron-onos.yaml' + }, + 'ovn': 'neutron-ml2-ovn.yaml', + False: { + 'vpp': 'neutron-ml2-vpp.yaml', + 'dataplane': ('ovs_dpdk', 'neutron-ovs-dpdk.yaml') + } +} + +OTHER_FILE_MAP = { + 'tacker': 'enable_tacker.yaml', + 'congress': 'enable_congress.yaml', + 'barometer': 'enable_barometer.yaml', + 'rt_kvm': 'enable_rt_kvm.yaml' +} + +OVS_PERF_MAP = { + 'HostCpusList': 'dpdk_cores', + 'NeutronDpdkCoreList': 'pmd_cores', + 'NeutronDpdkSocketMemory': 'socket_memory', + 'NeutronDpdkMemoryChannels': 'memory_channels' +} + +OVS_NSH_KMOD_RPM = "openvswitch-kmod-2.6.1-1.el7.centos.x86_64.rpm" +OVS_NSH_RPM = "openvswitch-2.6.1-1.el7.centos.x86_64.rpm" +ODL_NETVIRT_VPP_RPM = "/root/opendaylight-7.0.0-0.1.20170531snap665.el7" \ + ".noarch.rpm" + + +def build_sdn_env_list(ds, sdn_map, env_list=None): + if env_list is None: + env_list = list() + for k, v in sdn_map.items(): + if ds['sdn_controller'] == k or (k in ds and ds[k] is True): + if isinstance(v, dict): + env_list.extend(build_sdn_env_list(ds, v)) + else: + env_list.append(os.path.join(con.THT_ENV_DIR, v)) + elif isinstance(v, tuple): + if ds[k] == v[0]: + env_list.append(os.path.join(con.THT_ENV_DIR, v[1])) + if len(env_list) == 0: + try: + env_list.append(os.path.join( + con.THT_ENV_DIR, sdn_map[ds['sdn_controller']]['default'])) + except KeyError: + logging.warning("Unable to find default file for SDN") + + return env_list + + +def create_deploy_cmd(ds, ns, inv, tmp_dir, + virtual, env_file='opnfv-environment.yaml'): + + logging.info("Creating deployment command") + deploy_options = [env_file, 'network-environment.yaml'] + ds_opts = ds['deploy_options'] + deploy_options += build_sdn_env_list(ds_opts, SDN_FILE_MAP) + + # TODO(trozet): make sure rt kvm file is in tht dir + for k, v in OTHER_FILE_MAP.items(): + if k in ds_opts and ds_opts[k]: + deploy_options.append(os.path.join(con.THT_ENV_DIR, v)) + + if ds_opts['ceph']: + prep_storage_env(ds, tmp_dir) + deploy_options.append(os.path.join(con.THT_ENV_DIR, + 'storage-environment.yaml')) + if ds['global_params']['ha_enabled']: + deploy_options.append(os.path.join(con.THT_ENV_DIR, + 'puppet-pacemaker.yaml')) + + if virtual: + deploy_options.append('virtual-environment.yaml') + else: + deploy_options.append('baremetal-environment.yaml') + + nodes = inv['nodes'] + num_control = 0 + num_compute = 0 + for node in nodes: + if node['capabilities'] == 'profile:control': + num_control += 1 + elif node['capabilities'] == 'profile:compute': + num_compute += 1 + else: + # TODO(trozet) do we want to allow capabilities to not exist? + logging.error("Every node must include a 'capabilities' key " + "tagged with either 'profile:control' or " + "'profile:compute'") + raise ApexDeployException("Node missing capabilities " + "key: {}".format(node)) + if num_control == 0 or num_compute == 0: + logging.error("Detected 0 control or compute nodes. Control nodes: " + "{}, compute nodes{}".format(num_control, num_compute)) + raise ApexDeployException("Invalid number of control or computes") + cmd = "openstack overcloud deploy --templates --timeout {} " \ + "--libvirt-type kvm".format(con.DEPLOY_TIMEOUT) + # build cmd env args + for option in deploy_options: + cmd += " -e {}".format(option) + cmd += " --ntp-server {}".format(ns['ntp'][0]) + cmd += " --control-scale {}".format(num_control) + cmd += " --compute-scale {}".format(num_compute) + cmd += ' --control-flavor control --compute-flavor compute' + logging.info("Deploy command set: {}".format(cmd)) + + with open(os.path.join(tmp_dir, 'deploy_command'), 'w') as fh: + fh.write(cmd) + return cmd + + +def prep_image(ds, img, tmp_dir, root_pw=None): + """ + Locates sdn image and preps for deployment. + :param ds: deploy settings + :param img: sdn image + :param tmp_dir: dir to store modified sdn image + :param root_pw: password to configure for overcloud image + :return: None + """ + # TODO(trozet): Come up with a better way to organize this logic in this + # function + logging.info("Preparing image: {} for deployment".format(img)) + if not os.path.isfile(img): + logging.error("Missing SDN image {}".format(img)) + raise ApexDeployException("Missing SDN image file: {}".format(img)) + + ds_opts = ds['deploy_options'] + virt_cmds = list() + sdn = ds_opts['sdn_controller'] + # we need this due to rhbz #1436021 + # fixed in systemd-219-37.el7 + if sdn is not False: + logging.info("Neutron openvswitch-agent disabled") + virt_cmds.extend([{ + con.VIRT_RUN_CMD: + "rm -f /etc/systemd/system/multi-user.target.wants/" + "neutron-openvswitch-agent.service"}, + { + con.VIRT_RUN_CMD: + "rm -f /usr/lib/systemd/system/neutron-openvswitch-agent" + ".service" + }]) + + if ds_opts['vpn']: + virt_cmds.append({con.VIRT_RUN_CMD: "systemctl enable zrpcd"}) + logging.info("ZRPC and Quagga enabled") + + dataplane = ds_opts['dataplane'] + if dataplane == 'ovs_dpdk' or dataplane == 'fdio': + logging.info("Enabling kernel modules for dpdk") + # file to module mapping + uio_types = { + os.path.join(tmp_dir, 'vfio_pci.modules'): 'vfio_pci', + os.path.join(tmp_dir, 'uio_pci_generic.modules'): 'uio_pci_generic' + } + for mod_file, mod in uio_types: + with open(mod_file, 'w') as fh: + fh.write('#!/bin/bash\n') + fh.write('exec /sbin/modprobe {}'.format(mod)) + fh.close() + + virt_cmds.extend([ + {con.VIRT_UPLOAD: "{}:/etc/sysconfig/modules/".format( + mod_file)}, + {con.VIRT_RUN_CMD: "chmod 0755 /etc/sysconfig/modules/" + "{}".format(os.path.basename(mod_file))} + ]) + if root_pw: + pw_op = "password:{}".format(root_pw) + virt_cmds.append({con.VIRT_PW: pw_op}) + if ds_opts['sfc'] and dataplane == 'ovs': + virt_cmds.extend([ + {con.VIRT_RUN_CMD: "yum -y install " + "/root/ovs/rpm/rpmbuild/RPMS/x86_64/" + "{}".format(OVS_NSH_KMOD_RPM)}, + {con.VIRT_RUN_CMD: "yum upgrade -y " + "/root/ovs/rpm/rpmbuild/RPMS/x86_64/" + "{}".format(OVS_NSH_RPM)} + ]) + if dataplane == 'fdio': + # Patch neutron with using OVS external interface for router + # and add generic linux NS interface driver + virt_cmds.append( + {con.VIRT_RUN_CMD: "cd /usr/lib/python2.7/site-packages && patch " + "-p1 < neutron-patch-NSDriver.patch"}) + + if sdn == 'opendaylight': + if ds_opts['odl_version'] != con.DEFAULT_ODL_VERSION: + virt_cmds.extend([ + {con.VIRT_RUN_CMD: "yum -y remove opendaylight"}, + {con.VIRT_RUN_CMD: "yum -y install /root/{}/*".format( + con.DEFAULT_ODL_VERSION)}, + {con.VIRT_RUN_CMD: "rm -rf /etc/puppet/modules/opendaylight"}, + {con.VIRT_RUN_CMD: "cd /etc/puppet/modules && tar xzf " + "/root/puppet-opendaylight-" + "{}.tar.gz".format(ds_opts['odl_version'])} + ]) + elif sdn == 'opendaylight' and 'odl_vpp_netvirt' in ds_opts \ + and ds_opts['odl_vpp_netvirt']: + virt_cmds.extend([ + {con.VIRT_RUN_CMD: "yum -y remove opendaylight"}, + {con.VIRT_RUN_CMD: "yum -y install /root/{}/*".format( + ODL_NETVIRT_VPP_RPM)} + ]) + + if sdn == 'ovn': + virt_cmds.extend([ + {con.VIRT_RUN_CMD: "cd /root/ovs28 && yum update -y " + "*openvswitch*"}, + {con.VIRT_RUN_CMD: "cd /root/ovs28 && yum downgrade -y " + "*openvswitch*"} + ]) + + tmp_oc_image = os.path.join(tmp_dir, 'overcloud-full.qcow2') + shutil.copyfile(img, tmp_oc_image) + logging.debug("Temporary overcloud image stored as: {}".format( + tmp_oc_image)) + virt_utils.virt_customize(virt_cmds, tmp_oc_image) + logging.info("Overcloud image customization complete") + + +def make_ssh_key(): + """ + Creates public and private ssh keys with 1024 bit RSA encryption + :return: private, public key + """ + key = rsa.generate_private_key( + backend=crypto_default_backend(), + public_exponent=65537, + key_size=1024 + ) + + private_key = key.private_bytes( + crypto_serialization.Encoding.PEM, + crypto_serialization.PrivateFormat.PKCS8, + crypto_serialization.NoEncryption()) + public_key = key.public_key().public_bytes( + crypto_serialization.Encoding.OpenSSH, + crypto_serialization.PublicFormat.OpenSSH + ) + pub_key = re.sub('ssh-rsa\s*', '', public_key.decode('utf-8')) + return private_key.decode('utf-8'), pub_key + + +def prep_env(ds, ns, opnfv_env, net_env, tmp_dir): + """ + Creates modified opnfv/network environments for deployment + :param ds: deploy settings + :param ns: network settings + :param opnfv_env: file path for opnfv-environment file + :param net_env: file path for network-environment file + :param tmp_dir: Apex tmp dir + :return: + """ + + logging.info("Preparing opnfv-environment and network-environment files") + ds_opts = ds['deploy_options'] + tmp_opnfv_env = os.path.join(tmp_dir, os.path.basename(opnfv_env)) + shutil.copyfile(opnfv_env, tmp_opnfv_env) + tenant_nic_map = ns['networks']['tenant']['nic_mapping'] + tenant_ctrl_nic = tenant_nic_map['controller']['members'][0] + tenant_comp_nic = tenant_nic_map['compute']['members'][0] + + # SSH keys + private_key, public_key = make_ssh_key() + + # Make easier/faster variables to index in the file editor + if 'performance' in ds_opts: + perf = True + # vpp + if 'vpp' in ds_opts['performance']['Compute']: + perf_vpp_comp = ds_opts['performance']['Compute']['vpp'] + else: + perf_vpp_comp = None + if 'vpp' in ds_opts['performance']['Controller']: + perf_vpp_ctrl = ds_opts['performance']['Controller']['vpp'] + else: + perf_vpp_ctrl = None + + # ovs + if 'ovs' in ds_opts['performance']['Compute']: + perf_ovs_comp = ds_opts['performance']['Compute']['ovs'] + else: + perf_ovs_comp = None + + # kernel + if 'kernel' in ds_opts['performance']['Compute']: + perf_kern_comp = ds_opts['performance']['Compute']['kernel'] + else: + perf_kern_comp = None + else: + perf = False + + # Modify OPNFV environment + for line in fileinput.input(tmp_opnfv_env, inplace=True): + line = line.strip('\n') + if 'CloudDomain' in line: + print(" CloudDomain: {}".format(ns['domain_name'])) + elif ds_opts['sdn_controller'] == 'opendaylight' and \ + 'odl_vpp_routing_node' in ds_opts and ds_opts[ + 'odl_vpp_routing_node'] != 'dvr': + if 'opendaylight::vpp_routing_node' in line: + print(" opendaylight::vpp_routing_node: ${}.${}".format( + ds_opts['odl_vpp_routing_node'], ns['domain_name'])) + elif 'ControllerExtraConfig' in line: + print(" ControllerExtraConfig:\n " + "tripleo::profile::base::neutron::agents::honeycomb" + "::interface_role_mapping: ['{}:tenant-" + "interface]'".format(tenant_ctrl_nic)) + elif 'NovaComputeExtraConfig' in line: + print(" NovaComputeExtraConfig:\n " + "tripleo::profile::base::neutron::agents::honeycomb" + "::interface_role_mapping: ['{}:tenant-" + "interface]'".format(tenant_comp_nic)) + else: + print(line) + + elif not ds_opts['sdn_controller'] and ds_opts['dataplane'] == 'fdio': + if 'NeutronVPPAgentPhysnets' in line: + print(" NeutronVPPAgentPhysnets: 'datacentre:{}'".format( + tenant_ctrl_nic)) + else: + print(line) + elif perf: + line_printed = False + for role in 'NovaCompute', 'Controller': + if role == 'NovaCompute': + perf_opts = perf_vpp_comp + else: + perf_opts = perf_vpp_ctrl + cfg = "{}ExtraConfig".format(role) + if cfg in line and perf_opts: + if 'main-core' in perf_opts: + print(" {}:\n" + " fdio::vpp_cpu_main_core: '{}'" + "".format(cfg, perf_opts['main-core'])) + line_printed = True + break + elif 'corelist-workers' in perf_vpp_comp: + print(" {}:\n" + " fdio::vpp_cpu_corelist_workers: '{}'" + "".format(cfg, perf_opts['corelist-workers'])) + line_printed = True + break + + # kernel args + # (FIXME) use compute's kernel settings for all nodes for now. + if 'ComputeKernelArgs' in line and perf_kern_comp: + kernel_args = '' + for k, v in perf_kern_comp.items(): + kernel_args += "{}={}".format(k, v) + if kernel_args: + print("ComputeKernelArgs: '{}'".format(kernel_args)) + line_printed = True + elif ds_opts['dataplane'] == 'ovs_dpdk' and perf_ovs_comp: + for k, v in OVS_PERF_MAP.items(): + if k in line and v in perf_ovs_comp: + print(" {}: {}".format(k, perf_ovs_comp[v])) + line_printed = True + + if not line_printed: + print(line) + elif 'replace_private_key' in line: + print(" key: '{}'".format(private_key)) + elif 'replace_public_key' in line: + print(" key: '{}'".format(public_key)) + else: + print(line) + + logging.info("opnfv-environment file written to {}".format(tmp_opnfv_env)) + + # Modify Network environment + for line in fileinput.input(net_env, inplace=True): + line = line.strip('\n') + if ds_opts['dataplane'] == 'ovs_dpdk': + if 'ComputeExtraConfigPre' in line: + print(' OS::TripleO::ComputeExtraConfigPre: ' + './ovs-dpdk-preconfig.yaml') + else: + print(line) + elif perf and perf_kern_comp: + if 'resource_registry' in line: + print("resource_registry:\n" + " OS::TripleO::NodeUserData: first-boot.yaml") + elif 'NovaSchedulerDefaultFilters' in line: + print(" NovaSchedulerDefaultFilters: 'RamFilter," + "ComputeFilter,AvailabilityZoneFilter," + "ComputeCapabilitiesFilter,ImagePropertiesFilter," + "NUMATopologyFilter'") + else: + print(line) + else: + print(line) + + logging.info("network-environment file written to {}".format(net_env)) + + +def generate_ceph_key(): + key = os.urandom(16) + header = struct.pack(' MAX_NUM_MACS: + raise ValueError("The MAX num of MACS supported is %i." % MAX_NUM_MACS) + + base_nums = [0x00, + random.randint(0x00, 0xff), + random.randint(0x00, 0xff), + random.randint(0x00, 0xff), + random.randint(0x00, 0xff)] + base_mac = ':'.join(map(lambda x: "%02x" % x, base_nums)) + + start = random.randint(0x00, 0xff) + if (start + (count * 2)) > 0xff: + # leave room to generate macs in sequence + start = 0xff - count * 2 + for num in range(0, count * 2, 2): + mac = start + num + macs.append(base_mac + ":" + ("%02x" % mac)) + return macs + + +def create_vm_storage(domain, vol_path='/var/lib/libvirt/images'): + volume_name = domain + '.qcow2' + stgvol_xml = """ + + {} + 0 + 41 + + + {} + + 107 + 107 + 0744 + + + + """.format(volume_name, os.path.join(vol_path, volume_name)) + + conn = libvirt.open('qemu:///system') + pool = conn.storagePoolLookupByName('default') + if pool is None: + raise Exception("Default libvirt storage pool missing") + # TODO(trozet) create default storage pool + + if pool.isActive() == 0: + pool.create() + try: + vol = pool.storageVolLookupByName(volume_name) + vol.wipe(0) + vol.delete(0) + except libvirt.libvirtError as e: + if e.get_error_code() != libvirt.VIR_ERR_NO_STORAGE_VOL: + raise + new_vol = pool.createXML(stgvol_xml) + if new_vol is None: + raise Exception("Unable to create new volume") + logging.debug("Created new storage volume: {}".format(volume_name)) + + +def create_vm(name, image, diskbus='sata', baremetal_interfaces=['admin'], + arch='x86_64', engine='kvm', memory=8192, bootdev='network', + cpus=4, nic_driver='virtio', macs=[], direct_boot=None, + kernel_args=None, default_network=False, + template_dir='/usr/share/opnfv-apex'): + # TODO(trozet): fix name here to be image since it is full path of qcow2 + create_vm_storage(name) + with open(os.path.join(template_dir, 'domain.xml'), 'r') as f: + source_template = f.read() + imagefile = os.path.realpath(image) + memory = int(memory) * 1024 + params = { + 'name': name, + 'imagefile': imagefile, + 'engine': engine, + 'arch': arch, + 'memory': str(memory), + 'cpus': str(cpus), + 'bootdev': bootdev, + 'network': '', + 'enable_serial_console': '', + 'direct_boot': '', + 'kernel_args': '', + 'user_interface': '', + } + + # Configure the bus type for the target disk device + params['diskbus'] = diskbus + nicparams = { + 'nicdriver': nic_driver, + } + if default_network: + params['network'] = """ + + + + + """ % nicparams + else: + params['network'] = '' + while len(macs) < len(baremetal_interfaces): + macs += generate_baremetal_macs(1) + + params['bm_network'] = "" + for bm_interface, mac in zip(baremetal_interfaces, macs): + bm_interface_params = { + 'bminterface': bm_interface, + 'bmmacaddress': mac, + 'nicdriver': nic_driver, + } + params['bm_network'] += """ + + + + + + """ % bm_interface_params + + params['enable_serial_console'] = """ + + + + + + + """ + if direct_boot: + params['direct_boot'] = """ + /var/lib/libvirt/images/%(direct_boot)s.vmlinuz + /var/lib/libvirt/images/%(direct_boot)s.initrd + """ % {'direct_boot': direct_boot} + if kernel_args: + params['kernel_args'] = """ + %s + """ % ' '.join(kernel_args) + + if arch == 'aarch64': + + params['direct_boot'] += """ + /usr/share/AAVMF/AAVMF_CODE.fd + /var/lib/libvirt/qemu/nvram/centos7.0_VARS.fd + """ + params['user_interface'] = """ + +
+ + + + + + + + + +
+ + """ + else: + params['user_interface'] = """ + + + + """ + + libvirt_template = source_template % params + logging.debug("libvirt template is {}".format(libvirt_template)) + conn = libvirt.open('qemu:///system') + vm = conn.defineXML(libvirt_template) + logging.info("Created machine %s with UUID %s" % (name, vm.UUIDString())) + return vm diff --git a/apex/virtual/virtual_utils.py b/apex/virtual/virtual_utils.py new file mode 100644 index 00000000..5ebb0582 --- /dev/null +++ b/apex/virtual/virtual_utils.py @@ -0,0 +1,140 @@ +############################################################################## +# Copyright (c) 2017 Tim Rozet (trozet@redhat.com) and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import copy +import iptc +import logging +import os +import pprint +import subprocess + +from apex.common import utils +from apex.virtual import configure_vm as vm_lib +from virtualbmc import manager as vbmc_lib + +DEFAULT_RAM = 8192 +DEFAULT_PM_PORT = 6230 +DEFAULT_USER = 'admin' +DEFAULT_PASS = 'password' +DEFAULT_VIRT_IP = '192.168.122.1' + + +def generate_inventory(target_file, ha_enabled=False, num_computes=1, + controller_ram=DEFAULT_RAM, arch='x86_64', + compute_ram=DEFAULT_RAM, vcpus=4): + """ + Generates inventory file for virtual deployments + :param target_file: + :param ha_enabled: + :param num_computes: + :param controller_ram: + :param arch: + :param compute_ram: + :param vcpus: + :return: + """ + + node = {'mac_address': '', + 'ipmi_ip': DEFAULT_VIRT_IP, + 'ipmi_user': DEFAULT_USER, + 'ipmi_pass': DEFAULT_PASS, + 'pm_type': 'pxe_ipmitool', + 'pm_port': '', + 'cpu': vcpus, + 'memory': DEFAULT_RAM, + 'disk': 41, + 'arch': arch, + 'capabilities': '' + } + + inv_output = {'nodes': {}} + if ha_enabled: + num_ctrlrs = 3 + else: + num_ctrlrs = 1 + + for idx in range(num_ctrlrs + num_computes): + tmp_node = copy.deepcopy(node) + tmp_node['mac_address'] = vm_lib.generate_baremetal_macs(1)[0] + tmp_node['pm_port'] = DEFAULT_PM_PORT + idx + if idx < num_ctrlrs: + tmp_node['capabilities'] = 'profile:control' + tmp_node['memory'] = controller_ram + else: + tmp_node['capabilities'] = 'profile:compute' + tmp_node['memory'] = compute_ram + inv_output['nodes']['node{}'.format(idx)] = copy.deepcopy(tmp_node) + + utils.dump_yaml(inv_output, target_file) + + logging.info('Virtual environment file created: {}'.format(target_file)) + + +def host_setup(node): + """ + Handles configuring vmbc and firewalld/iptables + :param node: dictionary of domain names and ports for ipmi + :return: + """ + vbmc_manager = vbmc_lib.VirtualBMCManager() + for name, port in node.items(): + vbmc_manager.add(username=DEFAULT_USER, password=DEFAULT_PASS, + port=port, address=DEFAULT_VIRT_IP, domain_name=name, + libvirt_uri='qemu:///system', + libvirt_sasl_password=False, + libvirt_sasl_username=False) + + # TODO(trozet): add support for firewalld + subprocess.call(['systemctl', 'stop', 'firewalld']) + + # iptables rule + rule = iptc.Rule() + rule.protocol = 'udp' + match = rule.create_match('udp') + match.dport = str(port) + rule.add_match(match) + rule.target = iptc.Target(rule, "ACCEPT") + chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), "INPUT") + chain.insert_rule(rule) + try: + subprocess.check_call(['vbmc', 'start', name]) + logging.debug("Started vbmc for domain {}".format(name)) + except subprocess.CalledProcessError: + logging.error("Failed to start vbmc for {}".format(name)) + raise + logging.debug('vmbcs setup: {}'.format(vbmc_manager.list())) + + +def virt_customize(ops, target): + """ + Helper function to virt customize disks + :param ops: list of of operations and arguments + :param target: target disk to modify + :return: None + """ + logging.info("Virt customizing target disk: {}".format(target)) + virt_cmd = ['virt-customize'] + for op in ops: + for op_cmd, op_arg in op.items(): + virt_cmd.append(op_cmd) + virt_cmd.append(op_arg) + virt_cmd.append('-a') + virt_cmd.append(target) + if not os.path.isfile(target): + raise FileNotFoundError + my_env = os.environ.copy() + my_env['LIBGUESTFS_BACKEND'] = 'direct' + logging.debug("Virt-customizing with: \n{}".format(virt_cmd)) + try: + logging.debug(subprocess.check_output(virt_cmd, env=my_env, + stderr=subprocess.STDOUT)) + except subprocess.CalledProcessError as e: + logging.error("Error executing virt-customize: {}".format( + pprint.pformat(e.output))) + raise diff --git a/build/Makefile b/build/Makefile index 43eae37a..74e72657 100644 --- a/build/Makefile +++ b/build/Makefile @@ -79,7 +79,7 @@ $(RPMREL): rpmbuild --clean -ba rpm_specs/opnfv-apex-release.spec $(RPM_DIR_ARGS) -D "_release $(shell echo $(RELEASE) | tr -d '_-')" $(BUILD_DIR)/opnfv-apex-common.tar.gz: - pushd ../ && git archive --format=tar.gz --prefix=opnfv-apex-common-$(RPMVERS)/ HEAD > $(BUILD_DIR)/opnfv-apex-common.tar.gz + pushd ../ && git archive --format=tar.gz --prefix=opnfv-apex-$(RPMVERS)/ HEAD > $(BUILD_DIR)/opnfv-apex-common.tar.gz .PHONY: common-rpm-check common-rpm-check: $(BUILD_DIR)/opnfv-apex-common.tar.gz @@ -99,14 +99,7 @@ $(RPMCOM): .PHONY: python-tests python-tests: - # clean previous coverage data - rm -rf ../tests/.coverage - rm -rf ../tests/htmlcov - # run nose tests - cd ../tests && PYTHONPATH=../lib/python/ nosetests-3.4 . --with-coverage --cover-package apex --cover-package apex_python_utils --cover-html --cover-min-percentage 90 - - # generate reports - cd ../tests && coverage3 report --include '*lib/python/*' -m + tox -e py35 ####################### # PYTHON PEP8 CHECK # @@ -114,8 +107,7 @@ python-tests: .PHONY: python-pep8-check python-pep8-check: - pep8 ../lib/python - pep8 ../tests + tox -e pep8 ############# # YAMLLINT # diff --git a/lib/installer/domain.xml b/build/domain.xml similarity index 100% rename from lib/installer/domain.xml rename to build/domain.xml diff --git a/build/rpm_specs/opnfv-apex-common.spec b/build/rpm_specs/opnfv-apex-common.spec index ccb100f3..42bc42f9 100644 --- a/build/rpm_specs/opnfv-apex-common.spec +++ b/build/rpm_specs/opnfv-apex-common.spec @@ -1,7 +1,9 @@ -Name: opnfv-apex-common +%global srcname opnfv-apex + +Name: python3-%{srcname} Version: 5.0 Release: %{_release} -Summary: Scripts for OPNFV deployment using RDO Manager +Summary: Scripts for OPNFV deployment using Apex Group: System Environment License: Apache 2.0 @@ -13,23 +15,25 @@ BuildRequires: python-docutils python34-devel Requires: opnfv-apex-sdn opnfv-apex-undercloud openvswitch qemu-kvm bridge-utils libguestfs-tools libvirt-python Requires: initscripts net-tools iputils iproute iptables python34 python34-yaml python34-jinja2 python3-ipmi python2-virtualbmc Requires: ipxe-roms-qemu >= 20160127-1 +Requires: libvirt-devel %description -Scripts for OPNFV deployment using RDO Manager +Scripts for OPNFV deployment using Apex https://wiki.opnfv.org/apex %prep -%setup -q +%autosetup -n %{srcname}-%{version} %build rst2html docs/release/installation/index.rst docs/release/installation/installation-instructions.html rst2html docs/release/release-notes/release-notes.rst docs/release/release-notes/release-notes.html +%py3_build %global __python %{__python3} %install mkdir -p %{buildroot}%{_bindir}/ -install ci/deploy.sh %{buildroot}%{_bindir}/opnfv-deploy +%py3_install install ci/clean.sh %{buildroot}%{_bindir}/opnfv-clean install ci/util.sh %{buildroot}%{_bindir}/opnfv-util @@ -37,67 +41,10 @@ mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d/ install build/bash_completion_apex %{buildroot}%{_sysconfdir}/bash_completion.d/apex mkdir -p %{buildroot}%{_sysconfdir}/opnfv-apex/ -install config/deploy/os-nosdn-nofeature-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-nofeature-noha.yaml -install config/deploy/os-nosdn-bar-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-bar-noha.yaml -install config/deploy/os-nosdn-bar-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-bar-ha.yaml -install config/deploy/os-nosdn-fdio-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-fdio-noha.yaml -install config/deploy/os-nosdn-fdio-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-fdio-ha.yaml -install config/deploy/os-nosdn-ovs_dpdk-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-ovs_dpdk-noha.yaml -install config/deploy/os-nosdn-nofeature-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-nofeature-ha.yaml -install config/deploy/os-nosdn-performance-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-performance-ha.yaml -install config/deploy/os-nosdn-ovs_dpdk-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-ovs_dpdk-ha.yaml -install config/deploy/os-nosdn-kvm-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-kvm-ha.yaml -install config/deploy/os-nosdn-kvm-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-kvm-noha.yaml -install config/deploy/os-nosdn-kvm_ovs_dpdk-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-kvm_ovs_dpdk-ha.yaml -install config/deploy/os-nosdn-kvm_ovs_dpdk-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-nosdn-kvm_ovs_dpdk-noha.yaml -install config/deploy/os-odl-bgpvpn-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-bgpvpn-ha.yaml -install config/deploy/os-odl-bgpvpn-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-bgpvpn-noha.yaml -install config/deploy/os-odl-sfc-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-sfc-ha.yaml -install config/deploy/os-odl-sfc-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-sfc-noha.yaml -install config/deploy/os-odl-fdio-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-fdio-noha.yaml -install config/deploy/os-odl_netvirt-fdio-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl_netvirt-fdio-noha.yaml -install config/deploy/os-odl-fdio-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-fdio-ha.yaml -install config/deploy/os-odl-fdio-dvr-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-fdio-dvr-ha.yaml -install config/deploy/os-odl-fdio-dvr-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-fdio-dvr-noha.yaml -install config/deploy/os-odl-nofeature-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-nofeature-ha.yaml -install config/deploy/os-odl-nofeature-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-nofeature-noha.yaml -install config/deploy/os-odl-ovs_dpdk-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-ovs_dpdk-ha.yaml -install config/deploy/os-odl-ovs_dpdk-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-ovs_dpdk-noha.yaml -install config/deploy/os-odl-gluon-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-gluon-noha.yaml -install config/deploy/os-ovn-nofeature-noha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-ovn-nofeature-noha.yaml -install config/deploy/os-onos-nofeature-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-onos-nofeature-ha.yaml -install config/deploy/os-onos-sfc-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-onos-sfc-ha.yaml -install config/deploy/os-ocl-nofeature-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-ocl-nofeature-ha.yaml -install config/network/network_settings.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/network_settings.yaml -install config/network/network_settings_v6.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/network_settings_v6.yaml -install config/network/network_settings_vpp.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/network_settings_vpp.yaml - - -mkdir -p %{buildroot}%{_var}/opt/opnfv/lib/python/apex -install lib/common-functions.sh %{buildroot}%{_var}/opt/opnfv/lib/ -install lib/configure-deps-functions.sh %{buildroot}%{_var}/opt/opnfv/lib/ -install lib/parse-functions.sh %{buildroot}%{_var}/opt/opnfv/lib/ -install lib/virtual-setup-functions.sh %{buildroot}%{_var}/opt/opnfv/lib/ -install lib/undercloud-functions.sh %{buildroot}%{_var}/opt/opnfv/lib/ -install lib/overcloud-deploy-functions.sh %{buildroot}%{_var}/opt/opnfv/lib/ -install lib/post-install-functions.sh %{buildroot}%{_var}/opt/opnfv/lib/ -install lib/utility-functions.sh %{buildroot}%{_var}/opt/opnfv/lib/ -install lib/configure-vm %{buildroot}%{_var}/opt/opnfv/lib/ -install lib/python/apex_python_utils.py %{buildroot}%{_var}/opt/opnfv/lib/python/ -mkdir -p %{buildroot}%{python3_sitelib}/apex/ -install lib/python/apex/__init__.py %{buildroot}%{python3_sitelib}/apex/ -install lib/python/apex/deploy_settings.py %{buildroot}%{python3_sitelib}/apex/ -install lib/python/apex/ip_utils.py %{buildroot}%{python3_sitelib}/apex/ -install lib/python/apex/inventory.py %{buildroot}%{python3_sitelib}/apex/ -install lib/python/apex/network_environment.py %{buildroot}%{python3_sitelib}/apex/ -install lib/python/apex/network_settings.py %{buildroot}%{python3_sitelib}/apex/ -install lib/python/apex/clean.py %{buildroot}%{python3_sitelib}/apex/ -mkdir -p %{buildroot}%{python3_sitelib}/apex/common -install lib/python/apex/common/__init__.py %{buildroot}%{python3_sitelib}/apex/common/ -install lib/python/apex/common/constants.py %{buildroot}%{python3_sitelib}/apex/common/ -install lib/python/apex/common/utils.py %{buildroot}%{python3_sitelib}/apex/common/ -mkdir -p %{buildroot}%{_var}/opt/opnfv/lib/installer/onos/ -install lib/installer/domain.xml %{buildroot}%{_var}/opt/opnfv/lib/installer/ +cp -f %{buildroot}%{_datadir}/opnfv-apex/config/deploy/* %{buildroot}%{_sysconfdir}/opnfv-apex/ +cp -f %{buildroot}%{_datadir}/opnfv-apex/config/network/* %{buildroot}%{_sysconfdir}/opnfv-apex/ +rm -f %{buildroot}%{_sysconfdir}/opnfv-apex/os-odl-csit-noha.yaml +rm -f %{buildroot}%{_sysconfdir}/opnfv-apex/deploy_settings.yaml mkdir -p %{buildroot}%{_docdir}/opnfv/ install LICENSE.rst %{buildroot}%{_docdir}/opnfv/ @@ -111,22 +58,13 @@ install config/network/network_settings_vpp.yaml %{buildroot}%{_docdir}/opnfv/ne install config/inventory/pod_example_settings.yaml %{buildroot}%{_docdir}/opnfv/inventory.yaml.example %files -%defattr(644, root, root, -) +%{python3_sitelib}/apex/ +%{python3_sitelib}/apex-*.egg-info +%defattr(644, root, root, 644) %attr(755,root,root) %{_bindir}/opnfv-deploy %attr(755,root,root) %{_bindir}/opnfv-clean %attr(755,root,root) %{_bindir}/opnfv-util -%{_var}/opt/opnfv/lib/common-functions.sh -%{_var}/opt/opnfv/lib/configure-deps-functions.sh -%{_var}/opt/opnfv/lib/parse-functions.sh -%{_var}/opt/opnfv/lib/virtual-setup-functions.sh -%{_var}/opt/opnfv/lib/undercloud-functions.sh -%{_var}/opt/opnfv/lib/overcloud-deploy-functions.sh -%{_var}/opt/opnfv/lib/post-install-functions.sh -%{_var}/opt/opnfv/lib/utility-functions.sh -%attr(755,root,root) %{_var}/opt/opnfv/lib/configure-vm -%{_var}/opt/opnfv/lib/python/ -%{python3_sitelib}/apex/ -%{_var}/opt/opnfv/lib/installer/domain.xml +%{_datadir}/opnfv-apex/ %{_sysconfdir}/bash_completion.d/apex %{_sysconfdir}/opnfv-apex/os-nosdn-nofeature-noha.yaml %{_sysconfdir}/opnfv-apex/os-nosdn-bar-noha.yaml @@ -160,6 +98,7 @@ install config/inventory/pod_example_settings.yaml %{buildroot}%{_docdir}/opnfv/ %{_sysconfdir}/opnfv-apex/os-onos-sfc-ha.yaml %{_sysconfdir}/opnfv-apex/os-ocl-nofeature-ha.yaml %{_sysconfdir}/opnfv-apex/network_settings.yaml +%{_sysconfdir}/opnfv-apex/network_settings_vlans.yaml %{_sysconfdir}/opnfv-apex/network_settings_v6.yaml %{_sysconfdir}/opnfv-apex/network_settings_vpp.yaml %doc %{_docdir}/opnfv/LICENSE.rst @@ -173,6 +112,8 @@ install config/inventory/pod_example_settings.yaml %{buildroot}%{_docdir}/opnfv/ %doc %{_docdir}/opnfv/inventory.yaml.example %changelog +* Mon Aug 14 2017 Tim Rozet - 5.0-4 +- Updated for python refactoring * Mon May 08 2017 Dan Radez - 5.0-3 - adding configure-vm * Tue Apr 11 2017 Dan Radez - 5.0-2 diff --git a/build/variables.sh b/build/variables.sh index d37be0d0..6f8a6696 100644 --- a/build/variables.sh +++ b/build/variables.sh @@ -14,7 +14,7 @@ QUAGGA_RPMS_DIR=${BUILD_DIR}/quagga_build_dir CACHE_DIR="$(dirname ${BUILD_ROOT})/.cache" CACHE_HISTORY=".cache_history" PATCHES_DIR="${BUILD_ROOT}/patches" -BUILD_UTILS="$(dirname ${BUILD_ROOT})/lib/python/build_utils.py" +BUILD_UTILS="$(dirname ${BUILD_ROOT})/apex/build/build_utils.py" rdo_images_uri=${RDO_IMAGES_URI:-https://images.rdoproject.org/ocata/delorean/current-tripleo/stable/} @@ -52,4 +52,4 @@ fdio_pkgs=( 'http://artifacts.opnfv.org/apex/danube/fdio_common_rpms/vpp-plugins-17.04.1-3~ge3b7ad7~b72.x86_64.rpm' ) -honeycomb_pkg='http://artifacts.opnfv.org/apex/danube/fdio_common_rpms/honeycomb-1.17.04.1-2073.noarch.rpm' \ No newline at end of file +honeycomb_pkg='http://artifacts.opnfv.org/apex/danube/fdio_common_rpms/honeycomb-1.17.04.1-2073.noarch.rpm' diff --git a/ci/build.sh b/ci/build.sh index 5cd2c28d..113f35d6 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -13,4 +13,4 @@ set -e rpm -q ansible || sudo yum -y install ansible ansible-playbook --become -i "localhost," -c local $DIR/../lib/ansible/playbooks/build_dependencies.yml -vvv make -C $DIR/../build clean -python3 $DIR/build.py $@ +python3 $DIR/../apex/build.py $@ diff --git a/ci/clean.sh b/ci/clean.sh index fba1f126..e35b95b1 100755 --- a/ci/clean.sh +++ b/ci/clean.sh @@ -12,23 +12,11 @@ #author: Dan Radez (dradez@redhat.com) #author: Tim Rozet (trozet@redhat.com) -# Use default if no param passed -BASE=${BASE:-'/var/opt/opnfv'} -IMAGES=${IMAGES:-"$BASE/images"} -LIB=${LIB:-"$BASE/lib"} reset=$(tput sgr0 || echo "") blue=$(tput setaf 4 || echo "") red=$(tput setaf 1 || echo "") green=$(tput setaf 2 || echo "") -##LIBRARIES -for lib in common-functions parse-functions; do - if ! source $LIB/${lib}.sh; then - echo "Failed to source $LIB/${lib}.sh" - exit 1 - fi -done - vm_index=4 ovs_bridges="br-admin br-tenant br-external br-storage" ovs_bridges+=" br-private br-public" # Legacy names, remove in E river @@ -37,6 +25,102 @@ ovs_bridges+=" br-private br-public" # Legacy names, remove in E river OPNFV_NETWORK_TYPES+=" admin tenant external storage api" OPNFV_NETWORK_TYPES+=" admin_network private_network public_network storage_network api_network" # Legecy names, remove in E river +##detach interface from OVS and set the network config correctly +##params: bridge to detach from +##assumes only 1 real interface attached to OVS +function detach_interface_from_ovs { + local bridge + local port_output ports_no_orig + local net_path + local if_ip if_mask if_gw if_prefix + local if_metric if_dns1 if_dns2 + + net_path=/etc/sysconfig/network-scripts/ + if [[ -z "$1" ]]; then + return 1 + else + bridge=$1 + fi + + # if no interfaces attached then return + if ! ovs-vsctl list-ports ${bridge} | grep -Ev "vnet[0-9]*"; then + return 0 + fi + + # look for .orig ifcfg files to use + port_output=$(ovs-vsctl list-ports ${bridge} | grep -Ev "vnet[0-9]*") + while read -r line; do + if [ -z "$line" ]; then + continue + elif [ -e ${net_path}/ifcfg-${line}.orig ]; then + mv -f ${net_path}/ifcfg-${line}.orig ${net_path}/ifcfg-${line} + elif [ -e ${net_path}/ifcfg-${bridge} ]; then + if_ip=$(sed -n 's/^IPADDR=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge}) + if_mask=$(sed -n 's/^NETMASK=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge}) + if_gw=$(sed -n 's/^GATEWAY=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge}) + if_metric=$(sed -n 's/^METRIC=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge}) + if_dns1=$(sed -n 's/^DNS1=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge}) + if_dns2=$(sed -n 's/^DNS2=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge}) + + if [ -z "$if_mask" ]; then + if_prefix=$(sed -n 's/^PREFIX=[^0-9]*\([0-9][0-9]*\)[^0-9]*$/\1/p' ${net_path}/ifcfg-${bridge}) + if_mask=$(prefix2mask ${if_prefix}) + fi + + if [[ -z "$if_ip" || -z "$if_mask" ]]; then + echo "ERROR: IPADDR or PREFIX/NETMASK missing for ${bridge} and no .orig file for interface ${line}" + return 1 + fi + + # create if cfg + echo "DEVICE=${line} +IPADDR=${if_ip} +NETMASK=${if_mask} +BOOTPROTO=static +ONBOOT=yes +TYPE=Ethernet +NM_CONTROLLED=no +PEERDNS=no" > ${net_path}/ifcfg-${line} + + if [ -n "$if_gw" ]; then + echo "GATEWAY=${if_gw}" >> ${net_path}/ifcfg-${line} + fi + + if [ -n "$if_metric" ]; then + echo "METRIC=${if_metric}" >> ${net_path}/ifcfg-${line} + fi + + if [[ -n "$if_dns1" || -n "$if_dns2" ]]; then + sed -i '/PEERDNS/c\PEERDNS=yes' ${net_path}/ifcfg-${line} + + if [ -n "$if_dns1" ]; then + echo "DNS1=${if_dns1}" >> ${net_path}/ifcfg-${line} + fi + + if [ -n "$if_dns2" ]; then + echo "DNS2=${if_dns2}" >> ${net_path}/ifcfg-${line} + fi + fi + break + else + echo "ERROR: Real interface ${line} attached to bridge, but no interface or ${bridge} ifcfg file exists" + return 1 + fi + + done <<< "$port_output" + + # modify the bridge ifcfg file + # to remove IP params + sudo sed -i 's/IPADDR=.*//' ${net_path}/ifcfg-${bridge} + sudo sed -i 's/NETMASK=.*//' ${net_path}/ifcfg-${bridge} + sudo sed -i 's/GATEWAY=.*//' ${net_path}/ifcfg-${bridge} + sudo sed -i 's/DNS1=.*//' ${net_path}/ifcfg-${bridge} + sudo sed -i 's/DNS2=.*//' ${net_path}/ifcfg-${bridge} + sudo sed -i 's/METRIC=.*//' ${net_path}/ifcfg-${bridge} + sudo sed -i 's/PEERDNS=.*//' ${net_path}/ifcfg-${bridge} + + sudo systemctl restart network +} display_usage() { echo -e "Usage:\n$0 [arguments] \n" @@ -47,7 +131,7 @@ display_usage() { ##params: $@ the entire command line is passed ##usage: parse_cmd_line() "$@" parse_cmdline() { - echo -e "\n\n${blue}This script is used to deploy the Apex Installer and Provision OPNFV Target System${reset}\n\n" + echo -e "\n\n${blue}This script is used to clean an Apex environment${reset}\n\n" echo "Use -h to display help" sleep 2 @@ -79,7 +163,13 @@ parse_cmdline "$@" if [ -n "$INVENTORY_FILE" ]; then echo -e "${blue}INFO: Parsing inventory file...${reset}" - if ! python3 -B $LIB/python/apex_python_utils.py clean -f ${INVENTORY_FILE}; then + # hack for now (until we switch fully over to clean.py) to tell if + # we should install apex from python or if rpm is being used + if ! rpm -q opnfv-apex-common > /dev/null; then + pushd ../ && python3 setup.py install > /dev/null + popd + fi + if ! python3 -m apex.clean -f ${INVENTORY_FILE}; then echo -e "${red}WARN: Unable to shutdown all nodes! Please check /var/log/apex.log${reset}" else echo -e "${blue}INFO: Node shutdown complete...${reset}" diff --git a/ci/deploy.sh b/ci/deploy.sh index f1a807f7..0ba0c74b 100755 --- a/ci/deploy.sh +++ b/ci/deploy.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash ############################################################################## # Copyright (c) 2015 Tim Rozet (Red Hat), Dan Radez (Red Hat) and others. # @@ -12,245 +12,11 @@ # author: Dan Radez (dradez@redhat.com) # author: Tim Rozet (trozet@redhat.com) # -# Based on RDO Manager http://www.rdoproject.org set -e - -##VARIABLES -reset=$(tput sgr0 || echo "") -blue=$(tput setaf 4 || echo "") -red=$(tput setaf 1 || echo "") -green=$(tput setaf 2 || echo "") - -interactive="FALSE" -ping_site="8.8.8.8" -dnslookup_site="www.google.com" -post_config="TRUE" -debug="FALSE" - -ovs_rpm_name=openvswitch-2.6.1-1.el7.centos.x86_64.rpm -ovs_kmod_rpm_name=openvswitch-kmod-2.6.1-1.el7.centos.x86_64.rpm - -declare -i CNT -declare UNDERCLOUD -declare -A deploy_options_array -declare -a performance_options -declare -A NET_MAP - -APEX_TMP_DIR=$(python3 -c "import tempfile; print(tempfile.mkdtemp())") -SSH_OPTIONS=(-o StrictHostKeyChecking=no -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null -o LogLevel=error) -DEPLOY_OPTIONS="" -BASE=${BASE:-'/var/opt/opnfv'} -IMAGES=${IMAGES:-"$BASE/images"} -LIB=${LIB:-"$BASE/lib"} -OPNFV_NETWORK_TYPES="admin tenant external storage api" -ENV_FILE="opnfv-environment.yaml" - -VM_CPUS=4 -VM_RAM=8 -VM_COMPUTES=1 - -# Netmap used to map networks to OVS bridge names -NET_MAP['admin']="br-admin" -NET_MAP['tenant']="br-tenant" -NET_MAP['external']="br-external" -NET_MAP['storage']="br-storage" -NET_MAP['api']="br-api" -ext_net_type="interface" -ip_address_family=4 - -# Libraries -lib_files=( -$LIB/common-functions.sh -$LIB/configure-deps-functions.sh -$LIB/parse-functions.sh -$LIB/virtual-setup-functions.sh -$LIB/undercloud-functions.sh -$LIB/overcloud-deploy-functions.sh -$LIB/post-install-functions.sh -$LIB/utility-functions.sh -) -for lib_file in ${lib_files[@]}; do - if ! source $lib_file; then - echo -e "${red}ERROR: Failed to source $lib_file${reset}" - exit 1 - fi -done - -display_usage() { - echo -e "Usage:\n$0 [arguments] \n" - echo -e " --deploy-settings | -d : Full path to deploy settings yaml file. Optional. Defaults to null" - echo -e " --inventory | -i : Full path to inventory yaml file. Required only for baremetal" - echo -e " --net-settings | -n : Full path to network settings file. Optional." - echo -e " --ping-site | -p : site to use to verify IP connectivity. Optional. Defaults to 8.8.8.8" - echo -e " --dnslookup-site : site to use to verify DNS resolution. Optional. Defaults to www.google.com" - echo -e " --virtual | -v : Virtualize overcloud nodes instead of using baremetal." - echo -e " --no-post-config : disable Post Install configuration." - echo -e " --debug : enable debug output." - echo -e " --interactive : enable interactive deployment mode which requires user to confirm steps of deployment." - echo -e " --virtual-cpus : Number of CPUs to use per Overcloud VM in a virtual deployment (defaults to 4)." - echo -e " --virtual-computes : Number of Virtual Compute nodes to create and use during deployment (defaults to 1 for noha and 2 for ha)." - echo -e " --virtual-default-ram : Amount of default RAM to use per Overcloud VM in GB (defaults to 8)." - echo -e " --virtual-compute-ram : Amount of RAM to use per Overcloud Compute VM in GB (defaults to 8). Overrides --virtual-default-ram arg for computes" -} - -##translates the command line parameters into variables -##params: $@ the entire command line is passed -##usage: parse_cmd_line() "$@" -parse_cmdline() { - echo -e "\n\n${blue}This script is used to deploy the Apex Installer and Provision OPNFV Target System${reset}\n\n" - echo "Use -h to display help" - - while [ "${1:0:1}" = "-" ] - do - case "$1" in - -h|--help) - display_usage - exit 0 - ;; - -d|--deploy-settings) - DEPLOY_SETTINGS_FILE=$2 - echo "Deployment Configuration file: $2" - shift 2 - ;; - -i|--inventory) - INVENTORY_FILE=$2 - shift 2 - ;; - -n|--net-settings) - NETSETS=$2 - echo "Network Settings Configuration file: $2" - shift 2 - ;; - -e|--environment-file) - ENV_FILE=$2 - echo "Base OOO Environment file: $2" - shift 2 - ;; - -p|--ping-site) - ping_site=$2 - echo "Using $2 as the ping site" - shift 2 - ;; - --dnslookup-site) - dnslookup_site=$2 - echo "Using $2 as the dnslookup site" - shift 2 - ;; - -v|--virtual) - virtual="TRUE" - echo "Executing a Virtual Deployment" - shift 1 - ;; - --no-post-config ) - post_config="FALSE" - echo "Post install configuration disabled" - shift 1 - ;; - --debug ) - debug="TRUE" - echo "Enable debug output" - shift 1 - ;; - --interactive ) - interactive="TRUE" - echo "Interactive mode enabled" - shift 1 - ;; - --virtual-cpus ) - VM_CPUS=$2 - echo "Number of CPUs per VM set to $VM_CPUS" - shift 2 - ;; - --virtual-default-ram ) - VM_RAM=$2 - echo "Amount of Default RAM per VM set to $VM_RAM" - shift 2 - ;; - --virtual-computes ) - VM_COMPUTES=$2 - echo "Virtual Compute nodes set to $VM_COMPUTES" - shift 2 - ;; - --virtual-compute-ram ) - VM_COMPUTE_RAM=$2 - echo "Virtual Compute RAM set to $VM_COMPUTE_RAM" - shift 2 - ;; - *) - display_usage - exit 1 - ;; - esac - done - sleep 2 - - if [[ -z "$NETSETS" ]]; then - echo -e "${red}ERROR: You must provide a network_settings file with -n.${reset}" - exit 1 - fi - - # inventory file usage validation - if [[ -n "$virtual" ]]; then - if [[ -n "$INVENTORY_FILE" ]]; then - echo -e "${red}ERROR: You should not specify an inventory file with virtual deployments${reset}" - exit 1 - else - INVENTORY_FILE="$APEX_TMP_DIR/inventory-virt.yaml" - fi - elif [[ -z "$INVENTORY_FILE" ]]; then - echo -e "${red}ERROR: You must specify an inventory file for baremetal deployments! Exiting...${reset}" - exit 1 - elif [[ ! -f "$INVENTORY_FILE" ]]; then - echo -e "{$red}ERROR: Inventory File: ${INVENTORY_FILE} does not exist! Exiting...${reset}" - exit 1 - fi - - if [[ -z "$DEPLOY_SETTINGS_FILE" || ! -f "$DEPLOY_SETTINGS_FILE" ]]; then - echo -e "${red}ERROR: Deploy Settings: ${DEPLOY_SETTINGS_FILE} does not exist! Exiting...${reset}" - exit 1 - fi - - if [[ ! -z "$NETSETS" && ! -f "$NETSETS" ]]; then - echo -e "${red}ERROR: Network Settings: ${NETSETS} does not exist! Exiting...${reset}" - exit 1 - fi - -} - -main() { - parse_cmdline "$@" - if [ -n "$DEPLOY_SETTINGS_FILE" ]; then - echo -e "${blue}INFO: Parsing deploy settings file...${reset}" - parse_deploy_settings - fi - echo -e "${blue}INFO: Parsing network settings file...${reset}" - parse_network_settings - if ! configure_deps; then - echo -e "${red}Dependency Validation Failed, Exiting.${reset}" - exit 1 - fi - #Correct the time on the server prior to launching any VMs - if ntpdate $ntp_server; then - hwclock --systohc - else - echo "${blue}WARNING: ntpdate failed to update the time on the server. ${reset}" - fi - setup_undercloud_vm - if [ "$virtual" == "TRUE" ]; then - setup_virtual_baremetal $VM_CPUS $VM_RAM - fi - parse_inventory_file - configure_undercloud - overcloud_deploy - if [ "$post_config" == "TRUE" ]; then - if ! configure_post_install; then - echo -e "${red}ERROR:Post Install Configuration Failed, Exiting.${reset}" - exit 1 - else - echo -e "${blue}INFO: Post Install Configuration Complete${reset}" - fi - fi -} - -main "$@" +yum -y install python34 python34-devel libvirt-devel python34-pip python-tox ansible +mkdir -p /home/jenkins-ci/tmp +mv -f .build /home/jenkins-ci/tmp/ +pip3 install --upgrade --force-reinstall . +mv -f /home/jenkins-ci/tmp/.build . +opnfv-deploy $@ diff --git a/ci/run_smoke_tests.sh b/ci/run_smoke_tests.sh index 7cbd390d..517822ef 100755 --- a/ci/run_smoke_tests.sh +++ b/ci/run_smoke_tests.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -source ../lib/utility-functions.sh - export ANSIBLE_HOST_KEY_CHECKING=False ./dev_dep_check.sh diff --git a/ci/util.sh b/ci/util.sh index 1a931d0b..a9df0213 100755 --- a/ci/util.sh +++ b/ci/util.sh @@ -2,12 +2,88 @@ # Utility script used to interact with a deployment # @author Tim Rozet (trozet@redhat.com) -BASE=${BASE:-'/var/opt/opnfv'} -IMAGES=${IMAGES:-"$BASE/images"} -LIB=${LIB:-"$BASE/lib"} VALID_CMDS="undercloud overcloud opendaylight debug-stack mock-detached -h --help" +SSH_OPTIONS=(-o StrictHostKeyChecking=no -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null -o LogLevel=error) -source $LIB/utility-functions.sh +##connects to undercloud +##params: user to login with, command to execute on undercloud (optional) +function undercloud_connect { + local user=$1 + + if [ -z "$1" ]; then + echo "Missing required argument: user to login as to undercloud" + return 1 + fi + + if [ -z "$2" ]; then + ssh ${SSH_OPTIONS[@]} ${user}@$(get_undercloud_ip) + else + ssh ${SSH_OPTIONS[@]} -T ${user}@$(get_undercloud_ip) "$2" + fi +} + +##outputs the Undercloud's IP address +##params: none +function get_undercloud_ip { + echo $(arp -an | grep $(virsh domiflist undercloud | grep default |\ + awk '{print $5}') | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") +} + +##connects to overcloud nodes +##params: node to login to, command to execute on overcloud (optional) +function overcloud_connect { + local node + local node_output + local node_ip + + if [ -z "$1" ]; then + echo "Missing required argument: overcloud node to login to" + return 1 + elif ! echo "$1" | grep -E "(controller|compute)[0-9]+" > /dev/null; then + echo "Invalid argument: overcloud node to login to must be in the format: \ +controller or compute" + return 1 + fi + + node_output=$(undercloud_connect "stack" "source stackrc; nova list") + node=$(echo "$1" | sed -E 's/([a-zA-Z]+)([0-9]+)/\1-\2/') + + node_ip=$(echo "$node_output" | grep "$node" | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") + + if [ "$node_ip" == "" ]; then + echo -e "Unable to find IP for ${node} in \n${node_output}" + return 1 + fi + + if [ -z "$2" ]; then + ssh ${SSH_OPTIONS[@]} heat-admin@${node_ip} + else + ssh ${SSH_OPTIONS[@]} -T heat-admin@${node_ip} "$2" + fi +} + +##connects to opendaylight karaf console +##params: None +function opendaylight_connect { + local opendaylight_ip + opendaylight_ip=$(undercloud_connect "stack" "cat overcloudrc | grep SDN_CONTROLLER_IP | grep -Eo [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") + + if [ "$opendaylight_ip" == "" ]; then + echo -e "Unable to find IP for OpenDaylight in overcloudrc" + return 1 + else + echo -e "Connecting to ODL Karaf console. Default password is 'karaf'" + fi + + ssh -p 8101 ${SSH_OPTIONS[@]} karaf@${opendaylight_ip} +} + +##outputs heat stack deployment failures +##params: none +function debug_stack { + source ~/stackrc + openstack stack failures list overcloud --long +} resolve_cmd() { local given=$1 diff --git a/lib/ansible/playbooks/build_dependencies.yml b/lib/ansible/playbooks/build_dependencies.yml index dec8ab7c..afe12b74 100644 --- a/lib/ansible/playbooks/build_dependencies.yml +++ b/lib/ansible/playbooks/build_dependencies.yml @@ -11,15 +11,17 @@ libguestfs-tools,bsdtar,libvirt,yum-utils, python2-oslo-config,python2-debtcollector, make, python34-pip, python-virtualenv,libguestfs-tools-c, - supermin,supermin5,perl-Sys-Guestfs,python-libguestfs + supermin,supermin5,perl-Sys-Guestfs,python-libguestfs, + libvirt-devel,python34-docutils,python-docutils - name: Install Virtualization group yum: name: "@Virtualization Host" - - name: Install python ipmi from OPNFV artifacts - yum: - name: 'http://artifacts.opnfv.org/apex/dependencies/python3-ipmi-0.3.0-1.noarch.rpm' + - pip: + name: python-ipmi + executable: pip3.4 - pip: name: tox + executable: pip3.4 - pip: name: gitpython executable: pip3.4 diff --git a/lib/ansible/playbooks/configure_undercloud.yml b/lib/ansible/playbooks/configure_undercloud.yml new file mode 100644 index 00000000..7b236624 --- /dev/null +++ b/lib/ansible/playbooks/configure_undercloud.yml @@ -0,0 +1,116 @@ +--- +- hosts: all + tasks: + - name: Generate SSH key for stack if missing + shell: test -e ~/.ssh/id_rsa || ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa + - name: Fix ssh key for stack + shell: restorecon -r /home/stack + become: yes + - file: + path: /home/stack/nics + state: directory + owner: stack + group: stack + mode: 0775 + - copy: + src: /root/.ssh/id_rsa.pub + dest: /home/stack/jumphost_id_rsa.pub + owner: stack + owner: stack + mode: 0644 + - copy: + src: "{{ apex_temp_dir }}/{{ item }}.yaml" + dest: "/home/stack/nics/{{ item }}.yaml" + owner: stack + group: stack + mode: 0644 + with_items: + - controller + - compute + - lineinfile: + path: /etc/sudoers + regexp: 'Defaults\s*requiretty' + state: absent + become: yes + - name: openstack-configs undercloud + shell: openstack-config --set undercloud.conf DEFAULT {{ item }} + with_items: "{{ undercloud_config }}" + - name: openstack-configs ironic + shell: openstack-config --set /etc/ironic/ironic.conf {{ item }} + become: yes + with_items: "{{ ironic_config }}" + - name: openstack-configs undercloud aarch64 + shell: openstack-config --set undercloud.conf DEFAULT ipxe_enabled false + when: "{{ aarch64 }}" + - lineinfile: + path: /usr/lib/python2.7/site-packages/ironic/common/pxe_utils.py + regexp: '_link_ip_address_pxe_configs' + line: '_link_mac_pxe_configs(task)' + when: "{{ aarch64 }}" + - name: undercloud install + shell: openstack undercloud install &> apex-undercloud-install.log + become: yes + become_user: stack + - name: openstack-configs nova + shell: openstack-config --set /etc/nova/nova.conf DEFAULT {{ item }} + become: yes + with_items: "{{ nova_config }}" + - name: restart nova services + service: + name: "{{ item }}" + state: restarted + enabled: yes + with_items: + - openstack-nova-conductor + - openstack-nova-compute + - openstack-nova-api + - openstack-nova-scheduler + - name: openstack-configs neutron + shell: openstack-config --set /etc/neutron/neutron.conf DEFAULT {{ item }} + become: yes + with_items: "{{ neutron_config }}" + - name: restart neutron services + service: + name: "{{ item }}" + state: restarted + enabled: yes + with_items: + - neutron-server + - neutron-dhcp-agent + - name: configure external network vlan ifcfg + template: + src: external_vlan_ifcfg.yml.j2 + dest: "/etc/sysconfig/network-scripts/ifcfg-vlan{{ external_network.vlan }}" + owner: root + group: root + mode: 0644 + become: yes + when: + - external_network.vlan != "native" + - external_network.enabled + - name: bring up vlan ifcfg + shell: "ifup vlan{{ external_network.vlan }}" + become: yes + when: + - external_network.vlan != "native" + - external_network.enabled + - name: assign IP to native eth2 + shell: ip a a {{ external_network.ip }}/{{ external_network.prefix }} dev eth2 + become: yes + when: + - external_network.vlan == "native" + - external_network.enabled + - name: bring up eth2 + shell: ip link set up dev eth2 + when: + - external_network.vlan == "native" + - external_network.enabled + become: yes + - name: fetch storage environment file + fetch: + src: /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml + dest: "{{ apex_temp_dir }}/" + flat: yes + +- include: undercloud_aarch64.yml + when: aarch64 diff --git a/lib/ansible/playbooks/deploy_dependencies.yml b/lib/ansible/playbooks/deploy_dependencies.yml new file mode 100644 index 00000000..77231622 --- /dev/null +++ b/lib/ansible/playbooks/deploy_dependencies.yml @@ -0,0 +1,66 @@ +--- +- hosts: localhost + tasks: + - sysctl: + name: net.ipv4.ip_forward + state: present + value: 1 + sysctl_set: yes + - systemd: + name: dhcpd + state: stopped + enabled: no + ignore_errors: yes + - systemd: + name: libvirtd + state: started + enabled: yes + - systemd: + name: openvswitch + state: started + enabled: yes + - virt_net: + command: define + name: default + xml: '{{ lookup("template", "virsh_network_default.xml.j2") }}' + state: active + autostart: yes + - openvswitch_bridge: + bridge: 'br-{{ item }}' + state: present + with_items: '{{ virsh_enabled_networks }}' + - virt_net: + command: define + name: '{{ item }}' + xml: '{{ lookup("template", "virsh_network_ovs.xml.j2") }}' + autostart: yes + with_items: '{{ virsh_enabled_networks }}' + - virt_net: + command: create + name: '{{ item }}' + with_items: '{{ virsh_enabled_networks }}' + - virt_pool: + name: default + command: define + autostart: yes + state: active + xml: '{{ lookup("template", "virsh_pool.xml.j2") }}' + - lineinfile: + path: /etc/modprobe.d/kvm_intel.conf + line: 'options kvm-intel nested=1' + create: yes + when: ansible_architecture == "x86_64" + - modprobe: + name: "{{ item }}" + state: present + with_items: + - kvm + - kvm_intel + when: ansible_architecture == "x86_64" + - name: Generate SSH key for root if missing + shell: test -e ~/.ssh/id_rsa || ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa + - name: Manually patch vmbc to work with python3.x + lineinfile: + line: " conn.defineXML(ET.tostring(tree, encoding='unicode'))" + regexp: "tostring" + path: /usr/lib/python3.4/site-packages/virtualbmc/vbmc.py diff --git a/lib/ansible/playbooks/deploy_overcloud.yml b/lib/ansible/playbooks/deploy_overcloud.yml new file mode 100644 index 00000000..76bbbc67 --- /dev/null +++ b/lib/ansible/playbooks/deploy_overcloud.yml @@ -0,0 +1,68 @@ +--- +- hosts: all + tasks: + - name: Copy all files to undercloud + copy: + src: "{{ apex_temp_dir }}/{{ item }}" + dest: "/home/stack/{{ item }}" + owner: stack + group: stack + mode: 0644 + with_items: + - network-environment.yaml + - instackenv.json + - opnfv-environment.yaml + - overcloud-full.qcow2 + - deploy_command + - virtual-environment.yaml + - baremetal-environment.yaml + - copy: + src: "{{ apex_temp_dir }}/storage-environment.yaml" + dest: /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml + owner: root + group: root + mode: 0664 + - systemd: + name: openstack-swift-proxy + state: restarted + enabled: yes + become: yes + - name: Upload glance images + shell: "{{ stackrc }} && openstack overcloud image upload" + become: yes + become_user: stack + - name: Import inventory (baremetal) + shell: "{{ stackrc }} && {{ item }}" + with_items: + - openstack overcloud node import instackenv.json + - openstack overcloud node introspect --all-manageable --provide + when: not virtual + - name: Import inventory (virtual) + shell: "{{ stackrc }} && openstack overcloud node import --provide instackenv.json" + when: virtual + - name: Set flavors + shell: '{{ stackrc }} && openstack flavor set --property "cpu_arch"="x86_64" {{ item }}' + with_items: + - baremetal + - control + - compute + - name: Configure DNS server for ctlplane network + shell: "{{ stackrc }} && openstack subnet set ctlplane-subnet {{ dns_server_args }}" + - name: Execute Overcloud Deployment + shell: "{{ stackrc }} && bash deploy_command" + - name: Show Keystone output + shell: "{{ overcloudrc }} && {{ item }}" + when: debug + with_items: + - openstack endpoint list + - openstack service list + - name: Get overcloud nodes and IPs + shell: "{{ stackrc }} && openstack server list -f json" + register: nova_list + - name: Write nova list output to file + local_action: copy content="{{ nova_list.stdout }}" dest="{{ apex_temp_dir }}/nova_output" + - name: Fetch overcloudrc + fetch: + src: /home/stack/overcloudrc + dest: "{{ apex_temp_dir }}/" + flat: yes diff --git a/lib/ansible/playbooks/post_deploy_overcloud.yml b/lib/ansible/playbooks/post_deploy_overcloud.yml new file mode 100644 index 00000000..fdf70240 --- /dev/null +++ b/lib/ansible/playbooks/post_deploy_overcloud.yml @@ -0,0 +1,45 @@ +--- +- hosts: all + tasks: + - name: Bring up br-phy for OVS DPDK + shell: ifup br-phy + when: + - dataplane == 'ovs_dpdk' + - "'compute' in ansible_hostname" + become: yes + - name: Restart OVS Agent for DPDK + shell: systemctl restart neutron-openvswitch-agent + when: + - dataplane == 'ovs_dpdk' + - "'compute' in ansible_hostname" + - sdn == false + - name: SFC config workaround + file: + src: /etc/neutron/networking_sfc.conf + dest: /etc/neutron/conf.d/neutron-server/networking_sfc.conf + state: link + become: yes + when: + - sfc + - "'controller' in ansible_hostname" + - name: Ensure ZRPCD is up + systemd: + name: zrpcd + state: started + enabled: yes + become: yes + when: + - vpn + - "'controller-0' in ansible_hostname" + - name: VSPERF build base machine + shell: /build_base_machine.sh + args: + chdir: /var/opt/vsperf/systems/ + become: yes + when: + - vsperf + - "'compute-0' in ansible_hostname" + - name: Fetch logs from node + fetch: + src: /var/log/messages + dest: "{{ apex_temp_dir }}" diff --git a/lib/ansible/playbooks/post_deploy_undercloud.yml b/lib/ansible/playbooks/post_deploy_undercloud.yml new file mode 100644 index 00000000..ba0746b2 --- /dev/null +++ b/lib/ansible/playbooks/post_deploy_undercloud.yml @@ -0,0 +1,118 @@ +--- +- hosts: all + tasks: + - name: Enable ssh to overcloud nodes from jumphost + shell: "cat /home/stack/jumphost_id_rsa.pub | ssh -T {{ SSH_OPTIONS }} heat-admin@{{ item.value }} 'cat >> ~/.ssh/authorized_keys'" + with_dict: "{{ overcloud_nodes }}" + become: yes + become_user: stack + - name: Configure external network + shell: "{{ overcloudrc }} && {{ item }}" + with_items: "{{ external_network_cmds }}" + - name: Configure gluon networks + shell: "{{ overcloudrc }} && {{ item }}" + when: gluon + with_items: + - openstack network create gluon-network --share --provider-network-type vxlan + - openstack subnet create gluon-subnet --no-gateway --no-dhcp --network GluonNetwork --subnet-range 0.0.0.0/1 + - name: Find admin project id + shell: "{{ overcloudrc }} && openstack project list | grep admin | awk '{print $2}'" + register: os_project_id + - name: Inject OS_PROJECT_ID and OS_TENANT_NAME into overcloudrc + lineinfile: + line: "{{ item }}" + path: /home/stack/overcloudrc + with_items: + - "export OS_PROJECT_ID={{ os_project_id.stdout }}" + - "export OS_TENANT_NAME=admin" + - name: Install Docker + yum: + name: docker + state: present + when: yardstick or dovetail + become: yes + - systemd: + name: docker + state: started + enabled: yes + when: yardstick or dovetail + become: yes + - name: Pull yardstick docker image + docker_image: + name: opnfv/yardstick + when: yardstick + become: yes + - name: Pull dovetail docker image + docker_image: + name: opnfv/dovetail + when: dovetail + become: yes + - name: Register SDN VIP + shell: "{{ stackrc }} && neutron port-list | grep control_virtual_ip | grep -Eo '([0-9]+\\.){3}[0-9]+'" + register: sdn_vip + become: yes + become_user: stack + when: sdn != false + - name: Write SDN controller VIP to overcloudrc + lineinfile: + line: "export SDN_CONTROLLER_IP={{ sdn_vip.stdout }}" + regexp: 'SDN_CONTROLLER_IP' + path: "/home/stack/{{ item }}" + when: sdn != false + with_items: + - overcloudrc + - overcloudrc.v3 + - name: Undercloud NAT - MASQUERADE interface + iptables: + table: nat + chain: POSTROUTING + out_interface: eth0 + jump: MASQUERADE + when: + - virtual + - not external_network_ipv6 + become: yes + - name: Undercloud NAT - MASQUERADE interface with subnet + iptables: + table: nat + chain: POSTROUTING + out_interface: eth0 + jump: MASQUERADE + source: "{{ external_cidr }}" + when: + - virtual + - not external_network_ipv6 + become: yes + - name: Undercloud NAT - Allow Forwarding + iptables: + chain: FORWARD + in_interface: eth2 + jump: ACCEPT + when: + - virtual + - not external_network_ipv6 + become: yes + - name: Undercloud NAT - Allow Stateful Forwarding + iptables: + chain: FORWARD + in_interface: eth2 + jump: ACCEPT + source: "{{ external_cidr }}" + ctstate: ESTABLISHED,RELATED + when: + - virtual + - not external_network_ipv6 + become: yes + - name: Undercloud NAT - Save iptables + shell: service iptables save + become: yes + when: + - virtual + - not external_network_ipv6 + - name: Create congress datasources + shell: "{{ overcloudrc }} && openstack congress datasource create {{ item }}" + become: yes + become_user: stack + when: congress + with_items: "{{ congress_datasources }}" + ignore_errors: yes diff --git a/lib/ansible/playbooks/templates/external_vlan_ifcfg.yml.j2 b/lib/ansible/playbooks/templates/external_vlan_ifcfg.yml.j2 new file mode 100644 index 00000000..c478a7d9 --- /dev/null +++ b/lib/ansible/playbooks/templates/external_vlan_ifcfg.yml.j2 @@ -0,0 +1,9 @@ +DEVICE=vlan{{ external_network.vlan }} +ONBOOT=yes +DEVICETYPE=ovs +TYPE=OVSIntPort +BOOTPROTO=static +IPADDR={{ external_network.ip }} +PREFIX={{ external_network.prefix }} +OVS_BRIDGE=br-ctlplane +OVS_OPTIONS="tag={{ external_network.vlan }}" diff --git a/lib/ansible/playbooks/templates/virsh_network_default.xml.j2 b/lib/ansible/playbooks/templates/virsh_network_default.xml.j2 new file mode 100644 index 00000000..d7241d0c --- /dev/null +++ b/lib/ansible/playbooks/templates/virsh_network_default.xml.j2 @@ -0,0 +1,10 @@ + + default + + + + + + + + diff --git a/lib/ansible/playbooks/templates/virsh_network_ovs.xml.j2 b/lib/ansible/playbooks/templates/virsh_network_ovs.xml.j2 new file mode 100644 index 00000000..75a06eea --- /dev/null +++ b/lib/ansible/playbooks/templates/virsh_network_ovs.xml.j2 @@ -0,0 +1,6 @@ + + {{ item }} + + + + diff --git a/lib/ansible/playbooks/templates/virsh_pool.xml.j2 b/lib/ansible/playbooks/templates/virsh_pool.xml.j2 new file mode 100644 index 00000000..f6ea498a --- /dev/null +++ b/lib/ansible/playbooks/templates/virsh_pool.xml.j2 @@ -0,0 +1,6 @@ + + default + + /var/lib/libvirt/images + + diff --git a/lib/ansible/playbooks/undercloud_aarch64.yml b/lib/ansible/playbooks/undercloud_aarch64.yml new file mode 100644 index 00000000..5b607c3e --- /dev/null +++ b/lib/ansible/playbooks/undercloud_aarch64.yml @@ -0,0 +1,49 @@ +--- +- hosts: all + tasks: + - name: aarch64 configuration + block: + - shell: yum -y reinstall grub2-efi shim + - copy: + src: /boot/efi/EFI/centos/grubaa64.efi + dest: /tftpboot/grubaa64.efi + remote_src: yes + - file: + path: /tftpboot/EFI/centos + state: directory + mode: 0755 + - copy: + content: | + set default=master + set timeout=5 + set hidden_timeout_quiet=false + menuentry "master" { + configfile /tftpboot/\\\$net_default_ip.conf + } + dest: /tftpboot/EFI/centos/grub.cfg + mode: 0644 + - shell: 'openstack-config --set /etc/ironic/ironic.conf pxe uefi_pxe_config_template $pybasedir/drivers/modules/pxe_grub_config.template' + - shell: 'openstack-config --set /etc/ironic/ironic.conf pxe uefi_pxe_bootfile_name grubaa64.efi' + - systemd: + name: openstack-ironic-conductor + state: restarted + enabled: yes + - replace: + path: /usr/lib/python2.7/site-packages/ironic/drivers/modules/pxe_grub_config.template + regexp: 'linuxefi' + replace: 'linux' + - replace: + path: /usr/lib/python2.7/site-packages/ironic/drivers/modules/pxe_grub_config.template + regexp: 'initrdefi' + replace: 'initrd' + - lineinfile: + path: /tftpboot/map-file + insertafter: EOF + state: present + line: '' + - shell: "echo 'r ^/EFI/centos/grub.cfg-(.*) /tftpboot/pxelinux.cfg/\\1' | sudo tee --append /tftpboot/map-file" + - systemd: + name: xinetd + state: restarted + enabled: yes + become: yes diff --git a/lib/common-functions.sh b/lib/common-functions.sh deleted file mode 100644 index 709dbf97..00000000 --- a/lib/common-functions.sh +++ /dev/null @@ -1,308 +0,0 @@ -#!/usr/bin/env bash -############################################################################## -# Copyright (c) 2015 Tim Rozet (Red Hat), Dan Radez (Red Hat) and others. -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## - -# Common Functions used by OPNFV Apex -# author: Tim Rozet (trozet@redhat.com) - -##converts subnet mask to prefix -##params: subnet mask -function prefix2mask { - # Number of args to shift, 255..255, first non-255 byte, zeroes - set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0 - [ $1 -gt 1 ] && shift $1 || shift - echo ${1-0}.${2-0}.${3-0}.${4-0} -} - -##find ip of interface -##params: interface name, address family -function find_ip { - local af - if [[ -z "$1" ]]; then - return 1 - fi - if [[ -z "$2" ]]; then - af=4 - else - af=$2 - fi - - python3 -B $LIB/python/apex_python_utils.py find-ip -i $1 -af $af -} - -##attach interface to OVS and set the network config correctly -##params: bride to attach to, interface to attach, network type (optional) -##external indicates attaching to a external interface -function attach_interface_to_ovs { - local bridge interface - local if_ip if_mask if_gw if_file ovs_file if_prefix - local if_metric if_dns1 if_dns2 - - if [[ -z "$1" || -z "$2" ]]; then - return 1 - else - bridge=$1 - interface=$2 - fi - - if ovs-vsctl list-ports ${bridge} | grep ${interface}; then - return 0 - fi - - if_file=/etc/sysconfig/network-scripts/ifcfg-${interface} - ovs_file=/etc/sysconfig/network-scripts/ifcfg-${bridge} - - if [ -e "$if_file" ]; then - if_ip=$(sed -n 's/^IPADDR=\(.*\)$/\1/p' ${if_file}) - if_mask=$(sed -n 's/^NETMASK=\(.*\)$/\1/p' ${if_file}) - if_gw=$(sed -n 's/^GATEWAY=\(.*\)$/\1/p' ${if_file}) - if_metric=$(sed -n 's/^METRIC=\(.*\)$/\1/p' ${if_file}) - if_dns1=$(sed -n 's/^DNS1=\(.*\)$/\1/p' ${if_file}) - if_dns2=$(sed -n 's/^DNS2=\(.*\)$/\1/p' ${if_file}) - else - echo "ERROR: ifcfg file missing for ${interface}" - return 1 - fi - - if [ -z "$if_mask" ]; then - # we can look for PREFIX here, then convert it to NETMASK - if_prefix=$(sed -n 's/^PREFIX=[^0-9]*\([0-9][0-9]*\)[^0-9]*$/\1/p' ${if_file}) - if_mask=$(prefix2mask ${if_prefix}) - fi - - if [[ -z "$if_ip" || -z "$if_mask" ]]; then - echo "ERROR: IPADDR or NETMASK/PREFIX missing for ${interface}" - return 1 - elif [[ -z "$if_gw" && "$3" == "external" ]]; then - echo "ERROR: GATEWAY missing for ${interface}, which is external" - return 1 - fi - - # move old config file to .orig - mv -f ${if_file} ${if_file}.orig - echo "DEVICE=${interface} -DEVICETYPE=ovs -TYPE=OVSPort -PEERDNS=no -BOOTPROTO=static -NM_CONTROLLED=no -ONBOOT=yes -OVS_BRIDGE=${bridge} -PROMISC=yes" > ${if_file} - - - # create bridge cfg - echo "DEVICE=${bridge} -DEVICETYPE=ovs -IPADDR=${if_ip} -NETMASK=${if_mask} -BOOTPROTO=static -ONBOOT=yes -TYPE=OVSBridge -PROMISC=yes -PEERDNS=no" > ${ovs_file} - - if [ -n "$if_gw" ]; then - echo "GATEWAY=${if_gw}" >> ${ovs_file} - fi - - if [ -n "$if_metric" ]; then - echo "METRIC=${if_metric}" >> ${ovs_file} - fi - - if [[ -n "$if_dns1" || -n "$if_dns2" ]]; then - sed -i '/PEERDNS/c\PEERDNS=yes' ${ovs_file} - - if [ -n "$if_dns1" ]; then - echo "DNS1=${if_dns1}" >> ${ovs_file} - fi - - if [ -n "$if_dns2" ]; then - echo "DNS2=${if_dns2}" >> ${ovs_file} - fi - fi - - sudo systemctl restart network -} - -##detach interface from OVS and set the network config correctly -##params: bridge to detach from -##assumes only 1 real interface attached to OVS -function detach_interface_from_ovs { - local bridge - local port_output ports_no_orig - local net_path - local if_ip if_mask if_gw if_prefix - local if_metric if_dns1 if_dns2 - - net_path=/etc/sysconfig/network-scripts/ - if [[ -z "$1" ]]; then - return 1 - else - bridge=$1 - fi - - # if no interfaces attached then return - if ! ovs-vsctl list-ports ${bridge} | grep -Ev "vnet[0-9]*"; then - return 0 - fi - - # look for .orig ifcfg files to use - port_output=$(ovs-vsctl list-ports ${bridge} | grep -Ev "vnet[0-9]*") - while read -r line; do - if [ -z "$line" ]; then - continue - elif [ -e ${net_path}/ifcfg-${line}.orig ]; then - mv -f ${net_path}/ifcfg-${line}.orig ${net_path}/ifcfg-${line} - elif [ -e ${net_path}/ifcfg-${bridge} ]; then - if_ip=$(sed -n 's/^IPADDR=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge}) - if_mask=$(sed -n 's/^NETMASK=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge}) - if_gw=$(sed -n 's/^GATEWAY=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge}) - if_metric=$(sed -n 's/^METRIC=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge}) - if_dns1=$(sed -n 's/^DNS1=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge}) - if_dns2=$(sed -n 's/^DNS2=\(.*\)$/\1/p' ${net_path}/ifcfg-${bridge}) - - if [ -z "$if_mask" ]; then - if_prefix=$(sed -n 's/^PREFIX=[^0-9]*\([0-9][0-9]*\)[^0-9]*$/\1/p' ${net_path}/ifcfg-${bridge}) - if_mask=$(prefix2mask ${if_prefix}) - fi - - if [[ -z "$if_ip" || -z "$if_mask" ]]; then - echo "ERROR: IPADDR or PREFIX/NETMASK missing for ${bridge} and no .orig file for interface ${line}" - return 1 - fi - - # create if cfg - echo "DEVICE=${line} -IPADDR=${if_ip} -NETMASK=${if_mask} -BOOTPROTO=static -ONBOOT=yes -TYPE=Ethernet -NM_CONTROLLED=no -PEERDNS=no" > ${net_path}/ifcfg-${line} - - if [ -n "$if_gw" ]; then - echo "GATEWAY=${if_gw}" >> ${net_path}/ifcfg-${line} - fi - - if [ -n "$if_metric" ]; then - echo "METRIC=${if_metric}" >> ${net_path}/ifcfg-${line} - fi - - if [[ -n "$if_dns1" || -n "$if_dns2" ]]; then - sed -i '/PEERDNS/c\PEERDNS=yes' ${net_path}/ifcfg-${line} - - if [ -n "$if_dns1" ]; then - echo "DNS1=${if_dns1}" >> ${net_path}/ifcfg-${line} - fi - - if [ -n "$if_dns2" ]; then - echo "DNS2=${if_dns2}" >> ${net_path}/ifcfg-${line} - fi - fi - break - else - echo "ERROR: Real interface ${line} attached to bridge, but no interface or ${bridge} ifcfg file exists" - return 1 - fi - - done <<< "$port_output" - - # modify the bridge ifcfg file - # to remove IP params - sudo sed -i 's/IPADDR=.*//' ${net_path}/ifcfg-${bridge} - sudo sed -i 's/NETMASK=.*//' ${net_path}/ifcfg-${bridge} - sudo sed -i 's/GATEWAY=.*//' ${net_path}/ifcfg-${bridge} - sudo sed -i 's/DNS1=.*//' ${net_path}/ifcfg-${bridge} - sudo sed -i 's/DNS2=.*//' ${net_path}/ifcfg-${bridge} - sudo sed -i 's/METRIC=.*//' ${net_path}/ifcfg-${bridge} - sudo sed -i 's/PEERDNS=.*//' ${net_path}/ifcfg-${bridge} - - sudo systemctl restart network -} - -# Update iptables rule for external network reach internet -# for virtual deployments -# params: external_cidr -function configure_undercloud_nat { - local external_cidr - if [[ -z "$1" ]]; then - return 1 - else - external_cidr=$1 - fi - - ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" < /dev/null; then - return 0 - else - return 1 - fi -} - -##verify internet connectivity -#params: none -function verify_internet { - if ping -c 2 $ping_site > /dev/null; then - if ping -c 2 $dnslookup_site > /dev/null; then - echo "${blue}Internet connectivity detected${reset}" - return 0 - else - echo "${red}Internet connectivity detected, but DNS lookup failed${reset}" - return 1 - fi - else - echo "${red}No internet connectivity detected${reset}" - return 1 - fi -} - -##tests if overcloud nodes have external connectivity -#params:none -function test_overcloud_connectivity { - for node in $(undercloud_connect stack ". stackrc && nova list" | grep -Eo "controller-[0-9]+|compute-[0-9]+" | tr -d -) ; do - if ! overcloud_connect $node "ping -c 2 $ping_site > /dev/null"; then - echo "${blue}Node ${node} was unable to ping site ${ping_site}${reset}" - return 1 - fi - done - echo "${blue}Overcloud external connectivity OK${reset}" -} - diff --git a/lib/configure-deps-functions.sh b/lib/configure-deps-functions.sh deleted file mode 100755 index 4c00fbf3..00000000 --- a/lib/configure-deps-functions.sh +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env bash -############################################################################## -# Copyright (c) 2015 Tim Rozet (Red Hat), Dan Radez (Red Hat) and others. -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## - -##download dependencies if missing and configure host -#params: none -function configure_deps { - if ! verify_internet; then - echo "${red}Will not download dependencies${reset}" - internet=false - fi - - # verify ip forwarding - if sysctl net.ipv4.ip_forward | grep 0; then - sudo sysctl -w net.ipv4.ip_forward=1 - sudo sh -c "echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf" - fi - - # ensure no dhcp server is running on jumphost - if ! sudo systemctl status dhcpd | grep dead; then - echo "${red}WARN: DHCP Server detected on jumphost, disabling...${reset}" - sudo systemctl stop dhcpd - sudo systemctl disable dhcpd - fi - - # ensure networks are configured - systemctl status libvirtd || systemctl start libvirtd - systemctl status openvswitch || systemctl start openvswitch - - # For baremetal we only need to create/attach Undercloud to admin and external - if [ "$virtual" == "FALSE" ]; then - virsh_enabled_networks="admin external" - else - virsh_enabled_networks=$enabled_network_list - fi - - # ensure default network is configured correctly - libvirt_dir="/usr/share/libvirt/networks" - virsh net-list --all | grep default || virsh net-define ${libvirt_dir}/default.xml - virsh net-list --all | grep -E "default\s+active" > /dev/null || virsh net-start default - virsh net-list --all | grep -E "default\s+active\s+yes" > /dev/null || virsh net-autostart --network default - - if [[ -z "$virtual" || "$virtual" == "FALSE" ]]; then - for network in ${enabled_network_list}; do - echo "${blue}INFO: Creating Virsh Network: $network & OVS Bridge: ${NET_MAP[$network]}${reset}" - ovs-vsctl list-br | grep "^${NET_MAP[$network]}$" > /dev/null || ovs-vsctl add-br ${NET_MAP[$network]} - virsh net-list --all | grep " $network " > /dev/null || (cat > ${libvirt_dir}/apex-virsh-net.xml && virsh net-define ${libvirt_dir}/apex-virsh-net.xml) << EOF - - $network - - - - -EOF - if ! (virsh net-list --all | grep " $network " > /dev/null); then - echo "${red}ERROR: unable to create network: ${network}${reset}" - exit 1; - fi - rm -f ${libvirt_dir}/apex-virsh-net.xml &> /dev/null; - virsh net-list | grep -E "$network\s+active" > /dev/null || virsh net-start $network - virsh net-list | grep -E "$network\s+active\s+yes" > /dev/null || virsh net-autostart --network $network - done - - echo -e "${blue}INFO: Bridges set: ${reset}" - ovs-vsctl list-br - - # bridge interfaces to correct OVS instances for baremetal deployment - for network in ${enabled_network_list}; do - if [[ "$network" != "admin" && "$network" != "external" ]]; then - continue - fi - this_interface=$(eval echo \${${network}_installer_vm_members}) - # check if this a bridged interface for this network - if [[ ! -z "$this_interface" || "$this_interface" != "none" ]]; then - if ! attach_interface_to_ovs ${NET_MAP[$network]} ${this_interface} ${network}; then - echo -e "${red}ERROR: Unable to bridge interface ${this_interface} to bridge ${NET_MAP[$network]} for enabled network: ${network}${reset}" - exit 1 - else - echo -e "${blue}INFO: Interface ${this_interface} bridged to bridge ${NET_MAP[$network]} for enabled network: ${network}${reset}" - fi - else - echo "${red}ERROR: Unable to determine interface to bridge to for enabled network: ${network}${reset}" - exit 1 - fi - done - else - # verify virtualbmc is installed for a virtual install - if ! rpm -q python2-virtualbmc; then - echo -e "${red}ERROR: Package python2-virtualbmc is required to do a virtual install.$reset" - exit 1 - fi - for network in ${OPNFV_NETWORK_TYPES}; do - if ! ovs-vsctl --may-exist add-br ${NET_MAP[$network]}; then - echo -e "${red}ERROR: Failed to create ovs bridge ${NET_MAP[$network]}${reset}" - exit 1 - fi - echo "${blue}INFO: Creating Virsh Network: $network${reset}" - virsh net-list --all | grep " $network " > /dev/null || (cat > ${libvirt_dir}/apex-virsh-net.xml && virsh net-define ${libvirt_dir}/apex-virsh-net.xml) << EOF - -$network - - - - -EOF - if ! (virsh net-list --all | grep $network > /dev/null); then - echo "${red}ERROR: unable to create network: ${network}${reset}" - exit 1; - fi - rm -f ${libvirt_dir}/apex-virsh-net.xml &> /dev/null; - virsh net-list | grep -E "$network\s+active" > /dev/null || virsh net-start $network - virsh net-list | grep -E "$network\s+active\s+yes" > /dev/null || virsh net-autostart --network $network - done - - echo -e "${blue}INFO: Bridges set: ${reset}" - ovs-vsctl list-br - fi - - echo -e "${blue}INFO: virsh networks set: ${reset}" - virsh net-list - - # ensure storage pool exists and is started - virsh pool-list --all | grep default > /dev/null || virsh pool-define-as --name default dir --target /var/lib/libvirt/images - virsh pool-list | grep -Eo "default\s+active" > /dev/null || (virsh pool-autostart default; virsh pool-start default) - - # Virt flag check is Arch dependent on x86 - if [ "$(uname -i)" == 'x86_64' ]; then - if ! egrep '^flags.*(vmx|svm)' /proc/cpuinfo > /dev/null; then - echo "${red}virtualization extensions not found, kvm kernel module insertion may fail.\n \ -Are you sure you have enabled vmx in your bios or hypervisor?${reset}" - fi - - if ! lsmod | grep kvm > /dev/null; then modprobe kvm; fi - if ! lsmod | grep kvm_intel > /dev/null; then modprobe kvm_intel; fi - - if ! lsmod | grep kvm > /dev/null; then - echo "${red}kvm kernel modules not loaded!${reset}" - return 1 - fi - - # try to enabled nested kvm - if [ "$virtual" == "TRUE" ]; then - nested_kvm=`cat /sys/module/kvm_intel/parameters/nested` - if [ "$nested_kvm" != "Y" ]; then - # try to enable nested kvm - echo 'options kvm-intel nested=1' > /etc/modprobe.d/kvm_intel.conf - if rmmod kvm_intel; then - modprobe kvm_intel - fi - nested_kvm=`cat /sys/module/kvm_intel/parameters/nested` - fi - if [ "$nested_kvm" != "Y" ]; then - echo "${red}Cannot enable nested kvm, falling back to qemu for deployment${reset}" - DEPLOY_OPTIONS+=" --libvirt-type qemu" - else - echo "${blue}Nested kvm enabled, deploying with kvm acceleration${reset}" - fi - fi - fi - - ##sshkeygen for root - if [ ! -e ~/.ssh/id_rsa.pub ]; then - ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa - fi - - echo "${blue}All dependencies installed and running${reset}" -} diff --git a/lib/configure-vm b/lib/configure-vm deleted file mode 100755 index 5cb45218..00000000 --- a/lib/configure-vm +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env python - -import argparse -import math -import os -import random - -import libvirt - -templatedir = os.getenv('LIB', '/var/opt/opnfv/lib') + '/installer/' - -MAX_NUM_MACS = math.trunc(0xff/2) - - -def generate_baremetal_macs(count=1): - """Generate an Ethernet MAC address suitable for baremetal testing.""" - # NOTE(dprince): We generate our own bare metal MAC address's here - # instead of relying on libvirt so that we can ensure the - # locally administered bit is set low. (The libvirt default is - # to set the 2nd MSB high.) This effectively allows our - # fake baremetal VMs to more accurately behave like real hardware - # and fixes issues with bridge/DHCP configurations which rely - # on the fact that bridges assume the MAC address of the lowest - # attached NIC. - # MACs generated for a given machine will also be in sequential - # order, which matches how most BM machines are laid out as well. - # Additionally we increment each MAC by two places. - macs = [] - - if count > MAX_NUM_MACS: - raise ValueError("The MAX num of MACS supported is %i." % MAX_NUM_MACS) - - base_nums = [0x00, - random.randint(0x00, 0xff), - random.randint(0x00, 0xff), - random.randint(0x00, 0xff), - random.randint(0x00, 0xff)] - base_mac = ':'.join(map(lambda x: "%02x" % x, base_nums)) - - start = random.randint(0x00, 0xff) - if (start + (count * 2)) > 0xff: - # leave room to generate macs in sequence - start = 0xff - count * 2 - for num in range(0, count*2, 2): - mac = start + num - macs.append(base_mac + ":" + ("%02x" % mac)) - return macs - -def main(): - parser = argparse.ArgumentParser( - description="Configure a kvm virtual machine for the seed image.") - parser.add_argument('--name', default='seed', - help='the name to give the machine in libvirt.') - parser.add_argument('--image', - help='Use a custom image file (must be qcow2).') - parser.add_argument('--diskbus', default='sata', - help='Choose an alternate bus type for the disk') - parser.add_argument('--baremetal-interface', nargs='+', default=['brbm'], - help='The interface which bare metal nodes will be connected to.') - parser.add_argument('--engine', default='kvm', - help='The virtualization engine to use') - parser.add_argument('--arch', default='i686', - help='The architecture to use') - parser.add_argument('--memory', default='2097152', - help="Maximum memory for the VM in KB.") - parser.add_argument('--cpus', default='1', - help="CPU count for the VM.") - parser.add_argument('--bootdev', default='hd', - help="What boot device to use (hd/network).") - parser.add_argument('--seed', default=False, action='store_true', - help='Create a seed vm with two interfaces.') - parser.add_argument('--ovsbridge', default="", - help='Place the seed public interface on this ovs bridge.') - parser.add_argument('--libvirt-nic-driver', default='virtio', - help='The libvirt network driver to use') - parser.add_argument('--enable-serial-console', action="store_true", - help='Enable a serial console') - parser.add_argument('--direct-boot', - help='Enable directboot to .{vmlinux & initrd}') - parser.add_argument('--kernel-arg', action="append", dest='kernel_args', - help='Kernel arguments, use multiple time for multiple args.') - parser.add_argument('--uri', default='qemu:///system', - help='The server uri with which to connect.') - args = parser.parse_args() - with file(templatedir + '/domain.xml', 'rb') as f: - source_template = f.read() - imagefile = '/var/lib/libvirt/images/seed.qcow2' - if args.image: - imagefile = args.image - imagefile = os.path.realpath(imagefile) - params = { - 'name': args.name, - 'imagefile': imagefile, - 'engine': args.engine, - 'arch': args.arch, - 'memory': args.memory, - 'cpus': args.cpus, - 'bootdev': args.bootdev, - 'network': '', - 'enable_serial_console': '', - 'direct_boot': '', - 'kernel_args': '', - 'user_interface': '', - } - if args.image is not None: - params['imagefile'] = args.image - - # Configure the bus type for the target disk device - params['diskbus'] = args.diskbus - nicparams = { - 'nicdriver': args.libvirt_nic_driver, - 'ovsbridge': args.ovsbridge, - } - if args.seed: - if args.ovsbridge: - params['network'] = """ - - - - - """ % nicparams - else: - params['network'] = """ - - - - - """ % nicparams - - macs = generate_baremetal_macs(len(args.baremetal_interface)) - - params['bm_network'] = "" - for bm_interface, mac in zip(args.baremetal_interface, macs): - bm_interface_params = { - 'bminterface': bm_interface, - 'bmmacaddress': mac, - 'nicdriver': args.libvirt_nic_driver, - } - params['bm_network'] += """ - - - - - - """ % bm_interface_params - - if args.enable_serial_console: - params['enable_serial_console'] = """ - - - - - - - """ - if args.direct_boot: - params['direct_boot'] = """ - /var/lib/libvirt/images/%(direct_boot)s.vmlinuz - /var/lib/libvirt/images/%(direct_boot)s.initrd - """ % { 'direct_boot': args.direct_boot } - if args.kernel_args: - params['kernel_args'] = """ - %s - """ % ' '.join(args.kernel_args) - - if args.arch == 'aarch64': - - params['direct_boot'] += """ - /usr/share/AAVMF/AAVMF_CODE.fd - /var/lib/libvirt/qemu/nvram/centos7.0_VARS.fd - """ - params['user_interface'] = """ - -
- - - - - - - - - -
- - """ - else: - params['user_interface'] = """ - - - - """ - - - libvirt_template = source_template % params - conn=libvirt.open(args.uri) - a = conn.defineXML(libvirt_template) - print ("Created machine %s with UUID %s" % (args.name, a.UUIDString())) - -if __name__ == '__main__': - main() diff --git a/lib/overcloud-deploy-functions.sh b/lib/overcloud-deploy-functions.sh deleted file mode 100755 index b52d0c28..00000000 --- a/lib/overcloud-deploy-functions.sh +++ /dev/null @@ -1,503 +0,0 @@ -#!/usr/bin/env bash -############################################################################## -# Copyright (c) 2015 Tim Rozet (Red Hat), Dan Radez (Red Hat) and others. -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## - -##preping it for deployment and launch the deploy -##params: none -function overcloud_deploy { - local num_compute_nodes - local num_control_nodes - local dpdk_cores pmd_cores socket_mem ovs_dpdk_perf_flag ovs_option_heat_arr - declare -A ovs_option_heat_arr - - ovs_option_heat_arr['dpdk_cores']=HostCpusList - ovs_option_heat_arr['pmd_cores']=NeutronDpdkCoreList - ovs_option_heat_arr['socket_memory']=NeutronDpdkSocketMemory - ovs_option_heat_arr['memory_channels']=NeutronDpdkMemoryChannels - - # OPNFV Default Environment and Network settings - DEPLOY_OPTIONS+=" -e ${ENV_FILE}" - DEPLOY_OPTIONS+=" -e network-environment.yaml" - - # get number of nodes available in inventory - num_control_nodes=$(ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "grep -c profile:control /home/stack/instackenv.json") - num_compute_nodes=$(ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "grep -c profile:compute /home/stack/instackenv.json") - - # Custom Deploy Environment Templates - if [[ "${#deploy_options_array[@]}" -eq 0 || "${deploy_options_array['sdn_controller']}" == 'opendaylight' ]]; then - if [ "${deploy_options_array['sfc']}" == 'True' ]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/neutron-sfc-opendaylight.yaml" - elif [ "${deploy_options_array['vpn']}" == 'True' ]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/neutron-bgpvpn-opendaylight.yaml" - if [ "${deploy_options_array['gluon']}" == 'True' ]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/services/gluon.yaml" - fi - elif [ "${deploy_options_array['vpp']}" == 'True' ]; then - if [ "${deploy_options_array['odl_vpp_netvirt']}" == "True" ]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/neutron-opendaylight-netvirt-vpp.yaml" - elif [ "${deploy_options_array['odl_vpp_routing_node']}" == "dvr" ]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/neutron-opendaylight-fdio-dvr.yaml" - else - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/neutron-opendaylight-honeycomb.yaml" - fi - else - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/neutron-opendaylight.yaml" - fi - SDN_IMAGE=opendaylight - elif [ "${deploy_options_array['sdn_controller']}" == 'opendaylight-external' ]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/opendaylight-external.yaml" - SDN_IMAGE=opendaylight - elif [ "${deploy_options_array['sdn_controller']}" == 'onos' ]; then - if [ "${deploy_options_array['sfc']}" == 'True' ]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/neutron-onos-sfc.yaml" - else - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/neutron-onos.yaml" - fi - SDN_IMAGE=onos - elif [ "${deploy_options_array['sdn_controller']}" == 'ovn' ]; then - if [[ "$ha_enabled" == "True" ]]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/neutron-ml2-ovn-ha.yaml" - echo "${red}OVN HA support is not not supported... exiting.${reset}" - exit 1 - else - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/neutron-ml2-ovn.yaml" - fi - SDN_IMAGE=opendaylight - elif [ "${deploy_options_array['sdn_controller']}" == 'opencontrail' ]; then - echo -e "${red}ERROR: OpenContrail is currently unsupported...exiting${reset}" - exit 1 - elif [[ -z "${deploy_options_array['sdn_controller']}" || "${deploy_options_array['sdn_controller']}" == 'False' ]]; then - echo -e "${blue}INFO: SDN Controller disabled...will deploy nosdn scenario${reset}" - if [ "${deploy_options_array['vpp']}" == 'True' ]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/neutron-ml2-vpp.yaml" - elif [ "${deploy_options_array['dataplane']}" == 'ovs_dpdk' ]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/neutron-ovs-dpdk.yaml" - fi - SDN_IMAGE=opendaylight - else - echo "${red}Invalid sdn_controller: ${deploy_options_array['sdn_controller']}${reset}" - echo "${red}Valid choices are opendaylight, opendaylight-external, onos, opencontrail, False, or null${reset}" - exit 1 - fi - - # Enable Tacker - if [ "${deploy_options_array['tacker']}" == 'True' ]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/enable_tacker.yaml" - fi - - # Enable Congress - if [ "${deploy_options_array['congress']}" == 'True' ]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/enable_congress.yaml" - fi - - # Enable Real Time Kernel (kvm4nfv) - if [ "${deploy_options_array['rt_kvm']}" == 'True' ]; then - DEPLOY_OPTIONS+=" -e /home/stack/enable_rt_kvm.yaml" - fi - - # Enable Barometer service - if [ "${deploy_options_array['barometer']}" == 'True' ]; then - DEPLOY_OPTIONS+=" -e /usr/share/openstack-tripleo-heat-templates/environments/enable_barometer.yaml" - fi - -# Make sure the correct overcloud image is available - if [ ! -f $IMAGES/overcloud-full-${SDN_IMAGE}.qcow2 ]; then - echo "${red} $IMAGES/overcloud-full-${SDN_IMAGE}.qcow2 is required to execute your deployment." - echo "Please install the opnfv-apex package to provide this overcloud image for deployment.${reset}" - exit 1 - fi - - echo "Copying overcloud image to Undercloud" - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" "rm -f overcloud-full.qcow2" - scp ${SSH_OPTIONS[@]} $IMAGES/overcloud-full-${SDN_IMAGE}.qcow2 "stack@$UNDERCLOUD":overcloud-full.qcow2 - - # disable neutron openvswitch agent from starting - if [[ -n "${deploy_options_array['sdn_controller']}" && "${deploy_options_array['sdn_controller']}" != 'False' ]]; then - echo -e "${blue}INFO: Disabling neutron-openvswitch-agent from systemd${reset}" - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" < vfio_pci.modules -#!/bin/bash -exec /sbin/modprobe vfio_pci >/dev/null 2>&1 -EOF - - cat << EOF > uio_pci_generic.modules -#!/bin/bash -exec /sbin/modprobe uio_pci_generic >/dev/null 2>&1 -EOF - - LIBGUESTFS_BACKEND=direct virt-customize --upload vfio_pci.modules:/etc/sysconfig/modules/ \ - --upload uio_pci_generic.modules:/etc/sysconfig/modules/ \ - --run-command "chmod 0755 /etc/sysconfig/modules/vfio_pci.modules" \ - --run-command "chmod 0755 /etc/sysconfig/modules/uio_pci_generic.modules" \ - -a overcloud-full.qcow2 - - if [ "${deploy_options_array['dataplane']}" == 'ovs_dpdk' ]; then - sed -i "/OS::TripleO::ComputeExtraConfigPre:/c\ OS::TripleO::ComputeExtraConfigPre: ./ovs-dpdk-preconfig.yaml" network-environment.yaml - fi - -EOI - - elif [ "${deploy_options_array['dataplane']}" != 'ovs' ]; then - echo "${red}${deploy_options_array['dataplane']} not supported${reset}" - exit 1 - fi - - if [ "$debug" == 'TRUE' ]; then - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" "LIBGUESTFS_BACKEND=direct virt-customize -a overcloud-full.qcow2 --root-password password:opnfvapex" - fi - - # upgrade ovs into ovs ovs 2.6.1 with NSH function if SFC is enabled - if [[ "${deploy_options_array['sfc']}" == 'True' && "${deploy_options_array['dataplane']}" == 'ovs' ]]; then - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" < /dev/null; then - sudo systemctl restart openstack-swift-proxy -fi -echo "Uploading overcloud glance images" -openstack overcloud image upload - -echo "Configuring undercloud and discovering nodes" - - -if [[ -z "$virtual" ]]; then - openstack overcloud node import instackenv.json - openstack overcloud node introspect --all-manageable --provide - #if [[ -n "$root_disk_list" ]]; then - # TODO: replace node configure boot with ironic node-update - # TODO: configure boot is not used in ocata here anymore - #openstack overcloud node configure boot --root-device=${root_disk_list} - #https://github.com/openstack/tripleo-quickstart-extras/blob/master/roles/overcloud-prep-images/templates/overcloud-prep-images.sh.j2#L73-L130 - #ironic node-update $ironic_node add properties/root_device='{"{{ node['key'] }}": "{{ node['value'] }}"}' - #fi -else - openstack overcloud node import --provide instackenv.json -fi - -openstack flavor set --property "cpu_arch"="x86_64" baremetal -openstack flavor set --property "cpu_arch"="x86_64" control -openstack flavor set --property "cpu_arch"="x86_64" compute -echo "Configuring nameserver on ctlplane network" -dns_server_ext='' -for dns_server in ${dns_servers}; do - dns_server_ext="\${dns_server_ext} --dns-nameserver \${dns_server}" -done -openstack subnet set ctlplane-subnet \${dns_server_ext} -sed -i '/CloudDomain:/c\ CloudDomain: '${domain_name} ${ENV_FILE} -echo "Executing overcloud deployment, this could run for an extended period without output." -sleep 60 #wait for Hypervisor stats to check-in to nova -# save deploy command so it can be used for debugging -cat > deploy_command << EOF -openstack overcloud deploy --templates $DEPLOY_OPTIONS --timeout 90 -EOF -EOI - - if [ "$interactive" == "TRUE" ]; then - if ! prompt_user "Overcloud Deployment"; then - echo -e "${blue}INFO: User requests exit${reset}" - exit 0 - fi - fi - - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" </dev/null; then - $(typeset -f debug_stack) - debug_stack - exit 1 -fi -EOI - - # Configure DPDK and restart ovs agent after bringing up br-phy - if [ "${deploy_options_array['dataplane']}" == 'ovs_dpdk' ]; then - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" < instackenv.json << EOF -$instackenv_output -EOF -EOI - if output=$(python3 -B $LIB/python/apex_python_utils.py parse-inventory -f $INVENTORY_FILE $inv_virt $inv_ha --export-bash); then - echo -e "${blue}${output}${reset}" - eval "$output" - else - echo -e "${red}ERROR: Failed to parse inventory bash settings file ${INVENTORY_FILE}${reset}" - exit 1 - fi - -} diff --git a/lib/post-install-functions.sh b/lib/post-install-functions.sh deleted file mode 100755 index 7678b0d3..00000000 --- a/lib/post-install-functions.sh +++ /dev/null @@ -1,281 +0,0 @@ -#!/usr/bin/env bash -############################################################################## -# Copyright (c) 2015 Tim Rozet (Red Hat), Dan Radez (Red Hat) and others. -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## - -##Post configuration after install -##params: none -function configure_post_install { - local opnfv_attach_networks ovs_ip ip_range net_cidr tmp_ip af external_network_ipv6 - external_network_ipv6=False - opnfv_attach_networks="admin" - if [[ $enabled_network_list =~ "external" ]]; then - opnfv_attach_networks+=' external' - fi - - echo -e "${blue}INFO: Post Install Configuration Running...${reset}" - - echo -e "${blue}INFO: Configuring ssh for root to overcloud nodes...${reset}" - # copy host key to instack - scp ${SSH_OPTIONS[@]} /root/.ssh/id_rsa.pub "stack@$UNDERCLOUD":jumphost_id_rsa.pub - - # add host key to overcloud nodes authorized keys - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" << EOI -source stackrc -nodes=\$(nova list | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") -for node in \$nodes; do -cat ~/jumphost_id_rsa.pub | ssh -T ${SSH_OPTIONS[@]} "heat-admin@\$node" 'cat >> ~/.ssh/authorized_keys' -done -EOI - - echo -e "${blue}INFO: Checking if OVS bridges have IP addresses...${reset}" - for network in ${opnfv_attach_networks}; do - ovs_ip=$(find_ip ${NET_MAP[$network]}) - tmp_ip='' - if [ -n "$ovs_ip" ]; then - echo -e "${blue}INFO: OVS Bridge ${NET_MAP[$network]} has IP address ${ovs_ip}${reset}" - else - echo -e "${blue}INFO: OVS Bridge ${NET_MAP[$network]} missing IP, will configure${reset}" - # use last IP of allocation pool - eval "ip_range=\${${network}_overcloud_ip_range}" - ovs_ip=${ip_range##*,} - eval "net_cidr=\${${network}_cidr}" - if [[ $ovs_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - af=4 - else - af=6 - if [ "$network" == "external" ]; then - ublic_network_ipv6=True - fi - #enable ipv6 on bridge interface - echo 0 > /proc/sys/net/ipv6/conf/${NET_MAP[$network]}/disable_ipv6 - fi - sudo ip addr add ${ovs_ip}/${net_cidr##*/} dev ${NET_MAP[$network]} - sudo ip link set up ${NET_MAP[$network]} - tmp_ip=$(find_ip ${NET_MAP[$network]} $af) - if [ -n "$tmp_ip" ]; then - echo -e "${blue}INFO: OVS Bridge ${NET_MAP[$network]} IP set: ${tmp_ip}${reset}" - continue - else - echo -e "${red}ERROR: Unable to set OVS Bridge ${NET_MAP[$network]} with IP: ${ovs_ip}${reset}" - return 1 - fi - fi - done - - if [ "${deploy_options_array['dataplane']}" == 'ovs_dpdk' ]; then - echo -e "${blue}INFO: Bringing up br-phy and ovs-agent for dpdk compute nodes...${reset}" - compute_nodes=$(undercloud_connect stack "source stackrc; nova list | grep compute | wc -l") - i=0 - while [ "$i" -lt "$compute_nodes" ]; do - overcloud_connect compute${i} "sudo ifup br-phy; sudo systemctl restart neutron-openvswitch-agent" - i=$((i + 1)) - done - fi - - # TODO fix this when HA SDN controllers are supported - if [ "${deploy_options_array['sdn_controller']}" != 'False' ]; then - echo -e "${blue}INFO: Finding SDN Controller IP for overcloudrc...${reset}" - sdn_controller_ip=$(undercloud_connect stack "source stackrc;nova list | grep controller-0 | cut -d '|' -f 7 | grep -Eo [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") - echo -e "${blue}INFO: SDN Controller IP is ${sdn_controller_ip} ${reset}" - undercloud_connect stack "echo 'export SDN_CONTROLLER_IP=${sdn_controller_ip}' >> /home/stack/overcloudrc" - fi - - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <> ./overcloudrc -fi -if ! grep -q "OS_TENANT_NAME" ./overcloudrc;then - echo "export OS_TENANT_NAME=admin" >> ./overcloudrc -fi - -if [ "${deploy_options_array['dataplane']}" == 'fdio' ] || [ "${deploy_options_array['dataplane']}" == 'ovs_dpdk' ]; then - for flavor in \$(openstack flavor list -c Name -f value); do - echo "INFO: Configuring \$flavor to use hugepage" - nova flavor-key \$flavor set hw:mem_page_size=large - done -fi - -if [ "${deploy_options_array['congress']}" == 'True' ]; then - ds_configs="--config username=\$OS_USERNAME - --config tenant_name=\$OS_PROJECT_NAME - --config password=\$OS_PASSWORD - --config auth_url=\$OS_AUTH_URL" - for s in nova neutronv2 cinder glancev2 keystone; do - ds_extra_configs="" - if [ "\$s" == "nova" ]; then - # nova's latest version is 2.38 but congress relies on nova to do - # floating ip operation instead of neutron. fip support in nova - # was depricated as of 2.35. Hard coding 2.34 for danube. - # Carlos.Goncalves working on fixes for upstream congress that - # should be ready for ocata. - nova_micro_version="2.34" - #nova_micro_version=\$(nova version-list | grep CURRENT | awk '{print \$10}') - ds_extra_configs+="--config api_version=\$nova_micro_version" - fi - if openstack congress datasource create \$s "\$s" \$ds_configs \$ds_extra_configs; then - echo "INFO: Datasource: \$s created" - else - echo "WARN: Datasource: \$s could NOT be created" - fi - done - if openstack congress datasource create doctor "doctor"; then - echo "INFO: Datasource: doctor created" - else - echo "WARN: Datsource: doctor could NOT be created" - fi -fi - - -EOI - - # we need to restart neutron-server in Gluon deployments to allow the Gluon core - # plugin to correctly register itself with Neutron - if [ "${deploy_options_array['gluon']}" == 'True' ]; then - echo "Restarting neutron-server to finalize Gluon installation" - overcloud_connect "controller0" "sudo systemctl restart neutron-server" - fi - - # for virtual, we NAT external network through Undercloud - # same goes for baremetal if only jumphost has external connectivity - if [ "$virtual" == "TRUE" ] || ! test_overcloud_connectivity && [ "$external_network_ipv6" != "True" ]; then - if [[ "$enabled_network_list" =~ "external" ]]; then - nat_cidr=${external_cidr} - else - nat_cidr=${admin_cidr} - fi - if ! configure_undercloud_nat ${nat_cidr}; then - echo -e "${red}ERROR: Unable to NAT undercloud with external net: ${nat_cidr}${reset}" - exit 1 - else - echo -e "${blue}INFO: Undercloud VM has been setup to NAT Overcloud external network${reset}" - fi - fi - - # for sfc deployments we need the vxlan workaround - if [ "${deploy_options_array['sfc']}" == 'True' ]; then - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" <&1 > /var/log/vsperf.log'" - fi - - # install docker - if [ "${deploy_options_array['yardstick']}" == 'True' ] || [ "${deploy_options_array['dovetail']}" == 'True' ]; then - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" < /dev/null || echo 'WARNING: zrpcd is not running on controller0'" -fi -} diff --git a/lib/python/apex/common/utils.py b/lib/python/apex/common/utils.py deleted file mode 100644 index 8e6896fa..00000000 --- a/lib/python/apex/common/utils.py +++ /dev/null @@ -1,31 +0,0 @@ -############################################################################## -# Copyright (c) 2016 Tim Rozet (trozet@redhat.com) and others. -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## - -import yaml - - -def str2bool(var): - if isinstance(var, bool): - return var - else: - return var.lower() in ("true", "yes") - - -def parse_yaml(yaml_file): - with open(yaml_file) as f: - parsed_dict = yaml.safe_load(f) - return parsed_dict - - -def write_str(bash_str, path=None): - if path: - with open(path, 'w') as file: - file.write(bash_str) - else: - print(bash_str) diff --git a/lib/python/apex_python_utils.py b/lib/python/apex_python_utils.py deleted file mode 100755 index 70fc592d..00000000 --- a/lib/python/apex_python_utils.py +++ /dev/null @@ -1,265 +0,0 @@ -############################################################################## -# Copyright (c) 2016 Feng Pan (fpan@redhat.com), Dan Radez (dradez@redhat.com) -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## - -import apex -import argparse -import sys -import logging -import os -import yaml - -from jinja2 import Environment -from jinja2 import FileSystemLoader - -from apex import NetworkSettings -from apex import NetworkEnvironment -from apex import DeploySettings -from apex import Inventory -from apex import ip_utils - - -def parse_net_settings(args): - """ - Parse OPNFV Apex network_settings.yaml config file - and dump bash syntax to set environment variables - - Args: - - file: string - file to network_settings.yaml file - """ - settings = NetworkSettings(args.net_settings_file) - net_env = NetworkEnvironment(settings, args.net_env_file, - args.compute_pre_config, - args.controller_pre_config) - target = args.target_dir.split('/') - target.append('network-environment.yaml') - dump_yaml(dict(net_env), '/'.join(target)) - settings.dump_bash() - - -def dump_yaml(data, file): - """ - Dumps data to a file as yaml - :param data: yaml to be written to file - :param file: filename to write to - :return: - """ - with open(file, "w") as fh: - yaml.dump(data, fh, default_flow_style=False) - - -def parse_deploy_settings(args): - settings = DeploySettings(args.file) - settings.dump_bash() - - -def run_clean(args): - apex.clean_nodes(args.file) - - -def parse_inventory(args): - inventory = Inventory(args.file, ha=args.ha, virtual=args.virtual) - if args.export_bash is True: - inventory.dump_bash() - else: - inventory.dump_instackenv_json() - - -def find_ip(args): - """ - Get and print the IP from a specific interface - - Args: - - interface: string - network interface name - - address_family: int - 4 or 6, respective to ipv4 or ipv6 - """ - interface = ip_utils.get_interface(args.interface, - args.address_family) - if interface: - print(interface.ip) - - -def build_nic_template(args): - """ - Build and print a Triple-O nic template from jinja template - - Args: - - template: string - path to jinja template to load - - enabled_networks: comma delimited list - list of networks defined in net_env.py - - ext_net_type: string - interface or br-ex, defines the external network configuration - - address_family: string - 4 or 6, respective to ipv4 or ipv6 - - ovs_dpdk_bridge: string - bridge name to use as ovs_dpdk - """ - template_dir, template = args.template.rsplit('/', 1) - - netsets = NetworkSettings(args.net_settings_file) - nets = netsets.get('networks') - ds = DeploySettings(args.deploy_settings_file).get('deploy_options') - env = Environment(loader=FileSystemLoader(template_dir), autoescape=True) - template = env.get_template(template) - - if ds['dataplane'] == 'fdio': - nets['tenant']['nic_mapping'][args.role]['phys_type'] = 'vpp_interface' - if ds['sdn_controller'] == 'opendaylight': - nets['external'][0]['nic_mapping'][args.role]['phys_type'] =\ - 'vpp_interface' - if ds.get('odl_vpp_routing_node') == 'dvr': - nets['admin']['nic_mapping'][args.role]['phys_type'] =\ - 'linux_bridge' - if ds.get('performance', {}).get(args.role.title(), {}).get('vpp', {})\ - .get('uio-driver'): - nets['tenant']['nic_mapping'][args.role]['uio-driver'] =\ - ds['performance'][args.role.title()]['vpp']['uio-driver'] - if ds['sdn_controller'] == 'opendaylight': - nets['external'][0]['nic_mapping'][args.role]['uio-driver'] =\ - ds['performance'][args.role.title()]['vpp']['uio-driver'] - if ds.get('performance', {}).get(args.role.title(), {}).get('vpp', {})\ - .get('interface-options'): - nets['tenant']['nic_mapping'][args.role]['interface-options'] =\ - ds['performance'][args.role.title()]['vpp']['interface-options'] - - print(template.render(nets=nets, - role=args.role, - external_net_af=netsets.get_ip_addr_family(), - external_net_type=args.ext_net_type, - ovs_dpdk_bridge=args.ovs_dpdk_bridge)) - - -def get_parser(): - parser = argparse.ArgumentParser() - parser.add_argument('--debug', action='store_true', default=False, - help="Turn on debug messages") - parser.add_argument('-l', '--log-file', default='/var/log/apex/apex.log', - dest='log_file', help="Log file to log to") - subparsers = parser.add_subparsers() - # parse-net-settings - net_settings = subparsers.add_parser('parse-net-settings', - help='Parse network settings file') - net_settings.add_argument('-s', '--net-settings-file', - default='network-settings.yaml', - dest='net_settings_file', - help='path to network settings file') - net_settings.add_argument('-e', '--net-env-file', - default="network-environment.yaml", - dest='net_env_file', - help='path to network environment file') - net_settings.add_argument('-td', '--target-dir', - default="/tmp", - dest='target_dir', - help='directory to write the' - 'network-environment.yaml file') - net_settings.add_argument('--compute-pre-config', - default=False, - action='store_true', - dest='compute_pre_config', - help='Boolean to enable Compute Pre Config') - net_settings.add_argument('--controller-pre-config', - action='store_true', - default=False, - dest='controller_pre_config', - help='Boolean to enable Controller Pre Config') - - net_settings.set_defaults(func=parse_net_settings) - # find-ip - get_int_ip = subparsers.add_parser('find-ip', - help='Find interface ip') - get_int_ip.add_argument('-i', '--interface', required=True, - help='Interface name') - get_int_ip.add_argument('-af', '--address-family', default=4, type=int, - choices=[4, 6], dest='address_family', - help='IP Address family') - get_int_ip.set_defaults(func=find_ip) - # nic-template - nic_template = subparsers.add_parser('nic-template', - help='Build NIC templates') - nic_template.add_argument('-r', '--role', required=True, - choices=['controller', 'compute'], - help='Role template generated for') - nic_template.add_argument('-t', '--template', required=True, - dest='template', - help='Template file to process') - nic_template.add_argument('-s', '--net-settings-file', - default='network-settings.yaml', - dest='net_settings_file', - help='path to network settings file') - nic_template.add_argument('-e', '--ext-net-type', default='interface', - dest='ext_net_type', - choices=['interface', 'vpp_interface', 'br-ex'], - help='External network type') - nic_template.add_argument('-d', '--ovs-dpdk-bridge', - default=None, dest='ovs_dpdk_bridge', - help='OVS DPDK Bridge Name') - nic_template.add_argument('--deploy-settings-file', - help='path to deploy settings file') - - nic_template.set_defaults(func=build_nic_template) - # parse-deploy-settings - deploy_settings = subparsers.add_parser('parse-deploy-settings', - help='Parse deploy settings file') - deploy_settings.add_argument('-f', '--file', - default='deploy_settings.yaml', - help='path to deploy settings file') - deploy_settings.set_defaults(func=parse_deploy_settings) - # parse-inventory - inventory = subparsers.add_parser('parse-inventory', - help='Parse inventory file') - inventory.add_argument('-f', '--file', - default='deploy_settings.yaml', - help='path to deploy settings file') - inventory.add_argument('--ha', - default=False, - action='store_true', - help='Indicate if deployment is HA or not') - inventory.add_argument('--virtual', - default=False, - action='store_true', - help='Indicate if deployment inventory is virtual') - inventory.add_argument('--export-bash', - default=False, - dest='export_bash', - action='store_true', - help='Export bash variables from inventory') - inventory.set_defaults(func=parse_inventory) - - clean = subparsers.add_parser('clean', - help='Parse deploy settings file') - clean.add_argument('-f', '--file', - help='path to inventory file') - clean.set_defaults(func=run_clean) - - return parser - - -def main(): - parser = get_parser() - args = parser.parse_args(sys.argv[1:]) - if args.debug: - logging.basicConfig(level=logging.DEBUG) - else: - apex_log_filename = args.log_file - os.makedirs(os.path.dirname(apex_log_filename), exist_ok=True) - logging.basicConfig(filename=apex_log_filename, - format='%(asctime)s %(levelname)s: %(message)s', - datefmt='%m/%d/%Y %I:%M:%S %p', - level=logging.DEBUG) - if hasattr(args, 'func'): - args.func(args) - else: - parser.print_help() - exit(1) - -if __name__ == "__main__": - main() diff --git a/lib/undercloud-functions.sh b/lib/undercloud-functions.sh deleted file mode 100755 index 08e1b7cf..00000000 --- a/lib/undercloud-functions.sh +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/env bash -############################################################################## -# Copyright (c) 2015 Tim Rozet (Red Hat), Dan Radez (Red Hat) and others. -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## - -##verify vm exists, an has a dhcp lease assigned to it -##params: none -function setup_undercloud_vm { - local libvirt_imgs=/var/lib/libvirt/images - if ! virsh list --all | grep undercloud > /dev/null; then - undercloud_nets="default admin" - if [[ $enabled_network_list =~ "external" ]]; then - undercloud_nets+=" external" - fi - define_vm undercloud hd 30 "$undercloud_nets" 4 12288 - - ### this doesn't work for some reason I was getting hangup events so using cp instead - #virsh vol-upload --pool default --vol undercloud.qcow2 --file $BASE/stack/undercloud.qcow2 - #2015-12-05 12:57:20.569+0000: 8755: info : libvirt version: 1.2.8, package: 16.el7_1.5 (CentOS BuildSystem , 2015-11-03-13:56:46, worker1.bsys.centos.org) - #2015-12-05 12:57:20.569+0000: 8755: warning : virKeepAliveTimerInternal:143 : No response from client 0x7ff1e231e630 after 6 keepalive messages in 35 seconds - #2015-12-05 12:57:20.569+0000: 8756: warning : virKeepAliveTimerInternal:143 : No response from client 0x7ff1e231e630 after 6 keepalive messages in 35 seconds - #error: cannot close volume undercloud.qcow2 - #error: internal error: received hangup / error event on socket - #error: Reconnected to the hypervisor - - cp -f $IMAGES/undercloud.qcow2 $libvirt_imgs/undercloud.qcow2 - cp -f $IMAGES/overcloud-full.vmlinuz $libvirt_imgs/overcloud-full.vmlinuz - cp -f $IMAGES/overcloud-full.initrd $libvirt_imgs/overcloud-full.initrd - - # resize Undercloud machine - echo "Checking if Undercloud needs to be resized..." - undercloud_size=$(LIBGUESTFS_BACKEND=direct virt-filesystems --long -h --all -a $libvirt_imgs/undercloud.qcow2 |grep device | grep -Eo "[0-9\.]+G" | sed -n 's/\([0-9][0-9]*\).*/\1/p') - if [ "$undercloud_size" -lt 30 ]; then - qemu-img resize /var/lib/libvirt/images/undercloud.qcow2 +25G - LIBGUESTFS_BACKEND=direct virt-resize --expand /dev/sda1 $IMAGES/undercloud.qcow2 $libvirt_imgs/undercloud.qcow2 - LIBGUESTFS_BACKEND=direct virt-customize -a $libvirt_imgs/undercloud.qcow2 --run-command 'xfs_growfs -d /dev/sda1 || true' - new_size=$(LIBGUESTFS_BACKEND=direct virt-filesystems --long -h --all -a $libvirt_imgs/undercloud.qcow2 |grep filesystem | grep -Eo "[0-9\.]+G" | sed -n 's/\([0-9][0-9]*\).*/\1/p') - if [ "$new_size" -lt 30 ]; then - echo "Error resizing Undercloud machine, disk size is ${new_size}" - exit 1 - else - echo "Undercloud successfully resized" - fi - else - echo "Skipped Undercloud resize, upstream is large enough" - fi - - else - echo "Found existing Undercloud VM, exiting." - exit 1 - fi - - # if the VM is not running update the authkeys and start it - if ! virsh list | grep undercloud > /dev/null; then - if [ "$debug" == 'TRUE' ]; then - LIBGUESTFS_BACKEND=direct virt-customize -a $libvirt_imgs/undercloud.qcow2 --root-password password:opnfvapex - fi - - echo "Injecting ssh key to Undercloud VM" - LIBGUESTFS_BACKEND=direct virt-customize -a $libvirt_imgs/undercloud.qcow2 --run-command "mkdir -p /root/.ssh/" \ - --upload ~/.ssh/id_rsa.pub:/root/.ssh/authorized_keys \ - --run-command "chmod 600 /root/.ssh/authorized_keys && restorecon /root/.ssh/authorized_keys" \ - --run-command "cp /root/.ssh/authorized_keys /home/stack/.ssh/" \ - --run-command "chown stack:stack /home/stack/.ssh/authorized_keys && chmod 600 /home/stack/.ssh/authorized_keys" - virsh start undercloud - virsh autostart undercloud - fi - - sleep 10 # let undercloud get started up - - # get the undercloud VM IP - CNT=10 - echo -n "${blue}Waiting for Undercloud's dhcp address${reset}" - undercloud_mac=$(virsh domiflist undercloud | grep default | awk '{ print $5 }') - while ! $(arp -en | grep ${undercloud_mac} > /dev/null) && [ $CNT -gt 0 ]; do - echo -n "." - sleep 10 - CNT=$((CNT-1)) - done - UNDERCLOUD=$(arp -en | grep ${undercloud_mac} | awk {'print $1'}) - - if [ -z "$UNDERCLOUD" ]; then - echo "\n\nCan't get IP for Undercloud. Can Not Continue." - exit 1 - else - echo -e "${blue}\rUndercloud VM has IP $UNDERCLOUD${reset}" - fi - - CNT=10 - echo -en "${blue}\rValidating Undercloud VM connectivity${reset}" - while ! ping -c 1 $UNDERCLOUD > /dev/null && [ $CNT -gt 0 ]; do - echo -n "." - sleep 3 - CNT=$((CNT-1)) - done - if [ "$CNT" -eq 0 ]; then - echo "Failed to contact Undercloud. Can Not Continue" - exit 1 - fi - CNT=10 - while ! ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "echo ''" 2>&1> /dev/null && [ $CNT -gt 0 ]; do - echo -n "." - sleep 3 - CNT=$((CNT-1)) - done - if [ "$CNT" -eq 0 ]; then - echo "Failed to connect to Undercloud. Can Not Continue" - exit 1 - fi - - # extra space to overwrite the previous connectivity output - echo -e "${blue}\r ${reset}" - sleep 1 - - # ensure stack user on Undercloud machine has an ssh key - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" "if [ ! -e ~/.ssh/id_rsa.pub ]; then ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa; fi" - - # ssh key fix for stack user - ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "restorecon -r /home/stack" -} - -##Copy over the glance images and instackenv json file -##params: none -function configure_undercloud { - local controller_nic_template compute_nic_template - echo - echo "Copying configuration files to Undercloud" - echo -e "${blue}Network Environment set for Deployment: ${reset}" - cat $APEX_TMP_DIR/network-environment.yaml - scp ${SSH_OPTIONS[@]} $APEX_TMP_DIR/network-environment.yaml "stack@$UNDERCLOUD": - - # check for ODL L3/ONOS - if [ "${deploy_options_array['dataplane']}" == 'fdio' ]; then - ext_net_type=vpp_interface - else - ext_net_type=br-ex - fi - - if [ "${deploy_options_array['dataplane']}" == 'ovs_dpdk' ]; then - ovs_dpdk_bridge='br-phy' - else - ovs_dpdk_bridge='' - fi - - # for some reason putting IP on the bridge fails with pinging validation in OOO - if [ "${deploy_options_array['sfc']}" == 'True' ]; then - controller_external='interface' - else - controller_external='br-ex' - fi - - if ! controller_nic_template=$(python3 -B $LIB/python/apex_python_utils.py nic-template -r controller -s $NETSETS -t $BASE/nics-template.yaml.jinja2 -e $controller_external --deploy-settings-file $DEPLOY_SETTINGS_FILE); then - echo -e "${red}ERROR: Failed to generate controller NIC heat template ${reset}" - exit 1 - fi - - if ! compute_nic_template=$(python3 -B $LIB/python/apex_python_utils.py nic-template -r compute -s $NETSETS -t $BASE/nics-template.yaml.jinja2 -e $ext_net_type -d "$ovs_dpdk_bridge" --deploy-settings-file $DEPLOY_SETTINGS_FILE); then - echo -e "${red}ERROR: Failed to generate compute NIC heat template ${reset}" - exit 1 - fi - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" << EOI -mkdir nics/ -cat > nics/controller.yaml << EOF -$controller_nic_template -EOF -cat > nics/compute.yaml << EOF -$compute_nic_template -EOF -EOI - - # disable requiretty for sudo - ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" "sed -i 's/Defaults\s*requiretty//'" /etc/sudoers - - # configure undercloud on Undercloud VM - echo "Running undercloud installation and configuration." - echo "Logging undercloud installation to stack@undercloud:/home/stack/apex-undercloud-install.log" - ssh -T ${SSH_OPTIONS[@]} "stack@$UNDERCLOUD" << EOI -set -e -openstack-config --set undercloud.conf DEFAULT local_ip ${admin_installer_vm_ip}/${admin_cidr##*/} -openstack-config --set undercloud.conf DEFAULT network_gateway ${admin_installer_vm_ip} -openstack-config --set undercloud.conf DEFAULT network_cidr ${admin_cidr} -openstack-config --set undercloud.conf DEFAULT dhcp_start ${admin_dhcp_range%%,*} -openstack-config --set undercloud.conf DEFAULT dhcp_end ${admin_dhcp_range##*,} -openstack-config --set undercloud.conf DEFAULT inspection_iprange ${admin_introspection_range} -openstack-config --set undercloud.conf DEFAULT undercloud_debug false -openstack-config --set undercloud.conf DEFAULT undercloud_hostname "undercloud.${domain_name}" -openstack-config --set undercloud.conf DEFAULT enable_ui false -openstack-config --set undercloud.conf DEFAULT undercloud_update_packages false -sudo openstack-config --set /etc/ironic/ironic.conf disk_utils iscsi_verify_attempts 30 -sudo openstack-config --set /etc/ironic/ironic.conf disk_partitioner check_device_max_retries 40 - -if [[ -n "${deploy_options_array['ceph_device']}" ]]; then - sed -i '/ExtraConfig/a\\ ceph::profile::params::osds: {\\x27${deploy_options_array['ceph_device']}\\x27: {}}' ${ENV_FILE} -fi - -sudo sed -i '/CephClusterFSID:/c\\ CephClusterFSID: \\x27$(cat /proc/sys/kernel/random/uuid)\\x27' /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml -sudo sed -i '/CephMonKey:/c\\ CephMonKey: \\x27'"\$(ceph-authtool --gen-print-key)"'\\x27' /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml -sudo sed -i '/CephAdminKey:/c\\ CephAdminKey: \\x27'"\$(ceph-authtool --gen-print-key)"'\\x27' /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml - -if [ "\$(uname -i)" == 'aarch64' ]; then - -# These two fixes are done in the base OOO image build right now -# keeping them here to know that they are done and in case we need -# to take care of them in the future. -# # remove syslinux references for aarch64 -# sudo sh -xc 'cd /etc/puppet/modules/ironic/manifests && patch -p0 < puppet-ironic-manifests-pxe-pp-aarch64.patch' -# sudo sed -i '/syslinux-extlinux/d' /usr/share/instack-undercloud/puppet-stack-config/puppet-stack-config.pp -# -# # disable use_linkat in swift -# sudo sed -i 's/o_tmpfile_supported()/False/' /usr/lib/python2.7/site-packages/swift/obj/diskfile.py - - openstack-config --set undercloud.conf DEFAULT ipxe_enabled false - sudo sed -i '/ _link_ip_address_pxe_configs/a\\ _link_mac_pxe_configs(task)' /usr/lib/python2.7/site-packages/ironic/common/pxe_utils.py -fi - -openstack undercloud install &> apex-undercloud-install.log || { - # cat the undercloud install log incase it fails - echo "ERROR: openstack undercloud install has failed. Dumping Log:" - cat apex-undercloud-install.log - exit 1 -} - -if [ "\$(uname -i)" == 'aarch64' ]; then -sudo yum -y reinstall grub2-efi shim -sudo cp /boot/efi/EFI/centos/grubaa64.efi /tftpboot/grubaa64.efi -sudo mkdir -p /tftpboot/EFI/centos -sudo tee /tftpboot/EFI/centos/grub.cfg > /dev/null << EOF -set default=master -set timeout=5 -set hidden_timeout_quiet=false - -menuentry "master" { -configfile /tftpboot/\\\$net_default_ip.conf -} -EOF -sudo chmod 644 /tftpboot/EFI/centos/grub.cfg -sudo openstack-config --set /etc/ironic/ironic.conf pxe uefi_pxe_config_template \\\$pybasedir/drivers/modules/pxe_grub_config.template -sudo openstack-config --set /etc/ironic/ironic.conf pxe uefi_pxe_bootfile_name grubaa64.efi -sudo service openstack-ironic-conductor restart -sudo sed -i 's/linuxefi/linux/g' /usr/lib/python2.7/site-packages/ironic/drivers/modules/pxe_grub_config.template -sudo sed -i 's/initrdefi/initrd/g' /usr/lib/python2.7/site-packages/ironic/drivers/modules/pxe_grub_config.template -echo '' | sudo tee --append /tftpboot/map-file > /dev/null -echo 'r ^/EFI/centos/grub.cfg-(.*) /tftpboot/pxelinux.cfg/\\1' | sudo tee --append /tftpboot/map-file > /dev/null -sudo service xinetd restart -fi - -# Set nova domain name -sudo openstack-config --set /etc/nova/nova.conf DEFAULT dns_domain ${domain_name} -sudo openstack-config --set /etc/nova/nova.conf DEFAULT dhcp_domain ${domain_name} -sudo systemctl restart openstack-nova-conductor -sudo systemctl restart openstack-nova-compute -sudo systemctl restart openstack-nova-api -sudo systemctl restart openstack-nova-scheduler - -# Set neutron domain name -sudo openstack-config --set /etc/neutron/neutron.conf DEFAULT dns_domain ${domain_name} -sudo systemctl restart neutron-server -sudo systemctl restart neutron-dhcp-agent -EOI - -# configure external network -if [[ "$enabled_network_list" =~ "external" ]]; then - ssh -T ${SSH_OPTIONS[@]} "root@$UNDERCLOUD" << EOI -if [[ "$external_installer_vm_vlan" != "native" ]]; then - cat < /etc/sysconfig/network-scripts/ifcfg-vlan${external_installer_vm_vlan} -DEVICE=vlan${external_installer_vm_vlan} -ONBOOT=yes -DEVICETYPE=ovs -TYPE=OVSIntPort -BOOTPROTO=static -IPADDR=${external_installer_vm_ip} -PREFIX=${external_cidr##*/} -OVS_BRIDGE=br-ctlplane -OVS_OPTIONS="tag=${external_installer_vm_vlan}" -EOF - ifup vlan${external_installer_vm_vlan} -else - if ! ip a s eth2 | grep ${external_installer_vm_ip} > /dev/null; then - ip a a ${external_installer_vm_ip}/${external_cidr##*/} dev eth2 - ip link set up dev eth2 - fi -fi -EOI -fi - -} diff --git a/lib/utility-functions.sh b/lib/utility-functions.sh deleted file mode 100644 index c12619ae..00000000 --- a/lib/utility-functions.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env bash -# Utility Functions used by OPNFV Apex -# author: Tim Rozet (trozet@redhat.com) - -SSH_OPTIONS=(-o StrictHostKeyChecking=no -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null -o LogLevel=error) - -##connects to undercloud -##params: user to login with, command to execute on undercloud (optional) -function undercloud_connect { - local user=$1 - - if [ -z "$1" ]; then - echo "Missing required argument: user to login as to undercloud" - return 1 - fi - - if [ -z "$2" ]; then - ssh ${SSH_OPTIONS[@]} ${user}@$(get_undercloud_ip) - else - ssh ${SSH_OPTIONS[@]} -T ${user}@$(get_undercloud_ip) "$2" - fi -} - -##outputs the Undercloud's IP address -##params: none -function get_undercloud_ip { - echo $(arp -an | grep $(virsh domiflist undercloud | grep default |\ - awk '{print $5}') | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") -} - -##connects to overcloud nodes -##params: node to login to, command to execute on overcloud (optional) -function overcloud_connect { - local node - local node_output - local node_ip - - if [ -z "$1" ]; then - echo "Missing required argument: overcloud node to login to" - return 1 - elif ! echo "$1" | grep -E "(controller|compute)[0-9]+" > /dev/null; then - echo "Invalid argument: overcloud node to login to must be in the format: \ -controller or compute" - return 1 - fi - - node_output=$(undercloud_connect "stack" "source stackrc; nova list") - node=$(echo "$1" | sed -E 's/([a-zA-Z]+)([0-9]+)/\1-\2/') - - node_ip=$(echo "$node_output" | grep "$node" | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") - - if [ "$node_ip" == "" ]; then - echo -e "Unable to find IP for ${node} in \n${node_output}" - return 1 - fi - - if [ -z "$2" ]; then - ssh ${SSH_OPTIONS[@]} heat-admin@${node_ip} - else - ssh ${SSH_OPTIONS[@]} -T heat-admin@${node_ip} "$2" - fi -} - -##connects to opendaylight karaf console -##params: None -function opendaylight_connect { - local opendaylight_ip - opendaylight_ip=$(undercloud_connect "stack" "cat overcloudrc | grep SDN_CONTROLLER_IP | grep -Eo [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") - - if [ "$opendaylight_ip" == "" ]; then - echo -e "Unable to find IP for OpenDaylight in overcloudrc" - return 1 - else - echo -e "Connecting to ODL Karaf console. Default password is 'karaf'" - fi - - ssh -p 8101 ${SSH_OPTIONS[@]} karaf@${opendaylight_ip} -} - -##outputs heat stack deployment failures -##params: none -function debug_stack { - source ~/stackrc - openstack stack failures list overcloud --long -} diff --git a/lib/virtual-setup-functions.sh b/lib/virtual-setup-functions.sh deleted file mode 100755 index 5f9e6ba5..00000000 --- a/lib/virtual-setup-functions.sh +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash -############################################################################## -# Copyright (c) 2015 Tim Rozet (Red Hat), Dan Radez (Red Hat) and others. -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## - -##Create virtual nodes in virsh -##params: vcpus, ramsize -function setup_virtual_baremetal { - local vcpus ramsize held_ramsize - if [ -z "$1" ]; then - vcpus=4 - ramsize=8192 - elif [ -z "$2" ]; then - vcpus=$1 - ramsize=8192 - else - vcpus=$1 - ramsize=$(($2*1024)) - fi - #start by generating the opening yaml for the inventory-virt.yaml file - cat > $APEX_TMP_DIR/inventory-virt.yaml << EOF -nodes: -EOF - - # next create the virtual machines and add their definitions to the file - if [ "$ha_enabled" == "False" ]; then - controller_index=0 - else - controller_index=2 - # 3 controller + computes - # zero based so add 2 to compute count - if [ $VM_COMPUTES -lt 2 ]; then - VM_COMPUTES=2 - fi - fi - - # tmp var to hold ramsize in case modified during detection - held_ramsize=${ramsize} - for i in $(seq 0 $(($controller_index+$VM_COMPUTES))); do - ramsize=${held_ramsize} - if [ $i -gt $controller_index ]; then - capability="profile:compute" - if [ -n "$VM_COMPUTE_RAM" ]; then - ramsize=$((${VM_COMPUTE_RAM}*1024)) - fi - else - capability="profile:control" - if [[ "${deploy_options_array['sdn_controller']}" == 'opendaylight' && "$ramsize" -lt 12288 ]]; then - echo "WARN: RAM per controller too low. OpenDaylight specified in deployment requires at least 12GB" - echo "INFO: Increasing RAM per controller to 12GB" - ramsize=12288 - elif [[ "$ramsize" -lt 10240 ]]; then - echo "WARN: RAM per controller too low. Deployment requires at least 10GB" - echo "INFO: Increasing RAM per controller to 10GB" - ramsize=10240 - fi - fi - if ! virsh list --all | grep baremetal${i} > /dev/null; then - define_vm baremetal${i} network 41 'admin' $vcpus $ramsize - for n in tenant external storage api; do - if [[ $enabled_network_list =~ $n ]]; then - echo -n "$n " - virsh attach-interface --domain baremetal${i} --type network --source $n --model virtio --config - fi - done - else - echo "Found baremetal${i} VM, using existing VM" - fi - #virsh vol-list default | grep baremetal${i} 2>&1> /dev/null || virsh vol-create-as default baremetal${i}.qcow2 41G --format qcow2 - mac=$(virsh domiflist baremetal${i} | grep admin | awk '{ print $5 }') - - cat >> $APEX_TMP_DIR/inventory-virt.yaml << EOF - node${i}: - mac_address: "$mac" - ipmi_ip: 192.168.122.1 - ipmi_user: admin - ipmi_pass: "password" - pm_type: "pxe_ipmitool" - pm_port: "623$i" - cpu: $vcpus - memory: $ramsize - disk: 41 - arch: "$(uname -i)" - capabilities: "$capability" -EOF - vbmc add baremetal$i --port 623$i - if service firewalld status > /dev/null; then - firewall-cmd --permanent --zone=public --add-port=623$i/udp - fi - # TODO: add iptables check and commands too - vbmc start baremetal$i - done - if service firewalld status > /dev/null; then - firewall-cmd --reload - fi -} - -##Create virtual nodes in virsh -##params: name - String: libvirt name for VM -## bootdev - String: boot device for the VM -## disksize - Number: size of the disk in GB -## ovs_bridges: - List: list of ovs bridges -## vcpus - Number of VCPUs to use (defaults to 4) -## ramsize - Size of RAM for VM in MB (defaults to 8192) -function define_vm () { - local vcpus ramsize volume_path direct_boot kernel_args - - if [ -z "$5" ]; then - vcpus=4 - ramsize=8388608 - elif [ -z "$6" ]; then - vcpus=$5 - ramsize=8388608 - else - vcpus=$5 - ramsize=$(($6*1024)) - fi - - # Create the libvirt storage volume - if virsh vol-list default | grep ${1}.qcow2 2>&1> /dev/null; then - volume_path=$(virsh vol-path --pool default ${1}.qcow2 || echo "/var/lib/libvirt/images/${1}.qcow2") - echo "Volume ${1} exists. Deleting Existing Volume $volume_path" - virsh vol-dumpxml ${1}.qcow2 --pool default > /dev/null || echo '' #ok for this to fail - touch $volume_path - virsh vol-delete ${1}.qcow2 --pool default - fi - virsh vol-create-as default ${1}.qcow2 ${3}G --format qcow2 - volume_path=$(virsh vol-path --pool default ${1}.qcow2) - if [ ! -f $volume_path ]; then - echo "$volume_path Not created successfully... Aborting" - exit 1 - fi - - # undercloud need to be direct booted. - # the upstream image no longer includes the kernel and initrd - if [ "$1" == 'undercloud' ]; then - direct_boot='--direct-boot overcloud-full' - kernel_args='--kernel-arg console=ttyS0 --kernel-arg root=/dev/sda' - fi - - if [ "$(uname -i)" == 'aarch64' ]; then - diskbus='scsi' - else - diskbus='sata' - fi - - # create the VM - $LIB/configure-vm --name $1 \ - --bootdev $2 \ - --image "$volume_path" \ - --diskbus $diskbus \ - --arch $(uname -i) \ - --cpus $vcpus \ - --memory $ramsize \ - --libvirt-nic-driver virtio \ - $direct_boot \ - $kernel_args \ - --baremetal-interface $4 -} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..af2a106e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +pbr!=2.1.0,>=2.0.0 # Apache-2.0 + +libvirt-python +python-iptables +virtualbmc +PyYAML +cryptography +python-ipmi +PyYAML +Jinja2>=2.8 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..ee3105af --- /dev/null +++ b/setup.cfg @@ -0,0 +1,48 @@ +[metadata] +name = apex +summary = A ansible roles and tools for deploying OPNFV +description-file = + INFO +author = Apex Team +author-email = michapma@redhat.com trozet@redhat.com dradez@redhat.com +home-page = https://github.com/opnfv/apex +classifier = + License :: OSI Approved :: Apache Software License + Development Status :: 4 - Beta + Intended Audience :: Developers + Intended Audience :: System Administrators + Intended Audience :: Information Technology + Topic :: Utilities + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.5 + +[global] +setup-hooks = + pbr.hooks.setup_hook + +[entry_points] +console_scripts = + opnfv-deploy = apex.deploy:main + +[files] +packages = + apex +data_files = + share/opnfv-apex/ = + build/network-environment.yaml + build/opnfv-environment.yaml + build/nics-template.yaml.jinja2 + build/csit-environment.yaml + build/virtual-environment.yaml + build/baremetal-environment.yaml + build/domain.xml + share/opnfv-apex/ansible = lib/ansible/* + share/opnfv-apex/config = config/* + share/opnfv-apex/docs = docs/* + +[wheel] +universal = 1 + +[pbr] +skip_authors = True +skip_changelog = True diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..6a931a61 --- /dev/null +++ b/setup.py @@ -0,0 +1,19 @@ +# Copyright Red Hat, Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import setuptools + +setuptools.setup( + setup_requires=['pbr'], + pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 00000000..f22863c7 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,6 @@ +coverage>=4.0 # Apache-2.0 +mock>=2.0 # BSD +nose # LGPL +flake8<2.6.0,>=2.5.4 # MIT +pylint==1.4.5 # GPLv2 +sphinx!=1.3b1,<1.4,>=1.2.1 # BSD diff --git a/tests/test_apex_python_utils_py.py b/tests/test_apex_python_utils_py.py deleted file mode 100644 index 550042f5..00000000 --- a/tests/test_apex_python_utils_py.py +++ /dev/null @@ -1,91 +0,0 @@ -############################################################################## -# Copyright (c) 2016 Dan Radez (Red Hat) -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## - -import shutil -import sys -import tempfile - -from test_apex_ip_utils import get_default_gateway_linux -from apex_python_utils import main -from apex_python_utils import get_parser -from apex_python_utils import parse_net_settings -from apex_python_utils import parse_deploy_settings -from apex_python_utils import find_ip -from apex_python_utils import build_nic_template -from apex_python_utils import parse_inventory - -from nose.tools import assert_equal -from nose.tools import assert_raises - - -net_sets = '../config/network/network_settings.yaml' -net_env = '../build/network-environment.yaml' -deploy_sets = '../config/deploy/deploy_settings.yaml' -nic_template = '../build/nics-template.yaml.jinja2' -inventory = '../config/inventory/pod_example_settings.yaml' - - -class TestCommonUtils(object): - @classmethod - def setup_class(klass): - """This method is run once for each class before any tests are run""" - klass.parser = get_parser() - klass.iface_name = get_default_gateway_linux() - - @classmethod - def teardown_class(klass): - """This method is run once for each class _after_ all tests are run""" - - def setUp(self): - """This method is run once before _each_ test method is executed""" - - def teardown(self): - """This method is run once after _each_ test method is executed""" - - def test_main(self): - sys.argv = ['apex_python_utils', '-l', '/dev/null'] - assert_raises(SystemExit, main) - sys.argv = ['apex_python_utils', '--debug', '-l', '/dev/null'] - assert_raises(SystemExit, main) - sys.argv = ['apex_python_utils', '-l', '/dev/null', - 'parse-deploy-settings', - '-f', deploy_sets] - assert_equal(main(), None) - - def test_parse_net_settings(self): - tmp_dir = tempfile.mkdtemp() - args = self.parser.parse_args(['parse-net-settings', - '-s', net_sets, - '-td', tmp_dir, - '-e', net_env]) - assert_equal(parse_net_settings(args), None) - shutil.rmtree(tmp_dir, ignore_errors=True) - - def test_parse_deploy_settings(self): - args = self.parser.parse_args(['parse-deploy-settings', - '-f', deploy_sets]) - assert_equal(parse_deploy_settings(args), None) - - def test_find_ip(self): - args = self.parser.parse_args(['find-ip', - '-i', self.iface_name]) - assert_equal(find_ip(args), None) - - def test_build_nic_template(self): - args = self.parser.parse_args(['nic-template', - '-s', net_sets, - '-r', 'compute', - '-t', nic_template, - '--deploy-settings-file', deploy_sets]) - assert_equal(build_nic_template(args), None) - - def test_parse_inventory(self): - args = self.parser.parse_args(['parse-inventory', - '-f', inventory]) - assert_equal(parse_inventory(args), None) diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..87b6c035 --- /dev/null +++ b/tox.ini @@ -0,0 +1,26 @@ +[tox] +envlist = docs,pep8,pylint,py35 + +[testenv] +usedevelop = True +deps = + -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +commands = + coverage erase + nosetests-3.4 --with-xunit \ + --with-coverage \ + --cover-tests \ + --cover-package=apex \ + --cover-xml \ + --cover-min-percentage 90 \ + apex/tests + coverage report + +[testenv:pep8] +basepython = python3 +commands = flake8 --exclude .build,build --ignore=F401 + +[testenv:py35] +basepython = python3 + -- 2.16.6