diff options
Diffstat (limited to 'deploy')
53 files changed, 8278 insertions, 0 deletions
diff --git a/deploy/README b/deploy/README new file mode 100644 index 000000000..167078bf8 --- /dev/null +++ b/deploy/README @@ -0,0 +1,186 @@ + +======== PREREQUISITES ======== + +the following dependencies and python modules are required to be installed: + +- for Ubuntu: + +sudo apt-get install -y libvirt-bin qemu-kvm python-pip fuseiso mkisofs +sudo apt-get install -y python-dev libz-dev libxml2-dev libxslt-dev +sudo pip install pyyaml netaddr paramiko lxml scp pycrypto ecdsa + +During libvirt install the user is added to the libvirtd group, so you have to +logout then login back again + + +======== PREPARE and RUN the OPNFV Autodeployment ======== + + +--- Step.1 Prepare the DEA and DHA configuration files and the OPNFV ISO file + +Make sure that you are using the right DEA - Deployment Environment Adapter and +DHA - Deployment Hardware Adapter configuration files, the ones provided are only templates +you will have to modify them according to your needs + +- If wou wish to deploy OPNFV cloud environment on top of KVM/Libvirt + virtualization use as example the following configuration files: + + * SR1 configuration files + + => templates/virtual_environment/conf/ha + dea.yaml + dha.yaml + + + * ARNO configuration files + + => templates/virtual_environment/old_conf/ha + dea.yaml + dha.yaml + + => templates/virtual_environment/old_conf/multinode + dea.yaml + dha.yaml + + +- If you wish to deploy OPNFV cloud environment on hardware + use as example the following configuration files: + + * SR1 configuration files + + => templates/hardware_environment/conf/ericsson_montreal_lab/ha + dea.yaml + dha.yaml + + => templates/hardware_environment/conf/linux_foundation_lab/pod1/ha + dea.yaml + dha.yaml + + => templates/hardware_environment/conf/linux_foundation_lab/pod2/ha + dea.yaml + dha.yaml + + + * ARNO configuration files + + => templates/hardware_environment/old_conf/ericsson_montreal_lab/ha + dea.yaml + dha.yaml + + => templates/hardware_environment/old_conf/ericsson_montreal_lab/multinode + dea.yaml + dha.yaml + + => templates/hardware_environment/old_conf/linux_foundation_lab/ha + dea.yaml + dha.yaml + + => templates/hardware_environment/old_conf/linux_foundation_lab/multinode + dea.yaml + dha.yaml + + +--- Step.2 Run Autodeployment --- + +usage: python deploy.py [-h] [-nf] [-nh] [-fo] [-co] [-c] [-iso [ISO_FILE]] + [-dea [DEA_FILE]] [-dha [DHA_FILE]] [-s STORAGE_DIR] + [-b PXE_BRIDGE] [-p FUEL_PLUGINS_DIR] + +optional arguments: + -h, --help show this help message and exit + -nf Do not install Fuel Master (and Node VMs when using libvirt) + -nh Don't run health check after deployment + -fo Install Fuel Master only (and Node VMs when using libvirt) + -co Cleanup VMs and Virtual Networks according to what is + defined in DHA + -c Cleanup after deploy + -iso [ISO_FILE] ISO File [default: OPNFV.iso] + -dea [DEA_FILE] Deployment Environment Adapter: dea.yaml + -dha [DHA_FILE] Deployment Hardware Adapter: dha.yaml + -s STORAGE_DIR Storage Directory [default: images] + -b PXE_BRIDGE Linux Bridge for booting up the Fuel Master VM + [default: pxebr] + -p FUEL_PLUGINS_DIR Fuel Plugins directory + + +* EXAMPLES: + +- Install Fuel Master and deploy OPNFV Cloud from scratch on Hardware Environment: + + sudo python deploy.py -iso ~/ISO/opnfv.iso -dea ~/CONF/hardware/dea.yaml -dha ~/CONF/hardware/dha.yaml -s /mnt/images -b pxebr + + +- Install Fuel Master and deploy OPNFV Cloud from scratch on Virtual Environment: + + sudo python deploy.py -iso ~/ISO/opnfv.iso -dea ~/CONF/virtual/dea.yaml -dha ~/CONF/virtual/dha.yaml -s /mnt/images + + +- Deploy OPNFV Cloud on an already active Environment where Fuel Master VM is running so no need to install Fuel again: + + sudo python deploy.py -nf -dea ~/CONF/virtual/dea.yaml -dha ~/CONF/virtual/dha.yaml + + => with plugin installation + sudo python deploy.py -nf -dea ~/CONF/virtual/dea.yaml -dha ~/CONF/virtual/dha.yaml + + => with cleanup after deployment is finished + sudo python deploy.py -nf -dea ~/CONF/virtual/dea.yaml -dha ~/CONF/virtual/dha.yaml -c + + => no healthcheck after deployment is completed + sudo python deploy.py -nf -dea ~/CONF/virtual/dea.yaml -dha ~/CONF/virtual/dha.yaml -nh + + +- Install Fuel Master only (and Node VMs when using virtual environment): + + => for virtual environment: + sudo python deploy.py -iso ~/ISO/opnfv.iso -dea ~/CONF/virtual/dea.yaml -dha ~/CONF/virtual/dha.yaml -s /mnt/images + + => for hardware environment: + sudo python deploy.py -iso ~/ISO/opnfv.iso -dea ~/CONF/hardware/dea.yaml -dha ~/CONF/hardware/dha.yaml -s /mnt/images -b pxebr + + +- Cleanup a running OPNFV environment: + + sudo python deploy.py -co -dha ~/CONF/virtual/dha.yaml + + +* WARNINGS: + +=> If optional argument -s <storage_dir> is not specified, Autodeployment will use +"<current_working_dir>/images" as default, and it will create it, if it hasn't been created before + +=> If optional argument -b <pxe_bridge> is not specified, Autodeployment will use "pxebr" as default, +if the bridge does not exist, the application will terminate with an error message + +=> If argument -iso [ISO_FILE] is not specified, Autodeployment will use "<current_working_dir>/OPNFV.iso" +as default, if the iso file does not exist, the application will terminate with an error message + +=> If argument -dea [DEA_FILE] is not specified, Autodeployment will use "<current_working_dir>/dea.yaml" +as default, if DEA file does not exist, the application will terminate with an error message + +=> If argument -dha [DHA_FILE] is not specified, Autodeployment will use "<current_working_dir>/dha.yaml" +as default, if DHA file does not exist, the application will terminate with an error message + +=> Optional argument -b PXE_BRIDGE is not required for Autodeployment in virtual environment, + even if it is specified it will not be used at all because virtual environment is using a different virtual network setup + +=> If optional argument -p FUEL_PLUGINS_DIR is not specified, no external plugins will be installed in Fuel + + +--- Networking considerations --- + +For Virtual Environment: + +There are some NAT, IPTABLE conflicts on the edge of libvirt bridging and Fuel Master +according to http://wiki.libvirt.org/page/Networking +netfilter on the bridges should be disabled + +Add these lines to /etc/sysctl.conf + +cat >> /etc/sysctl.conf <<EOF +net.bridge.bridge-nf-call-ip6tables = 0 +net.bridge.bridge-nf-call-iptables = 0 +net.bridge.bridge-nf-call-arptables = 0 +EOF + +and then reload configuration: +sysctl -p /etc/sysctl.conf diff --git a/deploy/__init__.py b/deploy/__init__.py new file mode 100644 index 000000000..fb73157f9 --- /dev/null +++ b/deploy/__init__.py @@ -0,0 +1,8 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 +############################################################################### diff --git a/deploy/cloud/configure_environment.py b/deploy/cloud/configure_environment.py new file mode 100644 index 000000000..ed89fa600 --- /dev/null +++ b/deploy/cloud/configure_environment.py @@ -0,0 +1,76 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 +############################################################################### + + +from configure_settings import ConfigureSettings +from configure_network import ConfigureNetwork +from configure_nodes import ConfigureNodes + +from common import ( + E, + exec_cmd, + parse, + err, + log, + delete, + create_dir_if_not_exists, +) + + +class ConfigureEnvironment(object): + + def __init__(self, dea, yaml_config_dir, release_id, node_id_roles_dict): + self.env_id = None + self.dea = dea + self.yaml_config_dir = yaml_config_dir + self.release_id = release_id + self.node_id_roles_dict = node_id_roles_dict + self.required_networks = [] + + def env_exists(self, env_name): + env_list = parse(exec_cmd('fuel env --list')) + for env in env_list: + if env[E['name']] == env_name and env[E['status']] == 'new': + self.env_id = env[E['id']] + return True + return False + + def configure_environment(self): + log('Configure environment') + delete(self.yaml_config_dir) + create_dir_if_not_exists(self.yaml_config_dir) + env_name = self.dea.get_env_name() + env_mode = self.dea.get_env_mode() + env_net_segment_type = self.dea.get_env_net_segment_type() + log('Creating environment %s release %s, mode %s, network-mode neutron' + ', net-segment-type %s' + % (env_name, self.release_id, env_mode, env_net_segment_type)) + exec_cmd('fuel env create --name %s --release %s --mode %s ' + '--network-mode neutron --net-segment-type %s' + % (env_name, self.release_id, env_mode, env_net_segment_type)) + + if not self.env_exists(env_name): + err('Failed to create environment %s' % env_name) + self.config_settings() + self.config_network() + self.config_nodes() + + def config_settings(self): + settings = ConfigureSettings(self.yaml_config_dir, self.env_id, + self.dea) + settings.config_settings() + + def config_network(self): + network = ConfigureNetwork(self.yaml_config_dir, self.env_id, self.dea) + network.config_network() + + def config_nodes(self): + nodes = ConfigureNodes(self.yaml_config_dir, self.env_id, + self.node_id_roles_dict, self.dea) + nodes.config_nodes() diff --git a/deploy/cloud/configure_network.py b/deploy/cloud/configure_network.py new file mode 100644 index 000000000..b3ff9e9e0 --- /dev/null +++ b/deploy/cloud/configure_network.py @@ -0,0 +1,69 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 yaml +import io + +from common import ( + exec_cmd, + check_file_exists, + log, + backup, +) + + +class ConfigureNetwork(object): + + def __init__(self, yaml_config_dir, env_id, dea): + self.yaml_config_dir = yaml_config_dir + self.env_id = env_id + self.dea = dea + self.required_networks = [] + + def download_network_config(self): + log('Download network config for environment %s' % self.env_id) + exec_cmd('fuel network --env %s --download --dir %s' + % (self.env_id, self.yaml_config_dir)) + + def upload_network_config(self): + log('Upload network config for environment %s' % self.env_id) + exec_cmd('fuel network --env %s --upload --dir %s' + % (self.env_id, self.yaml_config_dir)) + + def config_network(self): + log('Configure network') + self.download_network_config() + self.modify_network_config() + self.upload_network_config() + + def modify_network_config(self): + log('Modify network config for environment %s' % self.env_id) + network_yaml = ('%s/network_%s.yaml' + % (self.yaml_config_dir, self.env_id)) + check_file_exists(network_yaml) + backup(network_yaml) + + network_config = self.dea.get_property('network') + + with io.open(network_yaml) as stream: + network = yaml.load(stream) + + net_names = self.dea.get_network_names() + net_id = {} + for net in network['networks']: + if net['name'] in net_names: + net_id[net['name']] = {'id': net['id'], + 'group_id': net['group_id']} + + for network in network_config['networks']: + network.update(net_id[network['name']]) + + with io.open(network_yaml, 'w') as stream: + yaml.dump(network_config, stream, default_flow_style=False) diff --git a/deploy/cloud/configure_nodes.py b/deploy/cloud/configure_nodes.py new file mode 100644 index 000000000..a456679ab --- /dev/null +++ b/deploy/cloud/configure_nodes.py @@ -0,0 +1,109 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 yaml +import io +import glob + +from common import ( + exec_cmd, + check_file_exists, + log, + backup, +) + + +class ConfigureNodes(object): + + def __init__(self, yaml_config_dir, env_id, node_id_roles_dict, dea): + self.yaml_config_dir = yaml_config_dir + self.env_id = env_id + self.node_id_roles_dict = node_id_roles_dict + self.dea = dea + + def config_nodes(self): + log('Configure nodes') + for node_id, roles_blade in self.node_id_roles_dict.iteritems(): + exec_cmd('fuel node set --node-id %s --role %s --env %s' + % (node_id, roles_blade[0], self.env_id)) + + self.download_deployment_config() + for node_id, roles_blade in self.node_id_roles_dict.iteritems(): + self.download_interface_config(node_id) + self.modify_node_interface(node_id, roles_blade) + self.modify_node_network_schemes(node_id, roles_blade) + self.upload_interface_config(node_id) + self.upload_deployment_config() + + def modify_node_network_schemes(self, node_id, roles_blade): + log('Modify network transformations for node %s' % node_id) + type = self.dea.get_node_property(roles_blade[1], 'transformations') + transformations = self.dea.get_property(type) + deployment_dir = '%s/deployment_%s' % ( + self.yaml_config_dir, self.env_id) + backup(deployment_dir) + for node_file in glob.glob(deployment_dir + '/*_%s.yaml' % node_id): + with io.open(node_file) as stream: + node = yaml.load(stream) + + node['network_scheme'].update(transformations) + + with io.open(node_file, 'w') as stream: + yaml.dump(node, stream, default_flow_style=False) + + def download_deployment_config(self): + log('Download deployment config for environment %s' % self.env_id) + exec_cmd('fuel deployment --env %s --default --dir %s' + % (self.env_id, self.yaml_config_dir)) + + def upload_deployment_config(self): + log('Upload deployment config for environment %s' % self.env_id) + exec_cmd('fuel deployment --env %s --upload --dir %s' + % (self.env_id, self.yaml_config_dir)) + + def download_interface_config(self, node_id): + log('Download interface config for node %s' % node_id) + exec_cmd('fuel node --env %s --node %s --network --download ' + '--dir %s' % (self.env_id, node_id, self.yaml_config_dir)) + + def upload_interface_config(self, node_id): + log('Upload interface config for node %s' % node_id) + exec_cmd('fuel node --env %s --node %s --network --upload ' + '--dir %s' % (self.env_id, node_id, self.yaml_config_dir)) + + def modify_node_interface(self, node_id, roles_blade): + log('Modify interface config for node %s' % node_id) + interface_yaml = ('%s/node_%s/interfaces.yaml' + % (self.yaml_config_dir, node_id)) + check_file_exists(interface_yaml) + backup('%s/node_%s' % (self.yaml_config_dir, node_id)) + + with io.open(interface_yaml) as stream: + interfaces = yaml.load(stream) + + net_name_id = {} + for interface in interfaces: + for network in interface['assigned_networks']: + net_name_id[network['name']] = network['id'] + + type = self.dea.get_node_property(roles_blade[1], 'interfaces') + interface_config = self.dea.get_property(type) + + for interface in interfaces: + interface['assigned_networks'] = [] + if interface['name'] in interface_config: + for net_name in interface_config[interface['name']]: + net = {} + net['id'] = net_name_id[net_name] + net['name'] = net_name + interface['assigned_networks'].append(net) + + with io.open(interface_yaml, 'w') as stream: + yaml.dump(interfaces, stream, default_flow_style=False) diff --git a/deploy/cloud/configure_settings.py b/deploy/cloud/configure_settings.py new file mode 100644 index 000000000..c773e1b61 --- /dev/null +++ b/deploy/cloud/configure_settings.py @@ -0,0 +1,55 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 yaml +import io + +from common import ( + exec_cmd, + check_file_exists, + log, + backup, +) + + +class ConfigureSettings(object): + + def __init__(self, yaml_config_dir, env_id, dea): + self.yaml_config_dir = yaml_config_dir + self.env_id = env_id + self.dea = dea + + def download_settings(self): + log('Download settings for environment %s' % self.env_id) + exec_cmd('fuel settings --env %s --download --dir %s' + % (self.env_id, self.yaml_config_dir)) + + def upload_settings(self): + log('Upload settings for environment %s' % self.env_id) + exec_cmd('fuel settings --env %s --upload --dir %s' + % (self.env_id, self.yaml_config_dir)) + + def config_settings(self): + log('Configure settings') + self.download_settings() + self.modify_settings() + self.upload_settings() + + def modify_settings(self): + log('Modify settings for environment %s' % self.env_id) + settings_yaml = ('%s/settings_%s.yaml' + % (self.yaml_config_dir, self.env_id)) + check_file_exists(settings_yaml) + backup(settings_yaml) + + settings = self.dea.get_property('settings') + + with io.open(settings_yaml, 'w') as stream: + yaml.dump(settings, stream, default_flow_style=False) diff --git a/deploy/cloud/deploy.py b/deploy/cloud/deploy.py new file mode 100644 index 000000000..14d6a9621 --- /dev/null +++ b/deploy/cloud/deploy.py @@ -0,0 +1,94 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 yaml +import io + +from dea import DeploymentEnvironmentAdapter +from configure_environment import ConfigureEnvironment +from deployment import Deployment + +from common import ( + R, + exec_cmd, + parse, + check_file_exists, + commafy, + ArgParser, +) + +YAML_CONF_DIR = '/var/lib/opnfv' + + +class Deploy(object): + + def __init__(self, dea_file, no_health_check): + self.dea = DeploymentEnvironmentAdapter(dea_file) + self.no_health_check = no_health_check + self.macs_per_blade = {} + self.blades = self.dea.get_node_ids() + self.blade_node_dict = self.dea.get_blade_node_map() + self.node_roles_dict = {} + self.env_id = None + self.wanted_release = self.dea.get_property('wanted_release') + + def assign_roles_to_cluster_node_ids(self): + self.node_roles_dict = {} + for blade, node in self.blade_node_dict.iteritems(): + roles = commafy(self.dea.get_node_role(blade)) + self.node_roles_dict[node] = (roles, blade) + + def configure_environment(self): + release_list = parse(exec_cmd('fuel release -l')) + for release in release_list: + if release[R['name']] == self.wanted_release: + break + config_env = ConfigureEnvironment(self.dea, YAML_CONF_DIR, + release[R['id']], + self.node_roles_dict) + config_env.configure_environment() + self.env_id = config_env.env_id + + def deploy_cloud(self): + dep = Deployment(self.dea, YAML_CONF_DIR, self.env_id, + self.node_roles_dict, self.no_health_check) + dep.deploy() + + def deploy(self): + + self.assign_roles_to_cluster_node_ids() + + self.configure_environment() + + self.deploy_cloud() + + +def parse_arguments(): + parser = ArgParser(prog='python %s' % __file__) + parser.add_argument('-nh', dest='no_health_check', action='store_true', + default=False, + help='Don\'t run health check after deployment') + parser.add_argument('dea_file', action='store', + help='Deployment Environment Adapter: dea.yaml') + args = parser.parse_args() + check_file_exists(args.dea_file) + + kwargs = {'dea_file': args.dea_file, + 'no_health_check': args.no_health_check} + return kwargs + + +def main(): + kwargs = parse_arguments() + deploy = Deploy(**kwargs) + deploy.deploy() + +if __name__ == '__main__': + main() diff --git a/deploy/cloud/deployment.py b/deploy/cloud/deployment.py new file mode 100644 index 000000000..12c1f8978 --- /dev/null +++ b/deploy/cloud/deployment.py @@ -0,0 +1,156 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 time +import re + +from common import ( + N, + E, + exec_cmd, + run_proc, + parse, + err, + log, + delete, +) + +SEARCH_TEXT = 'Puppet (err)' +LOG_FILE = '/var/log/puppet.log' +GREP_LINES_OF_LEADING_CONTEXT = 100 +GREP_LINES_OF_TRAILING_CONTEXT = 100 +LIST_OF_CHAR_TO_BE_ESCAPED = ['[', ']', '"'] + +class Deployment(object): + + def __init__(self, dea, yaml_config_dir, env_id, node_id_roles_dict, + no_health_check): + self.dea = dea + self.yaml_config_dir = yaml_config_dir + self.env_id = env_id + self.node_id_roles_dict = node_id_roles_dict + self.no_health_check = no_health_check + self.pattern = re.compile( + '\d\d\d\d-\d\d-\d\d\s\d\d:\d\d:\d\d') + + def collect_error_logs(self): + for node_id, roles_blade in self.node_id_roles_dict.iteritems(): + log_list = [] + cmd = ('ssh -q node-%s grep \'"%s"\' %s' + % (node_id, SEARCH_TEXT, LOG_FILE)) + results, _ = exec_cmd(cmd, False) + for result in results.splitlines(): + log_msg = '' + + sub_cmd = '"%s" %s' % (result, LOG_FILE) + for c in LIST_OF_CHAR_TO_BE_ESCAPED: + sub_cmd = sub_cmd.replace(c, '\%s' % c) + grep_cmd = ('grep -B%s %s' + % (GREP_LINES_OF_LEADING_CONTEXT, sub_cmd)) + cmd = ('ssh -q node-%s "%s"' % (node_id, grep_cmd)) + + details, _ = exec_cmd(cmd, False) + details_list = details.splitlines() + + found_prev_log = False + for i in range(len(details_list) - 2, -1, -1): + if self.pattern.match(details_list[i]): + found_prev_log = True + break + if found_prev_log: + log_msg += '\n'.join(details_list[i:-1]) + '\n' + + grep_cmd = ('grep -A%s %s' + % (GREP_LINES_OF_TRAILING_CONTEXT, sub_cmd)) + cmd = ('ssh -q node-%s "%s"' % (node_id, grep_cmd)) + + details, _ = exec_cmd(cmd, False) + details_list = details.splitlines() + + found_next_log = False + for i in range(1, len(details_list)): + if self.pattern.match(details_list[i]): + found_next_log = True + break + if found_next_log: + log_msg += '\n'.join(details_list[:i]) + else: + log_msg += details + + if log_msg: + log_list.append(log_msg) + + if log_list: + role = ('controller' if 'controller' in roles_blade[0] + else 'compute host') + log('_' * 40 + 'Errors in node-%s %s' % (node_id, role) + + '_' * 40) + for log_msg in log_list: + print(log_msg + '\n') + + def run_deploy(self): + WAIT_LOOP = 180 + SLEEP_TIME = 60 + LOG_FILE = 'cloud.log' + + log('Starting deployment of environment %s' % self.env_id) + run_proc('fuel --env %s deploy-changes | strings | tee %s' + % (self.env_id, LOG_FILE)) + + ready = False + for i in range(WAIT_LOOP): + env = parse(exec_cmd('fuel env --env %s' % self.env_id)) + log('Environment status: %s' % env[0][E['status']]) + r, _ = exec_cmd('tail -2 %s | head -1' % LOG_FILE, False) + if r: + log(r) + if env[0][E['status']] == 'operational': + ready = True + break + elif (env[0][E['status']] == 'error' + or env[0][E['status']] == 'stopped'): + break + else: + time.sleep(SLEEP_TIME) + delete(LOG_FILE) + + if ready: + log('Environment %s successfully deployed' % self.env_id) + else: + self.collect_error_logs() + err('Deployment failed, environment %s is not operational' + % self.env_id) + + def verify_node_status(self): + node_list = parse(exec_cmd('fuel node list')) + failed_nodes = [] + for node in node_list: + if node[N['status']] != 'ready': + failed_nodes.append((node[N['id']], node[N['status']])) + + if failed_nodes: + summary = '' + for node, status in failed_nodes: + summary += '[node %s, status %s]\n' % (node, status) + err('Deployment failed: %s' % summary) + + def health_check(self): + log('Now running sanity and smoke health checks') + r = exec_cmd('fuel health --env %s --check sanity,smoke --force' + % self.env_id) + log(r) + if 'failure' in r: + err('Healthcheck failed!') + + def deploy(self): + self.run_deploy() + self.verify_node_status() + if not self.no_health_check: + self.health_check() diff --git a/deploy/common.py b/deploy/common.py new file mode 100644 index 000000000..17fd2174a --- /dev/null +++ b/deploy/common.py @@ -0,0 +1,157 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 subprocess +import sys +import os +import logging +import argparse +import shutil +import stat +import errno + +N = {'id': 0, 'status': 1, 'name': 2, 'cluster': 3, 'ip': 4, 'mac': 5, + 'roles': 6, 'pending_roles': 7, 'online': 8} +E = {'id': 0, 'status': 1, 'name': 2, 'mode': 3, 'release_id': 4, + 'changes': 5, 'pending_release_id': 6} +R = {'id': 0, 'name': 1, 'state': 2, 'operating_system': 3, 'version': 4} +RO = {'name': 0, 'conflicts': 1} +CWD = os.getcwd() +LOG = logging.getLogger(__name__) +LOG.setLevel(logging.DEBUG) +formatter = logging.Formatter('%(message)s') +out_handler = logging.StreamHandler(sys.stdout) +out_handler.setFormatter(formatter) +LOG.addHandler(out_handler) +LOGFILE = 'autodeploy.log' +if os.path.isfile(LOGFILE): + os.remove(LOGFILE) +out_handler = logging.FileHandler(LOGFILE, mode='w') +out_handler.setFormatter(formatter) +LOG.addHandler(out_handler) +os.chmod(LOGFILE, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + +def exec_cmd(cmd, check=True): + process = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=True) + response = process.communicate()[0].strip() + return_code = process.returncode + if check: + if return_code > 0: + raise Exception(response) + else: + return response + return response, return_code + + +def run_proc(cmd): + process = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=True) + return process + + +def parse(printout): + parsed_list = [] + lines = printout.splitlines() + for l in lines[2:]: + parsed = [e.strip() for e in l.split('|')] + parsed_list.append(parsed) + return parsed_list + + +def clean(lines): + parsed_list = [] + parsed = [] + for l in lines.strip().splitlines(): + parsed = [] + cluttered = [e.strip() for e in l.split(' ')] + for p in cluttered: + if p: + parsed.append(p) + parsed_list.append(parsed) + return parsed if len(parsed_list) == 1 else parsed_list + + +def err(message): + LOG.error('%s\n' % message) + sys.exit(1) + + +def warn(message): + LOG.warning('%s\n' % message) + + +def check_file_exists(file_path): + if not os.path.dirname(file_path): + file_path = '%s/%s' % (CWD, file_path) + if not os.path.isfile(file_path): + err('ERROR: File %s not found\n' % file_path) + + +def check_dir_exists(dir_path): + if not os.path.dirname(dir_path): + dir_path = '%s/%s' % (CWD, dir_path) + if not os.path.isdir(dir_path): + err('ERROR: Directory %s not found\n' % dir_path) + + +def create_dir_if_not_exists(dir_path): + if not os.path.isdir(dir_path): + log('Creating directory %s' % dir_path) + os.makedirs(dir_path) + + +def delete(f): + if os.path.isfile(f): + log('Deleting file %s' % f) + os.remove(f) + elif os.path.isdir(f): + log('Deleting directory %s' % f) + shutil.rmtree(f) + + +def commafy(comma_separated_list): + l = [c.strip() for c in comma_separated_list.split(',')] + return ','.join(l) + + +def check_if_root(): + r = exec_cmd('whoami') + if r != 'root': + err('You need be root to run this application') + + +def log(message): + LOG.debug('%s\n' % message) + + +class ArgParser(argparse.ArgumentParser): + + def error(self, message): + sys.stderr.write('ERROR: %s\n' % message) + self.print_help() + sys.exit(2) + + +def backup(path): + src = path + dst = path + '_orig' + delete(dst) + try: + shutil.copytree(src, dst) + except OSError as e: + if e.errno == errno.ENOTDIR: + shutil.copy(src, dst) + else: + raise diff --git a/deploy/dea.py b/deploy/dea.py new file mode 100644 index 000000000..2d2b28ae8 --- /dev/null +++ b/deploy/dea.py @@ -0,0 +1,102 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 yaml +import io +import netaddr + + +class DeploymentEnvironmentAdapter(object): + + def __init__(self, yaml_path): + self.dea_struct = None + self.parse_yaml(yaml_path) + self.network_names = [] + self.collect_network_names() + + def modify_ip(self, ip_addr, index, val): + ip_str = str(netaddr.IPAddress(ip_addr)) + decimal_list = map(int, ip_str.split('.')) + decimal_list[index] = val + return '.'.join(map(str, decimal_list)) + + def parse_yaml(self, yaml_path): + with io.open(yaml_path) as yaml_file: + self.dea_struct = yaml.load(yaml_file) + + def get_env_name(self): + return self.get_property('environment')['name'] + + def get_env_mode(self): + return self.get_property('environment')['mode'] + + def get_env_net_segment_type(self): + return self.get_property('environment')['net_segment_type'] + + def get_fuel_config(self): + return self.dea_struct['fuel'] + + def get_fuel_ip(self): + fuel_conf = self.get_fuel_config() + return fuel_conf['ADMIN_NETWORK']['ipaddress'] + + def get_fuel_netmask(self): + fuel_conf = self.get_fuel_config() + return fuel_conf['ADMIN_NETWORK']['netmask'] + + def get_fuel_gateway(self): + ip = self.get_fuel_ip() + return self.modify_ip(ip, 3, 1) + + def get_fuel_hostname(self): + fuel_conf = self.get_fuel_config() + return fuel_conf['HOSTNAME'] + + def get_fuel_dns(self): + fuel_conf = self.get_fuel_config() + return fuel_conf['DNS_UPSTREAM'] + + def get_node_property(self, node_id, property_name): + for node in self.dea_struct['nodes']: + if node['id'] == node_id and property_name in node: + return node[property_name] + + def get_node_role(self, node_id): + return self.get_node_property(node_id, 'role') + + def get_node_ids(self): + node_ids = [] + for node in self.dea_struct['nodes']: + node_ids.append(node['id']) + return node_ids + + def get_property(self, property_name): + return self.dea_struct[property_name] + + def collect_network_names(self): + self.network_names = [] + for network in self.dea_struct['network']['networks']: + self.network_names.append(network['name']) + + def get_network_names(self): + return self.network_names + + def get_dns_list(self): + settings = self.get_property('settings') + dns_list = settings['editable']['external_dns']['dns_list']['value'] + return [d.strip() for d in dns_list.split(',')] + + def get_ntp_list(self): + settings = self.get_property('settings') + ntp_list = settings['editable']['external_ntp']['ntp_list']['value'] + return [n.strip() for n in ntp_list.split(',')] + + def get_blade_node_map(self): + return self.dea_struct['blade_node_map'] diff --git a/deploy/deploy.py b/deploy/deploy.py new file mode 100644 index 000000000..4e209a932 --- /dev/null +++ b/deploy/deploy.py @@ -0,0 +1,321 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 os +import io +import re +import sys +import netaddr +import yaml + +from dea import DeploymentEnvironmentAdapter +from dha import DeploymentHardwareAdapter +from install_fuel_master import InstallFuelMaster +from deploy_env import CloudDeploy +from execution_environment import ExecutionEnvironment + +from common import ( + log, + exec_cmd, + err, + warn, + check_file_exists, + create_dir_if_not_exists, + delete, + check_if_root, + ArgParser, +) + +FUEL_VM = 'fuel' +PATCH_DIR = 'fuel_patch' +WORK_DIR = '~/deploy' +CWD = os.getcwd() + + +class cd: + + def __init__(self, new_path): + self.new_path = os.path.expanduser(new_path) + + def __enter__(self): + self.saved_path = CWD + os.chdir(self.new_path) + + def __exit__(self, etype, value, traceback): + os.chdir(self.saved_path) + + +class AutoDeploy(object): + + def __init__(self, no_fuel, fuel_only, no_health_check, cleanup_only, + cleanup, storage_dir, pxe_bridge, iso_file, dea_file, + dha_file, fuel_plugins_dir, fuel_plugins_conf_dir, + no_plugins): + self.no_fuel = no_fuel + self.fuel_only = fuel_only + self.no_health_check = no_health_check + self.cleanup_only = cleanup_only + self.cleanup = cleanup + self.storage_dir = storage_dir + self.pxe_bridge = pxe_bridge + self.iso_file = iso_file + self.dea_file = dea_file + self.dha_file = dha_file + self.fuel_plugins_dir = fuel_plugins_dir + self.fuel_plugins_conf_dir = fuel_plugins_conf_dir + self.no_plugins = no_plugins + self.dea = (DeploymentEnvironmentAdapter(dea_file) + if not cleanup_only else None) + self.dha = DeploymentHardwareAdapter(dha_file) + self.fuel_conf = {} + self.fuel_node_id = self.dha.get_fuel_node_id() + self.fuel_username, self.fuel_password = self.dha.get_fuel_access() + self.tmp_dir = None + + def modify_ip(self, ip_addr, index, val): + ip_str = str(netaddr.IPAddress(ip_addr)) + decimal_list = map(int, ip_str.split('.')) + decimal_list[index] = val + return '.'.join(map(str, decimal_list)) + + def collect_fuel_info(self): + self.fuel_conf['ip'] = self.dea.get_fuel_ip() + self.fuel_conf['gw'] = self.dea.get_fuel_gateway() + self.fuel_conf['dns1'] = self.dea.get_fuel_dns() + self.fuel_conf['netmask'] = self.dea.get_fuel_netmask() + self.fuel_conf['hostname'] = self.dea.get_fuel_hostname() + self.fuel_conf['showmenu'] = 'yes' + + def install_fuel_master(self): + log('Install Fuel Master') + new_iso = ('%s/deploy-%s' + % (self.tmp_dir, os.path.basename(self.iso_file))) + self.patch_iso(new_iso) + self.iso_file = new_iso + self.install_iso() + + def install_iso(self): + fuel = InstallFuelMaster(self.dea_file, self.dha_file, + self.fuel_conf['ip'], self.fuel_username, + self.fuel_password, self.fuel_node_id, + self.iso_file, WORK_DIR, + self.fuel_plugins_dir, self.no_plugins) + fuel.install() + + def patch_iso(self, new_iso): + tmp_orig_dir = '%s/origiso' % self.tmp_dir + tmp_new_dir = '%s/newiso' % self.tmp_dir + try: + self.copy(tmp_orig_dir, tmp_new_dir) + self.patch(tmp_new_dir, new_iso) + except Exception as e: + exec_cmd('fusermount -u %s' % tmp_orig_dir, False) + delete(self.tmp_dir) + err(e) + + def copy(self, tmp_orig_dir, tmp_new_dir): + log('Copying...') + os.makedirs(tmp_orig_dir) + os.makedirs(tmp_new_dir) + exec_cmd('fuseiso %s %s' % (self.iso_file, tmp_orig_dir)) + with cd(tmp_orig_dir): + exec_cmd('find . | cpio -pd %s' % tmp_new_dir) + exec_cmd('fusermount -u %s' % tmp_orig_dir) + delete(tmp_orig_dir) + exec_cmd('chmod -R 755 %s' % tmp_new_dir) + + def patch(self, tmp_new_dir, new_iso): + log('Patching...') + patch_dir = '%s/%s' % (CWD, PATCH_DIR) + ks_path = '%s/ks.cfg.patch' % patch_dir + + with cd(tmp_new_dir): + exec_cmd('cat %s | patch -p0' % ks_path) + delete('.rr_moved') + isolinux = 'isolinux/isolinux.cfg' + log('isolinux.cfg before: %s' + % exec_cmd('grep netmask %s' % isolinux)) + self.update_fuel_isolinux(isolinux) + log('isolinux.cfg after: %s' + % exec_cmd('grep netmask %s' % isolinux)) + + iso_linux_bin = 'isolinux/isolinux.bin' + exec_cmd('mkisofs -quiet -r -J -R -b %s ' + '-no-emul-boot -boot-load-size 4 ' + '-boot-info-table -hide-rr-moved ' + '-x "lost+found:" -o %s .' + % (iso_linux_bin, new_iso)) + + def update_fuel_isolinux(self, file): + with io.open(file) as f: + data = f.read() + for key, val in self.fuel_conf.iteritems(): + pattern = r'%s=[^ ]\S+' % key + replace = '%s=%s' % (key, val) + data = re.sub(pattern, replace, data) + with io.open(file, 'w') as f: + f.write(data) + + def deploy_env(self): + dep = CloudDeploy(self.dea, self.dha, self.fuel_conf['ip'], + self.fuel_username, self.fuel_password, + self.dea_file, self.fuel_plugins_conf_dir, + WORK_DIR, self.no_health_check) + return dep.deploy() + + def setup_execution_environment(self): + exec_env = ExecutionEnvironment(self.storage_dir, self.pxe_bridge, + self.dha_file, self.dea) + exec_env.setup_environment() + + def cleanup_execution_environment(self): + exec_env = ExecutionEnvironment(self.storage_dir, self.pxe_bridge, + self.dha_file, self.dea) + exec_env.cleanup_environment() + + def create_tmp_dir(self): + self.tmp_dir = '%s/fueltmp' % CWD + delete(self.tmp_dir) + create_dir_if_not_exists(self.tmp_dir) + + def deploy(self): + self.collect_fuel_info() + if not self.no_fuel: + self.setup_execution_environment() + self.create_tmp_dir() + self.install_fuel_master() + if not self.fuel_only: + return self.deploy_env() + return True + + def run(self): + check_if_root() + if self.cleanup_only: + self.cleanup_execution_environment() + else: + deploy_success = self.deploy() + if self.cleanup: + self.cleanup_execution_environment() + return deploy_success + return True + +def check_bridge(pxe_bridge, dha_path): + with io.open(dha_path) as yaml_file: + dha_struct = yaml.load(yaml_file) + if dha_struct['adapter'] != 'libvirt': + log('Using Linux Bridge %s for booting up the Fuel Master VM' + % pxe_bridge) + r = exec_cmd('ip link show %s' % pxe_bridge) + if pxe_bridge in r and 'state DOWN' in r: + err('Linux Bridge {0} is not Active, bring' + ' it UP first: [ip link set dev {0} up]'.format(pxe_bridge)) + + +def check_fuel_plugins_dir(dir): + msg = None + if not dir: + msg = 'Fuel Plugins Directory not specified!' + elif not os.path.isdir(dir): + msg = 'Fuel Plugins Directory does not exist!' + elif not os.listdir(dir): + msg = 'Fuel Plugins Directory is empty!' + if msg: + warn('%s No external plugins will be installed!' % msg) + + +def parse_arguments(): + parser = ArgParser(prog='python %s' % __file__) + parser.add_argument('-nf', dest='no_fuel', action='store_true', + default=False, + help='Do not install Fuel Master (and Node VMs when ' + 'using libvirt)') + parser.add_argument('-nh', dest='no_health_check', action='store_true', + default=False, + help='Don\'t run health check after deployment') + parser.add_argument('-fo', dest='fuel_only', action='store_true', + default=False, + help='Install Fuel Master only (and Node VMs when ' + 'using libvirt)') + parser.add_argument('-co', dest='cleanup_only', action='store_true', + default=False, + help='Cleanup VMs and Virtual Networks according to ' + 'what is defined in DHA') + parser.add_argument('-c', dest='cleanup', action='store_true', + default=False, + help='Cleanup after deploy') + if {'-iso', '-dea', '-dha', '-h'}.intersection(sys.argv): + parser.add_argument('-iso', dest='iso_file', action='store', nargs='?', + default='%s/OPNFV.iso' % CWD, + help='ISO File [default: OPNFV.iso]') + parser.add_argument('-dea', dest='dea_file', action='store', nargs='?', + default='%s/dea.yaml' % CWD, + help='Deployment Environment Adapter: dea.yaml') + parser.add_argument('-dha', dest='dha_file', action='store', nargs='?', + default='%s/dha.yaml' % CWD, + help='Deployment Hardware Adapter: dha.yaml') + else: + parser.add_argument('iso_file', action='store', nargs='?', + default='%s/OPNFV.iso' % CWD, + help='ISO File [default: OPNFV.iso]') + parser.add_argument('dea_file', action='store', nargs='?', + default='%s/dea.yaml' % CWD, + help='Deployment Environment Adapter: dea.yaml') + parser.add_argument('dha_file', action='store', nargs='?', + default='%s/dha.yaml' % CWD, + help='Deployment Hardware Adapter: dha.yaml') + parser.add_argument('-s', dest='storage_dir', action='store', + default='%s/images' % CWD, + help='Storage Directory [default: images]') + parser.add_argument('-b', dest='pxe_bridge', action='store', + default='pxebr', + help='Linux Bridge for booting up the Fuel Master VM ' + '[default: pxebr]') + parser.add_argument('-p', dest='fuel_plugins_dir', action='store', + help='Fuel Plugins directory') + parser.add_argument('-pc', dest='fuel_plugins_conf_dir', action='store', + help='Fuel Plugins Configuration directory') + parser.add_argument('-np', dest='no_plugins', action='store_true', + default=False, help='Do not install Fuel Plugins') + + args = parser.parse_args() + log(args) + + check_file_exists(args.dha_file) + + if not args.cleanup_only: + check_file_exists(args.dea_file) + check_fuel_plugins_dir(args.fuel_plugins_dir) + + if not args.no_fuel and not args.cleanup_only: + log('Using OPNFV ISO file: %s' % args.iso_file) + check_file_exists(args.iso_file) + log('Using image directory: %s' % args.storage_dir) + create_dir_if_not_exists(args.storage_dir) + check_bridge(args.pxe_bridge, args.dha_file) + + kwargs = {'no_fuel': args.no_fuel, 'fuel_only': args.fuel_only, + 'no_health_check': args.no_health_check, + 'cleanup_only': args.cleanup_only, 'cleanup': args.cleanup, + 'storage_dir': args.storage_dir, 'pxe_bridge': args.pxe_bridge, + 'iso_file': args.iso_file, 'dea_file': args.dea_file, + 'dha_file': args.dha_file, + 'fuel_plugins_dir': args.fuel_plugins_dir, + 'fuel_plugins_conf_dir': args.fuel_plugins_conf_dir, + 'no_plugins': args.no_plugins} + return kwargs + + +def main(): + kwargs = parse_arguments() + d = AutoDeploy(**kwargs) + sys.exit(d.run()) + +if __name__ == '__main__': + main() diff --git a/deploy/deploy_env.py b/deploy/deploy_env.py new file mode 100644 index 000000000..735ea66a3 --- /dev/null +++ b/deploy/deploy_env.py @@ -0,0 +1,266 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 os +import io +import yaml +import glob +import time +import shutil + +from ssh_client import SSHClient + +from common import ( + err, + log, + parse, + N, + E, + R, + delete, +) + +CLOUD_DEPLOY_FILE = 'deploy.py' +BLADE_RESTART_TIMES = 3 + + +class CloudDeploy(object): + + def __init__(self, dea, dha, fuel_ip, fuel_username, fuel_password, + dea_file, fuel_plugins_conf_dir, work_dir, no_health_check): + self.dea = dea + self.dha = dha + self.fuel_ip = fuel_ip + self.fuel_username = fuel_username + self.fuel_password = fuel_password + self.dea_file = dea_file + self.updated_dea_file = ( + '%s/.%s' % (os.path.dirname(self.dea_file), + os.path.basename(self.dea_file))) + shutil.copy2(self.dea_file, self.updated_dea_file) + self.fuel_plugins_conf_dir = fuel_plugins_conf_dir + self.work_dir = work_dir + self.no_health_check = no_health_check + self.file_dir = os.path.dirname(os.path.realpath(__file__)) + self.ssh = SSHClient(self.fuel_ip, self.fuel_username, + self.fuel_password) + self.node_ids = self.dha.get_node_ids() + self.wanted_release = self.dea.get_property('wanted_release') + self.blade_node_dict = {} + self.macs_per_blade = {} + + def merge_plugin_config_files_to_dea_file(self): + plugins_conf_dir = ( + self.fuel_plugins_conf_dir if self.fuel_plugins_conf_dir + else '%s/plugins_conf' % os.path.dirname(self.dea_file)) + if os.path.isdir(plugins_conf_dir): + with io.open(self.updated_dea_file) as stream: + updated_dea = yaml.load(stream) + for plugin_file in glob.glob('%s/*.yaml' % plugins_conf_dir): + with io.open(plugin_file) as stream: + plugin_conf = yaml.load(stream) + updated_dea['settings']['editable'].update(plugin_conf) + with io.open(self.updated_dea_file, 'w') as stream: + yaml.dump(updated_dea, stream, default_flow_style=False) + + def upload_cloud_deployment_files(self): + with self.ssh as s: + s.exec_cmd('rm -rf %s' % self.work_dir, False) + s.exec_cmd('mkdir %s' % self.work_dir) + s.scp_put(self.updated_dea_file, '%s/%s' % ( + self.work_dir, os.path.basename(self.dea_file))) + s.scp_put('%s/common.py' % self.file_dir, self.work_dir) + s.scp_put('%s/dea.py' % self.file_dir, self.work_dir) + for f in glob.glob('%s/cloud/*' % self.file_dir): + s.scp_put(f, self.work_dir) + + def power_off_nodes(self): + for node_id in self.node_ids: + self.dha.node_power_off(node_id) + + def power_on_nodes(self): + for node_id in self.node_ids: + self.dha.node_power_on(node_id) + + def set_boot_order(self, boot_order_list): + for node_id in self.node_ids: + self.dha.node_set_boot_order(node_id, boot_order_list[:]) + + def get_mac_addresses(self): + self.macs_per_blade = {} + for node_id in self.node_ids: + self.macs_per_blade[node_id] = self.dha.get_node_pxe_mac(node_id) + + def run_cloud_deploy(self, deploy_app): + log('START CLOUD DEPLOYMENT') + deploy_app = '%s/%s' % (self.work_dir, deploy_app) + dea_file = '%s/%s' % (self.work_dir, os.path.basename(self.dea_file)) + with self.ssh as s: + status = s.run('python %s %s %s' % ( + deploy_app, ('-nh' if self.no_health_check else ''), dea_file)) + return status + + def check_supported_release(self): + log('Check supported release: %s' % self.wanted_release) + found = False + release_list = parse(self.ssh.exec_cmd('fuel release -l')) + for release in release_list: + if release[R['name']] == self.wanted_release: + found = True + break + if not found: + err('This Fuel does not contain the following release: %s' + % self.wanted_release) + + def check_previous_installation(self): + log('Check previous installation') + env_list = parse(self.ssh.exec_cmd('fuel env list')) + if env_list: + self.cleanup_fuel_environments(env_list) + node_list = parse(self.ssh.exec_cmd('fuel node list')) + if node_list: + self.cleanup_fuel_nodes(node_list) + + def cleanup_fuel_environments(self, env_list): + WAIT_LOOP = 60 + SLEEP_TIME = 10 + for env in env_list: + log('Deleting environment %s' % env[E['id']]) + self.ssh.exec_cmd('fuel env --env %s --delete --force' + % env[E['id']]) + all_env_erased = False + for i in range(WAIT_LOOP): + env_list = parse(self.ssh.exec_cmd('fuel env list')) + if env_list: + time.sleep(SLEEP_TIME) + else: + all_env_erased = True + break + if not all_env_erased: + err('Could not erase these environments %s' + % [(env[E['id']], env[E['status']]) for env in env_list]) + + def cleanup_fuel_nodes(self, node_list): + for node in node_list: + if node[N['status']] == 'discover': + log('Deleting node %s' % node[N['id']]) + self.ssh.exec_cmd('fuel node --node-id %s --delete-from-db ' + '--force' % node[N['id']]) + self.ssh.exec_cmd('cobbler system remove --name node-%s' + % node[N['id']], False) + + def check_prerequisites(self): + log('Check prerequisites') + with self.ssh: + self.check_supported_release() + self.check_previous_installation() + + def wait_for_discovered_blades(self): + log('Wait for discovered blades') + discovered_macs = [] + restart_times = BLADE_RESTART_TIMES + + for blade in self.node_ids: + self.blade_node_dict[blade] = None + + with self.ssh: + all_discovered = self.discovery_waiting_loop(discovered_macs) + + while not all_discovered and restart_times != 0: + restart_times -= 1 + for blade in self.get_not_discovered_blades(): + self.dha.node_reset(blade) + with self.ssh: + all_discovered = self.discovery_waiting_loop(discovered_macs) + + if not all_discovered: + err('Not all blades have been discovered: %s' + % self.not_discovered_blades_summary()) + + with io.open(self.updated_dea_file) as stream: + updated_dea = yaml.load(stream) + updated_dea.update({'blade_node_map': self.blade_node_dict}) + with io.open(self.updated_dea_file, 'w') as stream: + yaml.dump(updated_dea, stream, default_flow_style=False) + + def discovery_waiting_loop(self, discovered_macs): + WAIT_LOOP = 360 + SLEEP_TIME = 10 + all_discovered = False + for i in range(WAIT_LOOP): + node_list = parse(self.ssh.exec_cmd('fuel node list')) + if node_list: + self.node_discovery(node_list, discovered_macs) + if self.all_blades_discovered(): + all_discovered = True + break + else: + time.sleep(SLEEP_TIME) + return all_discovered + + def node_discovery(self, node_list, discovered_macs): + for node in node_list: + if (node[N['status']] == 'discover' and + node[N['online']] == 'True' and + node[N['mac']] not in discovered_macs): + discovered_macs.append(node[N['mac']]) + blade = self.find_mac_in_dict(node[N['mac']]) + if blade: + log('Blade %s discovered as Node %s with MAC %s' + % (blade, node[N['id']], node[N['mac']])) + self.blade_node_dict[blade] = int(node[N['id']]) + + def find_mac_in_dict(self, mac): + for blade, mac_list in self.macs_per_blade.iteritems(): + if mac in mac_list: + return blade + + def all_blades_discovered(self): + for blade, node_id in self.blade_node_dict.iteritems(): + if not node_id: + return False + return True + + def not_discovered_blades_summary(self): + summary = '' + for blade, node_id in self.blade_node_dict.iteritems(): + if not node_id: + summary += '\n[blade %s]' % blade + return summary + + def get_not_discovered_blades(self): + not_discovered_blades = [] + for blade, node_id in self.blade_node_dict.iteritems(): + if not node_id: + not_discovered_blades.append(blade) + return not_discovered_blades + + def set_boot_order_nodes(self): + self.power_off_nodes() + self.set_boot_order(['pxe', 'disk']) + self.power_on_nodes() + + def deploy(self): + + self.set_boot_order_nodes() + + self.check_prerequisites() + + self.get_mac_addresses() + + self.wait_for_discovered_blades() + + self.merge_plugin_config_files_to_dea_file() + + self.upload_cloud_deployment_files() + + delete(self.updated_dea_file) + + return self.run_cloud_deploy(CLOUD_DEPLOY_FILE) diff --git a/deploy/dha.py b/deploy/dha.py new file mode 100644 index 000000000..cb413cbb0 --- /dev/null +++ b/deploy/dha.py @@ -0,0 +1,35 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 yaml +import io + +from dha_adapters.libvirt_adapter import LibvirtAdapter +from dha_adapters.ipmi_adapter import IpmiAdapter +from dha_adapters.hp_adapter import HpAdapter +from dha_adapters.amt_adapter import AmtAdapter + +class DeploymentHardwareAdapter(object): + + def __new__(cls, yaml_path): + with io.open(yaml_path) as yaml_file: + dha_struct = yaml.load(yaml_file) + type = dha_struct['adapter'] + + if cls is DeploymentHardwareAdapter: + if type == 'libvirt': + return LibvirtAdapter(yaml_path) + if type == 'ipmi': + return IpmiAdapter(yaml_path) + if type == 'hp': + return HpAdapter(yaml_path) + if type == 'amt': + return AmtAdapter(yaml_path) + return super(DeploymentHardwareAdapter, cls).__new__(cls) diff --git a/deploy/dha_adapters/__init__.py b/deploy/dha_adapters/__init__.py new file mode 100644 index 000000000..fb73157f9 --- /dev/null +++ b/deploy/dha_adapters/__init__.py @@ -0,0 +1,8 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 +############################################################################### diff --git a/deploy/dha_adapters/amt_adapter.py b/deploy/dha_adapters/amt_adapter.py new file mode 100644 index 000000000..02025b9dd --- /dev/null +++ b/deploy/dha_adapters/amt_adapter.py @@ -0,0 +1,100 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# liyi.meng@ericsson.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 +############################################################################### + + +from hardware_adapter import HardwareAdapter + +from common import ( + log, + exec_cmd, + err, +) + + +''' +This is hardware adapter for Intel AMT based system. It use amttool to interact + with the targeting node. It dose not support vPro v9. if the targeting system + is v9 or later, we need to consider a new adpater with using lib + like https://github.com/sdague/amt +''' +class AmtAdapter(HardwareAdapter): + + def __init__(self, yaml_path): + super(AmtAdapter, self).__init__(yaml_path) + #amttool dose not allow you change bios setting permanently. + # so we have to make a workaround to make it IPMI like. + self.boot_order = {} + + def node_get_boot_dev(self, node_id): + if node_id in self.boot_order: + dev = self.boot_order[node_id][0] + if dev == 'pxe': + return 'PXE-boot' + elif dev == 'iso': + return 'cd-boot' + elif dev == 'disk': + return 'HD-boot' + else: + return 'HD-boot' + + def get_access_info(self, node_id): + ip = self.get_node_property(node_id, 'amtIp') + username = self.get_node_property(node_id, 'amtUser') + password = self.get_node_property(node_id, 'amtPass') + return ip, username, password + + def amt_cmd(self, node_id): + ip, username, password = self.get_access_info(node_id) + # We first Setup password for amttool, then use ping to wake up the node over LAN + cmd = 'export AMT_PASSWORD={0};' \ + 'ping {1} -W 5 -c 1 -q;' \ + 'yes | amttool {1}'.format(password, ip) + return cmd + + def get_node_pxe_mac(self, node_id): + mac_list = [] + mac_list.append(self.get_node_property(node_id, 'pxeMac').lower()) + return mac_list + + def node_power_on(self, node_id): + log('Power ON Node %s' % node_id) + cmd_prefix = self.amt_cmd(node_id) + resp, ret = exec_cmd('{0} info'.format(cmd_prefix), check=False) + if 'Powerstate: S0' not in resp: + dev = self.node_get_boot_dev(node_id) + resp, ret = exec_cmd('{0} powerup {1}'.format(cmd_prefix, dev), check=False) + if 'pt_status: success' not in resp: + err('Could Not Power ON Node %s' % node_id) + + def node_power_off(self, node_id): + log('Power OFF Node %s' % node_id) + cmd_prefix = self.amt_cmd(node_id) + resp, ret = exec_cmd('{0} info'.format(cmd_prefix), check=False) + if "Powerstate: S0" in resp: + resp, ret = exec_cmd('{0} powerdown'.format(cmd_prefix), check=False) + if 'pt_status: success' not in resp: + err('Could Not Power OFF Node %s' % node_id) + + def node_reset(self, node_id): + log('RESET Node %s' % node_id) + cmd_prefix = self.amt_cmd(node_id) + dev = self.node_get_boot_dev(node_id) + resp, ret = exec_cmd('{0} info'.format(cmd_prefix), check=False) + if 'Powerstate: S0' in resp: + resp, ret = exec_cmd('{0} reset {1}'.format(cmd_prefix, dev), check=False) + if 'pt_status: success' not in resp: + err('Could Not RESET Node %s' % node_id) + else: + err('Cannot RESET Node %s because it\'s not Active, state: %s' + % (node_id, resp)) + + def node_set_boot_order(self, node_id, boot_order_list): + log('Set boot order %s on Node %s' % (boot_order_list, node_id)) + self.boot_order[node_id] = boot_order_list + diff --git a/deploy/dha_adapters/hardware_adapter.py b/deploy/dha_adapters/hardware_adapter.py new file mode 100644 index 000000000..2cd5ab842 --- /dev/null +++ b/deploy/dha_adapters/hardware_adapter.py @@ -0,0 +1,60 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 yaml +import io + + +class HardwareAdapter(object): + + def __init__(self, yaml_path): + self.dha_struct = None + self.parse_yaml(yaml_path) + + def parse_yaml(self, yaml_path): + with io.open(yaml_path) as yaml_file: + self.dha_struct = yaml.load(yaml_file) + + def get_adapter_type(self): + return self.dha_struct['adapter'] + + def get_all_node_ids(self): + node_ids = [] + for node in self.dha_struct['nodes']: + node_ids.append(node['id']) + node_ids.sort() + return node_ids + + def get_fuel_node_id(self): + for node in self.dha_struct['nodes']: + if 'isFuel' in node and node['isFuel']: + return node['id'] + + def get_node_ids(self): + node_ids = [] + fuel_node_id = self.get_fuel_node_id() + for node in self.dha_struct['nodes']: + if node['id'] != fuel_node_id: + node_ids.append(node['id']) + node_ids.sort() + return node_ids + + def get_node_property(self, node_id, property_name): + for node in self.dha_struct['nodes']: + if node['id'] == node_id and property_name in node: + return node[property_name] + + def get_fuel_access(self): + for node in self.dha_struct['nodes']: + if 'isFuel' in node and node['isFuel']: + return node['username'], node['password'] + + def get_disks(self): + return self.dha_struct['disks'] diff --git a/deploy/dha_adapters/hp_adapter.py b/deploy/dha_adapters/hp_adapter.py new file mode 100644 index 000000000..13bb3491a --- /dev/null +++ b/deploy/dha_adapters/hp_adapter.py @@ -0,0 +1,37 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 +############################################################################### + + +from ipmi_adapter import IpmiAdapter +from ssh_client import SSHClient + +from common import ( + log, +) + +DEV = {'pxe': 'bootsource5', + 'disk': 'bootsource3', + 'iso': 'bootsource1'} + +ROOT = '/system1/bootconfig1' + + +class HpAdapter(IpmiAdapter): + + def __init__(self, yaml_path): + super(HpAdapter, self).__init__(yaml_path) + + def node_set_boot_order(self, node_id, boot_order_list): + log('Set boot order %s on Node %s' % (boot_order_list, node_id)) + ip, username, password = self.get_access_info(node_id) + ssh = SSHClient(ip, username, password) + with ssh as s: + for order, dev in enumerate(boot_order_list): + s.exec_cmd('set %s/%s bootorder=%s' + % (ROOT, DEV[dev], order + 1)) diff --git a/deploy/dha_adapters/ipmi_adapter.py b/deploy/dha_adapters/ipmi_adapter.py new file mode 100644 index 000000000..8fda4f9f0 --- /dev/null +++ b/deploy/dha_adapters/ipmi_adapter.py @@ -0,0 +1,119 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 time +from hardware_adapter import HardwareAdapter + +from common import ( + log, + exec_cmd, + err, +) + + +class IpmiAdapter(HardwareAdapter): + + def __init__(self, yaml_path): + super(IpmiAdapter, self).__init__(yaml_path) + + def get_access_info(self, node_id): + ip = self.get_node_property(node_id, 'ipmiIp') + username = self.get_node_property(node_id, 'ipmiUser') + password = self.get_node_property(node_id, 'ipmiPass') + return ip, username, password + + def ipmi_cmd(self, node_id): + ip, username, password = self.get_access_info(node_id) + cmd = 'ipmitool -I lanplus -A password' + cmd += ' -H %s -U %s -P %s' % (ip, username, password) + return cmd + + def get_node_pxe_mac(self, node_id): + mac_list = [] + mac_list.append(self.get_node_property(node_id, 'pxeMac').lower()) + return mac_list + + def node_power_on(self, node_id): + WAIT_LOOP = 200 + SLEEP_TIME = 3 + log('Power ON Node %s' % node_id) + cmd_prefix = self.ipmi_cmd(node_id) + state = exec_cmd('%s chassis power status' % cmd_prefix) + if state == 'Chassis Power is off': + exec_cmd('%s chassis power on' % cmd_prefix) + done = False + for i in range(WAIT_LOOP): + state, _ = exec_cmd('%s chassis power status' % cmd_prefix, + False) + if state == 'Chassis Power is on': + done = True + break + else: + time.sleep(SLEEP_TIME) + if not done: + err('Could Not Power ON Node %s' % node_id) + + def node_power_off(self, node_id): + WAIT_LOOP = 200 + SLEEP_TIME = 3 + log('Power OFF Node %s' % node_id) + cmd_prefix = self.ipmi_cmd(node_id) + state = exec_cmd('%s chassis power status' % cmd_prefix) + if state == 'Chassis Power is on': + done = False + exec_cmd('%s chassis power off' % cmd_prefix) + for i in range(WAIT_LOOP): + state, _ = exec_cmd('%s chassis power status' % cmd_prefix, + False) + if state == 'Chassis Power is off': + done = True + break + else: + time.sleep(SLEEP_TIME) + if not done: + err('Could Not Power OFF Node %s' % node_id) + + def node_reset(self, node_id): + WAIT_LOOP = 600 + log('RESET Node %s' % node_id) + cmd_prefix = self.ipmi_cmd(node_id) + state = exec_cmd('%s chassis power status' % cmd_prefix) + if state == 'Chassis Power is on': + was_shut_off = False + done = False + exec_cmd('%s chassis power reset' % cmd_prefix) + for i in range(WAIT_LOOP): + state, _ = exec_cmd('%s chassis power status' % cmd_prefix, + False) + if state == 'Chassis Power is off': + was_shut_off = True + elif state == 'Chassis Power is on' and was_shut_off: + done = True + break + time.sleep(1) + if not done: + err('Could Not RESET Node %s' % node_id) + else: + err('Cannot RESET Node %s because it\'s not Active, state: %s' + % (node_id, state)) + + def node_set_boot_order(self, node_id, boot_order_list): + log('Set boot order %s on Node %s' % (boot_order_list, node_id)) + boot_order_list.reverse() + cmd_prefix = self.ipmi_cmd(node_id) + for dev in boot_order_list: + if dev == 'pxe': + exec_cmd('%s chassis bootdev pxe options=persistent' + % cmd_prefix) + elif dev == 'iso': + exec_cmd('%s chassis bootdev cdrom' % cmd_prefix) + elif dev == 'disk': + exec_cmd('%s chassis bootdev disk options=persistent' + % cmd_prefix) diff --git a/deploy/dha_adapters/libvirt_adapter.py b/deploy/dha_adapters/libvirt_adapter.py new file mode 100644 index 000000000..d6a30d75e --- /dev/null +++ b/deploy/dha_adapters/libvirt_adapter.py @@ -0,0 +1,138 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 +############################################################################### + + +from lxml import etree +from hardware_adapter import HardwareAdapter + +from common import ( + log, + exec_cmd, + err, + delete, +) + +DEV = {'pxe': 'network', + 'disk': 'hd', + 'iso': 'cdrom'} + + +class LibvirtAdapter(HardwareAdapter): + + def __init__(self, yaml_path): + super(LibvirtAdapter, self).__init__(yaml_path) + self.parser = etree.XMLParser(remove_blank_text=True) + + def node_power_off(self, node_id): + vm_name = self.get_node_property(node_id, 'libvirtName') + log('Power OFF Node %s' % vm_name) + state = exec_cmd('virsh domstate %s' % vm_name) + if state == 'running': + exec_cmd('virsh destroy %s' % vm_name, False) + + def node_power_on(self, node_id): + vm_name = self.get_node_property(node_id, 'libvirtName') + log('Power ON Node %s' % vm_name) + state = exec_cmd('virsh domstate %s' % vm_name) + if state == 'shut off': + exec_cmd('virsh start %s' % vm_name) + + def node_reset(self, node_id): + vm_name = self.get_node_property(node_id, 'libvirtName') + log('Reset Node %s' % vm_name) + exec_cmd('virsh reset %s' % vm_name) + + def translate(self, boot_order_list): + translated = [] + for boot_dev in boot_order_list: + if boot_dev in DEV: + translated.append(DEV[boot_dev]) + else: + err('Boot device %s not recognized' % boot_dev) + return translated + + def node_set_boot_order(self, node_id, boot_order_list): + boot_order_list = self.translate(boot_order_list) + vm_name = self.get_node_property(node_id, 'libvirtName') + temp_dir = exec_cmd('mktemp -d') + log('Set boot order %s on Node %s' % (boot_order_list, vm_name)) + resp = exec_cmd('virsh dumpxml %s' % vm_name) + xml_dump = etree.fromstring(resp, self.parser) + os = xml_dump.xpath('/domain/os') + for o in os: + for bootelem in ['boot', 'bootmenu']: + boot = o.xpath(bootelem) + for b in boot: + o.remove(b) + for dev in boot_order_list: + b = etree.Element('boot') + b.set('dev', dev) + o.append(b) + bmenu = etree.Element('bootmenu') + bmenu.set('enable', 'no') + o.append(bmenu) + tree = etree.ElementTree(xml_dump) + xml_file = temp_dir + '/%s.xml' % vm_name + with open(xml_file, 'w') as f: + tree.write(f, pretty_print=True, xml_declaration=True) + exec_cmd('virsh define %s' % xml_file) + delete(temp_dir) + + def node_zero_mbr(self, node_id): + vm_name = self.get_node_property(node_id, 'libvirtName') + resp = exec_cmd('virsh dumpxml %s' % vm_name) + xml_dump = etree.fromstring(resp) + disks = xml_dump.xpath('/domain/devices/disk') + for disk in disks: + if disk.get('device') == 'disk': + sources = disk.xpath('source') + for source in sources: + disk_file = source.get('file') + disk_size = exec_cmd('ls -l %s' % disk_file).split()[4] + delete(disk_file) + exec_cmd('fallocate -l %s %s' % (disk_size, disk_file)) + + def node_eject_iso(self, node_id): + vm_name = self.get_node_property(node_id, 'libvirtName') + device = self.get_name_of_device(vm_name, 'cdrom') + exec_cmd('virsh change-media %s --eject %s --config --live' + % (vm_name, device), False) + + def node_insert_iso(self, node_id, iso_file): + vm_name = self.get_node_property(node_id, 'libvirtName') + device = self.get_name_of_device(vm_name, 'cdrom') + exec_cmd('virsh change-media %s --insert %s %s' + % (vm_name, device, iso_file)) + + def get_node_pxe_mac(self, node_id): + mac_list = [] + vm_name = self.get_node_property(node_id, 'libvirtName') + resp = exec_cmd('virsh dumpxml %s' % vm_name) + xml_dump = etree.fromstring(resp) + interfaces = xml_dump.xpath('/domain/devices/interface') + for interface in interfaces: + macs = interface.xpath('mac') + for mac in macs: + mac_list.append(mac.get('address').lower()) + return mac_list + + def get_name_of_device(self, vm_name, device_type): + resp = exec_cmd('virsh dumpxml %s' % vm_name) + xml_dump = etree.fromstring(resp) + disks = xml_dump.xpath('/domain/devices/disk') + for disk in disks: + if disk.get('device') == device_type: + targets = disk.xpath('target') + for target in targets: + device = target.get('dev') + if device: + return device + + def get_virt_net_conf_dir(self): + return self.dha_struct['virtNetConfDir'] diff --git a/deploy/environments/__init__.py b/deploy/environments/__init__.py new file mode 100644 index 000000000..fb73157f9 --- /dev/null +++ b/deploy/environments/__init__.py @@ -0,0 +1,8 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 +############################################################################### diff --git a/deploy/environments/execution_environment.py b/deploy/environments/execution_environment.py new file mode 100644 index 000000000..38e5bcdf7 --- /dev/null +++ b/deploy/environments/execution_environment.py @@ -0,0 +1,75 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 +############################################################################### + + +from lxml import etree +from dha_adapters.libvirt_adapter import LibvirtAdapter + +from common import ( + exec_cmd, + log, + delete, +) + + +class ExecutionEnvironment(object): + + def __init__(self, storage_dir, dha_file, root_dir): + self.storage_dir = storage_dir + self.dha = LibvirtAdapter(dha_file) + self.root_dir = root_dir + self.parser = etree.XMLParser(remove_blank_text=True) + self.fuel_node_id = self.dha.get_fuel_node_id() + + def delete_vm(self, node_id): + vm_name = self.dha.get_node_property(node_id, 'libvirtName') + r, c = exec_cmd('virsh dumpxml %s' % vm_name, False) + if c: + return + self.undefine_vm_delete_disk(r, vm_name) + + def undefine_vm_delete_disk(self, printout, vm_name): + disk_files = [] + xml_dump = etree.fromstring(printout, self.parser) + disks = xml_dump.xpath('/domain/devices/disk') + for disk in disks: + sources = disk.xpath('source') + for source in sources: + source_file = source.get('file') + if source_file: + disk_files.append(source_file) + log('Deleting VM %s with disks %s' % (vm_name, disk_files)) + exec_cmd('virsh destroy %s' % vm_name, False) + exec_cmd('virsh undefine %s' % vm_name, False) + for file in disk_files: + delete(file) + + def define_vm(self, vm_name, temp_vm_file, disk_path): + log('Creating VM %s with disks %s' % (vm_name, disk_path)) + with open(temp_vm_file) as f: + vm_xml = etree.parse(f) + names = vm_xml.xpath('/domain/name') + for name in names: + name.text = vm_name + uuids = vm_xml.xpath('/domain/uuid') + for uuid in uuids: + uuid.getparent().remove(uuid) + disks = vm_xml.xpath('/domain/devices/disk') + for disk in disks: + if (disk.get('type') == 'file' and + disk.get('device') == 'disk'): + sources = disk.xpath('source') + for source in sources: + disk.remove(source) + source = etree.Element('source') + source.set('file', disk_path) + disk.append(source) + with open(temp_vm_file, 'w') as f: + vm_xml.write(f, pretty_print=True, xml_declaration=True) + exec_cmd('virsh define %s' % temp_vm_file) diff --git a/deploy/environments/libvirt_environment.py b/deploy/environments/libvirt_environment.py new file mode 100644 index 000000000..af2d3f8fb --- /dev/null +++ b/deploy/environments/libvirt_environment.py @@ -0,0 +1,107 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 +############################################################################### + + +from lxml import etree +import glob +from execution_environment import ExecutionEnvironment + +from common import ( + exec_cmd, + log, + check_dir_exists, + check_file_exists, + delete, +) + + +class LibvirtEnvironment(ExecutionEnvironment): + + def __init__(self, storage_dir, dha_file, dea, root_dir): + super(LibvirtEnvironment, self).__init__( + storage_dir, dha_file, root_dir) + self.dea = dea + self.network_dir = '%s/%s' % (self.root_dir, + self.dha.get_virt_net_conf_dir()) + self.node_ids = self.dha.get_all_node_ids() + self.net_names = self.collect_net_names() + + def create_storage(self, node_id, disk_path, disk_sizes): + if node_id == self.fuel_node_id: + disk_size = disk_sizes['fuel'] + else: + roles = self.dea.get_node_role(node_id) + role = 'controller' if 'controller' in roles else 'compute' + disk_size = disk_sizes[role] + exec_cmd('fallocate -l %s %s' % (disk_size, disk_path)) + + def create_vms(self): + temp_dir = exec_cmd('mktemp -d') + disk_sizes = self.dha.get_disks() + for node_id in self.node_ids: + vm_name = self.dha.get_node_property(node_id, 'libvirtName') + vm_template = '%s/%s' % (self.root_dir, + self.dha.get_node_property( + node_id, 'libvirtTemplate')) + check_file_exists(vm_template) + disk_path = '%s/%s.raw' % (self.storage_dir, vm_name) + self.create_storage(node_id, disk_path, disk_sizes) + temp_vm_file = '%s/%s' % (temp_dir, vm_name) + exec_cmd('cp %s %s' % (vm_template, temp_vm_file)) + self.define_vm(vm_name, temp_vm_file, disk_path) + delete(temp_dir) + + def start_vms(self): + for node_id in self.node_ids: + self.dha.node_power_on(node_id) + + def create_networks(self): + for net_file in glob.glob('%s/*' % self.network_dir): + exec_cmd('virsh net-define %s' % net_file) + for net in self.net_names: + log('Creating network %s' % net) + exec_cmd('virsh net-autostart %s' % net) + exec_cmd('virsh net-start %s' % net) + + def delete_networks(self): + for net in self.net_names: + log('Deleting network %s' % net) + exec_cmd('virsh net-destroy %s' % net, False) + exec_cmd('virsh net-undefine %s' % net, False) + + def get_net_name(self, net_file): + with open(net_file) as f: + net_xml = etree.parse(f) + name_list = net_xml.xpath('/network/name') + for name in name_list: + net_name = name.text + return net_name + + def collect_net_names(self): + net_list = [] + for net_file in glob.glob('%s/*' % self.network_dir): + name = self.get_net_name(net_file) + net_list.append(name) + return net_list + + def delete_vms(self): + for node_id in self.node_ids: + self.delete_vm(node_id) + + + def setup_environment(self): + check_dir_exists(self.network_dir) + self.cleanup_environment() + self.create_networks() + self.create_vms() + self.start_vms() + + def cleanup_environment(self): + self.delete_vms() + self.delete_networks() diff --git a/deploy/environments/virtual_fuel.py b/deploy/environments/virtual_fuel.py new file mode 100644 index 000000000..705eee48b --- /dev/null +++ b/deploy/environments/virtual_fuel.py @@ -0,0 +1,70 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 +############################################################################### + + +from lxml import etree +from execution_environment import ExecutionEnvironment + +from common import ( + exec_cmd, + check_file_exists, + check_if_root, + delete, +) + + +class VirtualFuel(ExecutionEnvironment): + + def __init__(self, storage_dir, pxe_bridge, dha_file, root_dir): + super(VirtualFuel, self).__init__(storage_dir, dha_file, root_dir) + self.pxe_bridge = pxe_bridge + + def set_vm_nic(self, temp_vm_file): + with open(temp_vm_file) as f: + vm_xml = etree.parse(f) + interfaces = vm_xml.xpath('/domain/devices/interface') + for interface in interfaces: + interface.getparent().remove(interface) + interface = etree.Element('interface') + interface.set('type', 'bridge') + source = etree.SubElement(interface, 'source') + source.set('bridge', self.pxe_bridge) + model = etree.SubElement(interface, 'model') + model.set('type', 'virtio') + devices = vm_xml.xpath('/domain/devices') + if devices: + device = devices[0] + device.append(interface) + with open(temp_vm_file, 'w') as f: + vm_xml.write(f, pretty_print=True, xml_declaration=True) + + def create_vm(self): + temp_dir = exec_cmd('mktemp -d') + vm_name = self.dha.get_node_property(self.fuel_node_id, 'libvirtName') + vm_template = '%s/%s' % (self.root_dir, + self.dha.get_node_property( + self.fuel_node_id, 'libvirtTemplate')) + check_file_exists(vm_template) + disk_path = '%s/%s.raw' % (self.storage_dir, vm_name) + disk_sizes = self.dha.get_disks() + disk_size = disk_sizes['fuel'] + exec_cmd('fallocate -l %s %s' % (disk_size, disk_path)) + temp_vm_file = '%s/%s' % (temp_dir, vm_name) + exec_cmd('cp %s %s' % (vm_template, temp_vm_file)) + self.set_vm_nic(temp_vm_file) + self.define_vm(vm_name, temp_vm_file, disk_path) + delete(temp_dir) + + def setup_environment(self): + check_if_root() + self.cleanup_environment() + self.create_vm() + + def cleanup_environment(self): + self.delete_vm(self.fuel_node_id) diff --git a/deploy/execution_environment.py b/deploy/execution_environment.py new file mode 100644 index 000000000..b2d268253 --- /dev/null +++ b/deploy/execution_environment.py @@ -0,0 +1,37 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 yaml +import io +import os + +from environments.libvirt_environment import LibvirtEnvironment +from environments.virtual_fuel import VirtualFuel + + +class ExecutionEnvironment(object): + + def __new__(cls, storage_dir, pxe_bridge, dha_path, dea): + + with io.open(dha_path) as yaml_file: + dha_struct = yaml.load(yaml_file) + + type = dha_struct['adapter'] + + root_dir = os.path.dirname(os.path.realpath(__file__)) + + if cls is ExecutionEnvironment: + if type == 'libvirt': + return LibvirtEnvironment(storage_dir, dha_path, dea, root_dir) + + if type in ['ipmi', 'hp', 'amt']: + return VirtualFuel(storage_dir, pxe_bridge, dha_path, root_dir) + + return super(ExecutionEnvironment, cls).__new__(cls) diff --git a/deploy/fuel_patch/ks.cfg.patch b/deploy/fuel_patch/ks.cfg.patch new file mode 100644 index 000000000..189695792 --- /dev/null +++ b/deploy/fuel_patch/ks.cfg.patch @@ -0,0 +1,19 @@ +*** ks.cfg.orig Wed Apr 15 21:47:09 2015 +--- ks.cfg Wed Apr 15 21:47:24 2015 +*************** +*** 35,41 **** + default_drive=`echo ${drives} ${removable_drives} | awk '{print $1}'` + + installdrive="undefined" +! forceformat="no" + for I in `cat /proc/cmdline`; do case "$I" in *=*) eval $I;; esac ; done + + set ${drives} ${removable_drives} +--- 35,41 ---- + default_drive=`echo ${drives} ${removable_drives} | awk '{print $1}'` + + installdrive="undefined" +! forceformat="yes" + for I in `cat /proc/cmdline`; do case "$I" in *=*) eval $I;; esac ; done + + set ${drives} ${removable_drives} diff --git a/deploy/install_fuel_master.py b/deploy/install_fuel_master.py new file mode 100644 index 000000000..e3a7327c0 --- /dev/null +++ b/deploy/install_fuel_master.py @@ -0,0 +1,223 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 time +import os +import glob +from ssh_client import SSHClient +from dha_adapters.libvirt_adapter import LibvirtAdapter + +from common import ( + log, + err, + clean, + delete, +) + +TRANSPLANT_FUEL_SETTINGS = 'transplant_fuel_settings.py' +BOOTSTRAP_ADMIN = '/usr/local/sbin/bootstrap_admin_node' +FUEL_CLIENT_CONFIG = '/etc/fuel/client/config.yaml' +PLUGINS_DIR = '~/plugins' +LOCAL_PLUGIN_FOLDER = '/opt/opnfv' +IGNORABLE_FUEL_ERRORS = ['does not update installed package', + 'Couldn\'t resolve host'] + + +class InstallFuelMaster(object): + + def __init__(self, dea_file, dha_file, fuel_ip, fuel_username, + fuel_password, fuel_node_id, iso_file, work_dir, + fuel_plugins_dir, no_plugins): + self.dea_file = dea_file + self.dha = LibvirtAdapter(dha_file) + self.fuel_ip = fuel_ip + self.fuel_username = fuel_username + self.fuel_password = fuel_password + self.fuel_node_id = fuel_node_id + self.iso_file = iso_file + self.iso_dir = os.path.dirname(self.iso_file) + self.work_dir = work_dir + self.fuel_plugins_dir = fuel_plugins_dir + self.no_plugins = no_plugins + self.file_dir = os.path.dirname(os.path.realpath(__file__)) + self.ssh = SSHClient(self.fuel_ip, self.fuel_username, + self.fuel_password) + + def install(self): + log('Start Fuel Installation') + + self.dha.node_power_off(self.fuel_node_id) + + log('Zero the MBR') + self.dha.node_zero_mbr(self.fuel_node_id) + + self.dha.node_set_boot_order(self.fuel_node_id, ['disk', 'iso']) + + try: + self.proceed_with_installation() + except Exception as e: + self.post_install_cleanup() + err(e) + + def proceed_with_installation(self): + log('Eject ISO') + self.dha.node_eject_iso(self.fuel_node_id) + + log('Insert ISO %s' % self.iso_file) + self.dha.node_insert_iso(self.fuel_node_id, self.iso_file) + + self.dha.node_power_on(self.fuel_node_id) + + log('Waiting for Fuel master to accept SSH') + self.wait_for_node_up() + + log('Wait until Fuel menu is up') + fuel_menu_pid = self.wait_until_fuel_menu_up() + + log('Inject our own astute.yaml settings') + self.inject_own_astute_yaml() + + log('Let the Fuel deployment continue') + log('Found FUEL menu as PID %s, now killing it' % fuel_menu_pid) + self.ssh_exec_cmd('kill %s' % fuel_menu_pid, False) + + log('Wait until installation complete') + self.wait_until_installation_completed() + + log('Waiting for one minute for Fuel to stabilize') + time.sleep(60) + + self.delete_deprecated_fuel_client_config() + + if not self.no_plugins: + + self.collect_plugin_files() + + self.install_plugins() + + self.post_install_cleanup() + + log('Fuel Master installed successfully !') + + def collect_plugin_files(self): + with self.ssh as s: + s.exec_cmd('mkdir %s' % PLUGINS_DIR) + if self.fuel_plugins_dir: + for f in glob.glob('%s/*.rpm' % self.fuel_plugins_dir): + s.scp_put(f, PLUGINS_DIR) + + def install_plugins(self): + log('Installing Fuel Plugins') + plugin_files = [] + with self.ssh as s: + for plugin_location in [PLUGINS_DIR, LOCAL_PLUGIN_FOLDER]: + r = s.exec_cmd('find %s -type f -name \'*.rpm\'' + % plugin_location) + plugin_files.extend(r.splitlines()) + for f in plugin_files: + log('Found plugin %s, installing ...' % f) + r, e = s.exec_cmd('fuel plugins --install %s' % f, False) + printout = r + e if e else r + if e and all([err not in printout + for err in IGNORABLE_FUEL_ERRORS]): + raise Exception('Installation of Fuel Plugin %s ' + 'failed: %s' % (f, e)) + + def wait_for_node_up(self): + WAIT_LOOP = 60 + SLEEP_TIME = 10 + success = False + for i in range(WAIT_LOOP): + try: + self.ssh.open() + success = True + break + except Exception: + log('Trying to SSH into Fuel VM %s ... sleeping %s seconds' + % (self.fuel_ip, SLEEP_TIME)) + time.sleep(SLEEP_TIME) + finally: + self.ssh.close() + + if not success: + raise Exception('Could not SSH into Fuel VM %s' % self.fuel_ip) + + def wait_until_fuel_menu_up(self): + WAIT_LOOP = 60 + SLEEP_TIME = 10 + CMD = 'ps -ef' + SEARCH = 'fuelmenu' + fuel_menu_pid = None + with self.ssh: + for i in range(WAIT_LOOP): + ret = self.ssh.exec_cmd(CMD) + fuel_menu_pid = self.get_fuel_menu_pid(ret, SEARCH) + if not fuel_menu_pid: + time.sleep(SLEEP_TIME) + else: + break + if not fuel_menu_pid: + raise Exception('Could not find the Fuel Menu Process ID') + return fuel_menu_pid + + def get_fuel_menu_pid(self, printout, search): + for line in printout.splitlines(): + if line.endswith(search): + return clean(line)[1] + + def ssh_exec_cmd(self, cmd, check=True): + with self.ssh: + ret = self.ssh.exec_cmd(cmd, check=check) + return ret + + def inject_own_astute_yaml(self): + with self.ssh as s: + s.exec_cmd('rm -rf %s' % self.work_dir, False) + s.exec_cmd('mkdir %s' % self.work_dir) + s.scp_put(self.dea_file, self.work_dir) + s.scp_put('%s/common.py' % self.file_dir, self.work_dir) + s.scp_put('%s/dea.py' % self.file_dir, self.work_dir) + s.scp_put('%s/transplant_fuel_settings.py' + % self.file_dir, self.work_dir) + log('Modifying Fuel astute') + s.run('python %s/%s %s/%s' + % (self.work_dir, TRANSPLANT_FUEL_SETTINGS, + self.work_dir, os.path.basename(self.dea_file))) + + def wait_until_installation_completed(self): + WAIT_LOOP = 360 + SLEEP_TIME = 10 + CMD = 'ps -ef | grep %s | grep -v grep' % BOOTSTRAP_ADMIN + + install_completed = False + with self.ssh: + for i in range(WAIT_LOOP): + ret = self.ssh.exec_cmd(CMD) + if not ret: + install_completed = True + break + else: + time.sleep(SLEEP_TIME) + + if not install_completed: + raise Exception('Fuel installation did not complete') + + def post_install_cleanup(self): + log('Eject ISO file %s' % self.iso_file) + self.dha.node_eject_iso(self.fuel_node_id) + delete(self.iso_dir) + + def delete_deprecated_fuel_client_config(self): + with self.ssh as s: + response, error = s.exec_cmd('fuel -v', False) + if (error and + 'DEPRECATION WARNING' in error and FUEL_CLIENT_CONFIG in error): + log('Delete deprecated fuel client config %s' % FUEL_CLIENT_CONFIG) + with self.ssh as s: + s.exec_cmd('rm %s' % FUEL_CLIENT_CONFIG, False) diff --git a/deploy/reap.py b/deploy/reap.py new file mode 100644 index 000000000..b7080cdae --- /dev/null +++ b/deploy/reap.py @@ -0,0 +1,340 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 time +import os +import yaml +import glob +import shutil + +from common import ( + N, + E, + R, + ArgParser, + exec_cmd, + parse, + err, + log, + delete, + commafy, +) + +DEA_1 = ''' +title: Deployment Environment Adapter (DEA) +# DEA API version supported +version: 1.1 +created: {date} +comment: {comment} +''' + +DHA_1 = ''' +title: Deployment Hardware Adapter (DHA) +# DHA API version supported +version: 1.1 +created: {date} +comment: {comment} + +# Adapter to use for this definition +# adapter: [ipmi|libvirt] +adapter: + +# Node list. +# Mandatory properties are id and role. +# All other properties are adapter specific. +# For Non-Fuel nodes controlled by: +# - ipmi adapter you need to provide: +# pxeMac +# ipmiIp +# ipmiUser +# ipmiPass +# - libvirt adapter you need to provide: +# libvirtName: <whatever> +# libvirtTemplate: [libvirt/vms/controller.xml | libvirt/vms/compute.xml] +# +# For the Fuel Node you need to provide: +# libvirtName: <whatever> +# libvirtTemplate: libvirt/vms/fuel.xml +# isFuel: yes +# username: root +# password: r00tme +''' + +DHA_2 = ''' +# Adding the Fuel node as node id {node_id} +# which may not be correct - please adjust as needed. +''' + +DISKS = {'fuel': '30G', + 'controller': '30G', + 'compute': '30G'} + + +class Reap(object): + + def __init__(self, dea_file, dha_file, comment): + self.dea_file = dea_file + self.dha_file = dha_file + self.comment = comment + self.temp_dir = None + self.env = None + self.env_id = None + self.last_node = None + + def get_env(self): + env_list = parse(exec_cmd('fuel env')) + if len(env_list) > 1: + err('Not exactly one environment') + self.env = env_list[0] + self.env_id = self.env[E['id']] + + def download_config(self, config_type): + log('Download %s config for environment %s' + % (config_type, self.env_id)) + exec_cmd('fuel %s --env %s --download --dir %s' + % (config_type, self.env_id, self.temp_dir)) + + def write(self, file, text, newline=True): + mode = 'a' if os.path.isfile(file) else 'w' + with open(file, mode) as f: + f.write('%s%s' % (text, ('\n' if newline else ''))) + + def write_yaml(self, file, data, newline=True): + self.write(file, yaml.dump(data, default_flow_style=False).strip(), + newline) + + def get_node_by_id(self, node_list, node_id): + for node in node_list: + if node[N['id']] == node_id: + return node + + def reap_interface(self, node_id, interfaces): + interface, mac = self.get_interface(node_id) + if_name = None + if interfaces: + if_name = self.check_dict_exists(interfaces, interface) + if not if_name: + if_name = 'interfaces_%s' % str(len(interfaces) + 1) + interfaces[if_name] = interface + return if_name, mac + + def reap_transformation(self, node_id, roles, transformations): + main_role = 'controller' if 'controller' in roles else 'compute' + node_file = glob.glob('%s/deployment_%s/*%s_%s.yaml' + % (self.temp_dir, self.env_id, + main_role, node_id)) + tr_name = None + with open(node_file[0]) as f: + node_config = yaml.load(f) + transformation = {'transformations': + node_config['network_scheme']['transformations']} + if transformations: + tr_name = self.check_dict_exists(transformations, transformation) + if not tr_name: + tr_name = 'transformations_%s' % str(len(transformations) + 1) + transformations[tr_name] = transformation + return tr_name + + def check_dict_exists(self, main_dict, dict): + for key, val in main_dict.iteritems(): + if cmp(dict, val) == 0: + return key + + def reap_nodes_interfaces_transformations(self): + node_list = parse(exec_cmd('fuel node')) + real_node_ids = [node[N['id']] for node in node_list] + real_node_ids.sort() + min_node = real_node_ids[0] + + interfaces = {} + transformations = {} + dea_nodes = [] + dha_nodes = [] + + for real_node_id in real_node_ids: + node_id = int(real_node_id) - int(min_node) + 1 + self.last_node = node_id + node = self.get_node_by_id(node_list, real_node_id) + roles = commafy(node[N['roles']]) + if not roles: + err('Fuel Node %s has no role' % real_node_id) + dea_node = {'id': node_id, + 'role': roles} + dha_node = {'id': node_id} + if_name, mac = self.reap_interface(real_node_id, interfaces) + tr_name = self.reap_transformation(real_node_id, roles, + transformations) + dea_node.update( + {'interfaces': if_name, + 'transformations': tr_name}) + + dha_node.update( + {'pxeMac': mac if mac else None, + 'ipmiIp': None, + 'ipmiUser': None, + 'ipmiPass': None, + 'libvirtName': None, + 'libvirtTemplate': None}) + + dea_nodes.append(dea_node) + dha_nodes.append(dha_node) + + self.write_yaml(self.dha_file, {'nodes': dha_nodes}, False) + self.write_yaml(self.dea_file, {'nodes': dea_nodes}) + self.write_yaml(self.dea_file, interfaces) + self.write_yaml(self.dea_file, transformations) + self.reap_fuel_node_info() + self.write_yaml(self.dha_file, {'disks': DISKS}) + + def reap_fuel_node_info(self): + dha_nodes = [] + dha_node = { + 'id': self.last_node + 1, + 'libvirtName': None, + 'libvirtTemplate': None, + 'isFuel': True, + 'username': 'root', + 'password': 'r00tme'} + + dha_nodes.append(dha_node) + + self.write(self.dha_file, DHA_2.format(node_id=dha_node['id']), False) + self.write_yaml(self.dha_file, dha_nodes) + + def reap_environment_info(self): + network_file = ('%s/network_%s.yaml' + % (self.temp_dir, self.env_id)) + network = self.read_yaml(network_file) + env = {'environment': + {'name': self.env[E['name']], + 'mode': self.env[E['mode']], + 'net_segment_type': + network['networking_parameters']['segmentation_type']}} + self.write_yaml(self.dea_file, env) + wanted_release = None + rel_list = parse(exec_cmd('fuel release')) + for rel in rel_list: + if rel[R['id']] == self.env[E['release_id']]: + wanted_release = rel[R['name']] + self.write_yaml(self.dea_file, {'wanted_release': wanted_release}) + + def reap_fuel_settings(self): + data = self.read_yaml('/etc/fuel/astute.yaml') + fuel = {} + del data['ADMIN_NETWORK']['mac'] + del data['ADMIN_NETWORK']['interface'] + for key in ['ADMIN_NETWORK', 'HOSTNAME', 'DNS_DOMAIN', 'DNS_SEARCH', + 'DNS_UPSTREAM', 'NTP1', 'NTP2', 'NTP3', 'FUEL_ACCESS']: + fuel[key] = data[key] + for key in fuel['ADMIN_NETWORK'].keys(): + if key not in ['ipaddress', 'netmask', + 'dhcp_pool_start', 'dhcp_pool_end']: + del fuel['ADMIN_NETWORK'][key] + self.write_yaml(self.dea_file, {'fuel': fuel}) + + def reap_network_settings(self): + network_file = ('%s/network_%s.yaml' + % (self.temp_dir, self.env_id)) + data = self.read_yaml(network_file) + network = {} + network['networking_parameters'] = data['networking_parameters'] + network['networks'] = data['networks'] + for net in network['networks']: + del net['id'] + del net['group_id'] + self.write_yaml(self.dea_file, {'network': network}) + + def reap_settings(self): + settings_file = '%s/settings_%s.yaml' % (self.temp_dir, self.env_id) + settings = self.read_yaml(settings_file) + self.write_yaml(self.dea_file, {'settings': settings}) + + def get_interface(self, real_node_id): + exec_cmd('fuel node --node-id %s --network --download --dir %s' + % (real_node_id, self.temp_dir)) + interface_file = ('%s/node_%s/interfaces.yaml' + % (self.temp_dir, real_node_id)) + interfaces = self.read_yaml(interface_file) + interface_config = {} + pxe_mac = None + for interface in interfaces: + networks = [] + for network in interface['assigned_networks']: + networks.append(network['name']) + if network['name'] == 'fuelweb_admin': + pxe_mac = interface['mac'] + if networks: + interface_config[interface['name']] = networks + return interface_config, pxe_mac + + def read_yaml(self, yaml_file): + with open(yaml_file) as f: + data = yaml.load(f) + return data + + def intro(self): + delete(self.dea_file) + delete(self.dha_file) + self.temp_dir = exec_cmd('mktemp -d') + date = time.strftime('%c') + self.write(self.dea_file, + DEA_1.format(date=date, comment=self.comment), False) + self.write(self.dha_file, + DHA_1.format(date=date, comment=self.comment)) + self.get_env() + self.download_config('deployment') + self.download_config('settings') + self.download_config('network') + + def finale(self): + log('DEA file is available at %s' % self.dea_file) + log('DHA file is available at %s (this is just a template)' + % self.dha_file) + shutil.rmtree(self.temp_dir) + + def reap(self): + self.intro() + self.reap_environment_info() + self.reap_nodes_interfaces_transformations() + self.reap_fuel_settings() + self.reap_network_settings() + self.reap_settings() + self.finale() + + +def usage(): + print ''' + Usage: + python reap.py <dea_file> <dha_file> <comment> + ''' + + +def parse_arguments(): + parser = ArgParser(prog='python %s' % __file__) + parser.add_argument('dea_file', nargs='?', action='store', + default='dea.yaml', + help='Deployment Environment Adapter: dea.yaml') + parser.add_argument('dha_file', nargs='?', action='store', + default='dha.yaml', + help='Deployment Hardware Adapter: dha.yaml') + parser.add_argument('comment', nargs='?', action='store', help='Comment') + args = parser.parse_args() + return (args.dea_file, args.dha_file, args.comment) + + +def main(): + dea_file, dha_file, comment = parse_arguments() + + r = Reap(dea_file, dha_file, comment) + r.reap() + + +if __name__ == '__main__': + main() diff --git a/deploy/ssh_client.py b/deploy/ssh_client.py new file mode 100644 index 000000000..df780961f --- /dev/null +++ b/deploy/ssh_client.py @@ -0,0 +1,98 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 paramiko +import scp + +from common import ( + log, + err, +) + +TIMEOUT = 600 + +class SSHClient(object): + + def __init__(self, host, username, password): + self.host = host + self.username = username + self.password = password + self.client = None + + def open(self, timeout=TIMEOUT): + self.client = paramiko.SSHClient() + self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + self.client.connect(self.host, username=self.username, + password=self.password, look_for_keys=False, + timeout=timeout) + + def close(self): + if self.client is not None: + self.client.close() + self.client = None + + def __enter__(self): + self.open() + return self + + def __exit__(self, type, value, traceback): + self.close() + + def exec_cmd(self, command, check=True, sudo=False, timeout=TIMEOUT): + if sudo and self.username != 'root': + command = "sudo -S -p '' %s" % command + stdin, stdout, stderr = self.client.exec_command(command, + timeout=timeout) + if sudo: + stdin.write(self.password + '\n') + stdin.flush() + response = stdout.read().strip() + error = stderr.read().strip() + + if check: + if error: + self.close() + raise Exception(error) + else: + return response + return response, error + + def run(self, command): + transport = self.client.get_transport() + transport.set_keepalive(1) + chan = transport.open_session() + chan.exec_command(command) + while not chan.exit_status_ready(): + if chan.recv_ready(): + data = chan.recv(1024) + while data: + log(data.strip()) + data = chan.recv(1024) + + if chan.recv_stderr_ready(): + error_buff = chan.recv_stderr(1024) + while error_buff: + log(error_buff.strip()) + error_buff = chan.recv_stderr(1024) + return chan.recv_exit_status() + + def scp_get(self, remote, local='.', dir=False): + try: + with scp.SCPClient(self.client.get_transport()) as _scp: + _scp.get(remote, local, dir) + except Exception as e: + err(e) + + def scp_put(self, local, remote='.', dir=False): + try: + with scp.SCPClient(self.client.get_transport()) as _scp: + _scp.put(local, remote, dir) + except Exception as e: + err(e) diff --git a/deploy/templates/hardware_environment/conf/ericsson_montreal_lab/dea.yaml b/deploy/templates/hardware_environment/conf/ericsson_montreal_lab/dea.yaml new file mode 100644 index 000000000..c7d741880 --- /dev/null +++ b/deploy/templates/hardware_environment/conf/ericsson_montreal_lab/dea.yaml @@ -0,0 +1,901 @@ +title: Deployment Environment Adapter (DEA) +# DEA API version supported +version: +created: +comment: Config for Ericsson Montreal Lab - HA deployment with Ceph and Opendaylight +environment: + name: opnfv_virt + mode: ha + net_segment_type: tun +wanted_release: Kilo on Ubuntu 14.04 +nodes: +- id: 1 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 2 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 3 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 4 + interfaces: interfaces_1 + transformations: transformations_2 + role: ceph-osd,compute +- id: 5 + interfaces: interfaces_1 + transformations: transformations_2 + role: ceph-osd,compute +- id: 6 + interfaces: interfaces_1 + transformations: transformations_2 + role: ceph-osd,compute +fuel: + ADMIN_NETWORK: + cidr: 10.20.0.0/24 + dhcp_gateway: 10.20.0.2 + dhcp_pool_end: 10.20.0.254 + dhcp_pool_start: 10.20.0.3 + ipaddress: 10.20.0.2 + netmask: 255.255.0.0 + DNS_DOMAIN: opnfvericsson.ca + DNS_SEARCH: opnfvericsson.ca + DNS_UPSTREAM: 10.118.32.193 + FUEL_ACCESS: + password: admin + user: admin + HOSTNAME: opnfv_virt + NTP1: 10.118.32.193 + NTP2: 10.118.34.193 + NTP3: null +interfaces_1: + eth0: + - fuelweb_admin + eth2: + - public + - management + - storage + - private +transformations_1: + transformations: + - action: add-br + name: br-fw-admin + - action: add-br + name: br-mgmt + - action: add-br + name: br-storage + - action: add-br + name: br-ex + - action: add-br + name: br-floating + provider: ovs + - action: add-patch + bridges: + - br-floating + - br-ex + mtu: 65000 + provider: ovs + - action: add-br + name: br-mesh + - action: add-port + bridge: br-fw-admin + name: eth0 + - action: add-port + bridge: br-mgmt + name: eth2.320 + - action: add-port + bridge: br-storage + name: eth2.220 + - action: add-port + bridge: br-mesh + name: eth2.20 + - action: add-port + bridge: br-ex + name: eth0 +transformations_2: + transformations: + - action: add-br + name: br-fw-admin + - action: add-br + name: br-mgmt + - action: add-br + name: br-storage + - action: add-br + name: br-mesh + - action: add-port + bridge: br-fw-admin + name: eth0 + - action: add-port + bridge: br-mgmt + name: eth2.320 + - action: add-port + bridge: br-storage + name: eth2.220 + - action: add-port + bridge: br-mesh + name: eth2.20 +network: + management_vip: 192.168.0.2 + management_vrouter_vip: 192.168.0.1 + networking_parameters: + base_mac: fa:16:3e:00:00:00 + configuration_template: null + dns_nameservers: + - 10.118.32.193 + floating_ranges: + - - 10.118.34.226 + - 10.118.34.230 + gre_id_range: + - 2 + - 65535 + internal_cidr: 192.168.111.0/24 + internal_gateway: 192.168.111.1 + net_l23_provider: ovs + segmentation_type: tun + vlan_range: + - 2022 + - 2023 + networks: + - cidr: 10.118.34.192/24 + gateway: 10.118.34.193 + ip_ranges: + - - 10.118.34.220 + - 10.118.34.225 + meta: + cidr: 172.16.0.0/24 + configurable: true + floating_range_var: floating_ranges + ip_range: + - 172.16.0.2 + - 172.16.0.126 + map_priority: 1 + name: public + notation: ip_ranges + render_addr_mask: public + render_type: null + use_gateway: true + vips: + - haproxy + - vrouter + vlan_start: null + name: public + vlan_start: null + - cidr: 192.168.0.0/24 + gateway: null + ip_ranges: + - - 192.168.0.1 + - 192.168.0.254 + meta: + cidr: 192.168.0.0/24 + configurable: true + map_priority: 2 + name: management + notation: cidr + render_addr_mask: internal + render_type: cidr + use_gateway: false + vips: + - haproxy + - vrouter + vlan_start: 101 + name: management + vlan_start: 320 + - cidr: 192.168.1.0/24 + gateway: null + ip_ranges: + - - 192.168.1.1 + - 192.168.1.254 + meta: + cidr: 192.168.1.0/24 + configurable: true + map_priority: 2 + name: storage + notation: cidr + render_addr_mask: storage + render_type: cidr + use_gateway: false + vlan_start: 220 + name: storage + vlan_start: 102 + - cidr: 192.168.2.0/24 + gateway: null + ip_ranges: + - - 192.168.2.1 + - 192.168.2.254 + meta: + cidr: 192.168.2.0/24 + configurable: true + map_priority: 2 + name: private + notation: cidr + render_addr_mask: null + render_type: cidr + seg_type: tun + use_gateway: false + vlan_start: 103 + name: private + vlan_start: 20 + - cidr: 10.20.0.0/16 + gateway: 10.20.0.2 + ip_ranges: + - - 10.20.0.3 + - 10.20.0.254 + meta: + configurable: false + map_priority: 0 + notation: ip_ranges + render_addr_mask: null + render_type: null + unmovable: true + use_gateway: true + name: fuelweb_admin + vlan_start: null + public_vip: 10.118.34.220 + public_vrouter_vip: 10.118.34.221 + vips: + management: + ipaddr: 192.168.0.2 + namespace: haproxy + network_role: mgmt/vip + node_roles: + - controller + - primary-controller + public: + ipaddr: 10.118.34.220 + namespace: haproxy + network_role: public/vip + node_roles: + - controller + - primary-controller + vrouter: + ipaddr: 192.168.0.1 + namespace: vrouter + network_role: mgmt/vip + node_roles: + - controller + - primary-controller + vrouter_pub: + ipaddr: 10.118.34.221 + namespace: vrouter + network_role: public/vip + node_roles: + - controller + - primary-controller +settings: + editable: + access: + email: + description: Email address for Administrator + label: Email + regex: + error: Invalid email + source: ^\S+@\S+$ + type: text + value: admin@localhost + weight: 40 + metadata: + label: Access + weight: 10 + password: + description: Password for Administrator + label: Password + regex: + error: Empty password + source: \S + type: password + value: admin + weight: 20 + tenant: + description: Tenant (project) name for Administrator + label: Tenant + regex: + error: Invalid tenant name + source: ^(?!services$)(?!nova$)(?!glance$)(?!keystone$)(?!neutron$)(?!cinder$)(?!swift$)(?!ceph$)(?![Gg]uest$)(?!.* + +.*$).+ + type: text + value: admin + weight: 30 + user: + description: Username for Administrator + label: Username + regex: + error: Invalid username + source: ^(?!services$)(?!nova$)(?!glance$)(?!keystone$)(?!neutron$)(?!cinder$)(?!swift$)(?!ceph$)(?![Gg]uest$)(?!.* + +.*$).+ + type: text + value: admin + weight: 10 + additional_components: + ceilometer: + description: If selected, Ceilometer component will be installed + label: Install Ceilometer + type: checkbox + value: false + weight: 40 + heat: + description: '' + label: '' + type: hidden + value: true + weight: 30 + metadata: + label: Additional Components + weight: 20 + mongo: + description: If selected, You can use external Mongo DB as ceilometer backend + label: Use external Mongo DB + restrictions: + - settings:additional_components.ceilometer.value == false + type: checkbox + value: false + weight: 40 + murano: + description: If selected, Murano component will be installed + label: Install Murano + type: checkbox + value: false + weight: 20 + sahara: + description: If selected, Sahara component will be installed + label: Install Sahara + type: checkbox + value: false + weight: 10 + common: + auth_key: + description: Public key(s) to include in authorized_keys on deployed nodes + label: Public Key + type: textarea + value: '' + weight: 70 + auto_assign_floating_ip: + description: If selected, OpenStack will automatically assign a floating IP + to a new instance + label: Auto assign floating IP + restrictions: + - action: hide + condition: cluster:net_provider == 'neutron' + type: checkbox + value: false + weight: 40 + debug: + description: Debug logging mode provides more information, but requires more + disk space. + label: OpenStack debug logging + type: checkbox + value: false + weight: 20 + libvirt_type: + label: Hypervisor type + type: radio + value: kvm + values: + - data: kvm + description: Choose this type of hypervisor if you run OpenStack on hardware + label: KVM + - data: qemu + description: Choose this type of hypervisor if you run OpenStack on virtual + hosts. + label: QEMU + weight: 30 + metadata: + label: Common + weight: 30 + nova_quota: + description: Quotas are used to limit CPU and memory usage for tenants. Enabling + quotas will increase load on the Nova database. + label: Nova quotas + type: checkbox + value: false + weight: 25 + puppet_debug: + description: Debug puppet logging mode provides more information, but requires + more disk space. + label: Puppet debug logging + type: checkbox + value: true + weight: 20 + resume_guests_state_on_host_boot: + description: Whether to resume previous guests state when the host reboots. + If enabled, this option causes guests assigned to the host to resume their + previous state. If the guest was running a restart will be attempted when + nova-compute starts. If the guest was not running previously, a restart will + not be attempted. + label: Resume guests state on host boot + type: checkbox + value: true + weight: 60 + use_cow_images: + description: For most cases you will want qcow format. If it's disabled, raw + image format will be used to run VMs. OpenStack with raw format currently + does not support snapshotting. + label: Use qcow format for images + type: checkbox + value: true + weight: 50 + use_vcenter: + restrictions: + - condition: cluster:net_provider == 'nova_network' + type: hidden + value: false + weight: 30 + corosync: + group: + description: '' + label: Group + type: text + value: 226.94.1.1 + weight: 10 + metadata: + label: Corosync + restrictions: + - action: hide + condition: 'true' + weight: 50 + port: + description: '' + label: Port + type: text + value: '12000' + weight: 20 + verified: + description: Set True only if multicast is configured correctly on router. + label: Need to pass network verification. + type: checkbox + value: false + weight: 10 + external_dns: + dns_list: + description: List of upstream DNS servers, separated by comma + label: DNS list + regex: + error: Invalid IP address list + source: ^\*$|^(?:\d|1?\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|1?\d\d|2[0-4]\d|25[0-5])){3}(?:\s*,\s*(?:\d|1?\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|1?\d\d|2[0-4]\d|25[0-5])){3})*$ + type: text + value: 10.118.32.193 + weight: 10 + metadata: + label: Host OS DNS Servers + weight: 90 + external_mongo: + hosts_ip: + description: IP Addresses of MongoDB. Use comma to split IPs + label: MongoDB hosts IP + regex: + error: Invalid hosts ip sequence + source: ^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?),)*((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ + type: text + value: '' + weight: 30 + metadata: + label: External MongoDB + restrictions: + - action: hide + condition: settings:additional_components.mongo.value == false + message: Ceilometer and MongoDB are not enabled on the Additional Components + section + weight: 20 + mongo_db_name: + description: Mongo database name + label: Database name + regex: + error: Invalid database name + source: ^\w+$ + type: text + value: ceilometer + weight: 30 + mongo_password: + description: Mongo database password + label: Password + regex: + error: Password contains spaces + source: ^\S*$ + type: password + value: ceilometer + weight: 30 + mongo_replset: + description: Name for Mongo replication set + label: Replset + type: text + value: '' + weight: 30 + mongo_user: + description: Mongo database username + label: Username + regex: + error: Empty username + source: ^\w+$ + type: text + value: ceilometer + weight: 30 + external_ntp: + metadata: + label: Host OS NTP Servers + weight: 100 + ntp_list: + description: List of upstream NTP servers, separated by comma + label: NTP server list + regex: + error: Invalid NTP server list + source: ^\s*(?:(?:\w+(?:-+\w+)*\.)+[a-z]+|\d{1,3}(?:\.\d{1,3}){3})\s*(?:,\s*(?:(?:\w+(?:-+\w+)*\.)+[a-z]+|\d{1,3}(\.\d{1,3}){3})\s*)*$ + type: text + value: 10.118.32.193, 10.118.34.193 + weight: 10 + kernel_params: + kernel: + description: Default kernel parameters + label: Initial parameters + type: text + value: console=tty0 net.ifnames=0 biosdevname=0 rootdelay=90 nomodeset + weight: 45 + metadata: + label: Kernel parameters + weight: 40 + murano_settings: + metadata: + label: Murano Settings + restrictions: + - action: hide + condition: settings:additional_components.murano.value == false + message: Murano is not enabled on the Additional Components section + weight: 20 + murano_repo_url: + description: '' + label: Murano Repository URL + type: text + value: http://storage.apps.openstack.org/ + weight: 10 + neutron_advanced_configuration: + metadata: + label: Neutron Advanced Configuration + restrictions: + - action: hide + condition: cluster:net_provider != 'neutron' + weight: 45 + neutron_dvr: + description: Enable Distributed Virtual Routers in Neutron + label: Neutron DVR + restrictions: + - ? networking_parameters:segmentation_type != 'vlan' and settings:neutron_advanced_configuration.neutron_l2_pop.value + == false + : DVR requires L2 population to be enabled. + type: checkbox + value: false + weight: 20 + neutron_l2_pop: + description: Enable L2 population mechanism in Neutron + label: Neutron L2 population + restrictions: + - action: hide + condition: networking_parameters:segmentation_type == 'vlan' + type: checkbox + value: false + weight: 10 + neutron_mellanox: + metadata: + enabled: true + label: Mellanox Neutron components + restrictions: + - action: hide + condition: not ('experimental' in version:feature_groups) + toggleable: false + weight: 50 + plugin: + label: Mellanox drivers and SR-IOV plugin + type: radio + value: disabled + values: + - data: disabled + description: If selected, Mellanox drivers, Neutron and Cinder plugin will + not be installed. + label: Mellanox drivers and plugins disabled + restrictions: + - settings:storage.iser.value == true + - data: drivers_only + description: If selected, Mellanox Ethernet drivers will be installed to support + networking over Mellanox NIC. Mellanox Neutron plugin will not be installed. + label: Install only Mellanox drivers + restrictions: + - settings:common.libvirt_type.value != 'kvm' + - data: ethernet + description: If selected, both Mellanox Ethernet drivers and Mellanox network + acceleration (Neutron) plugin will be installed. + label: Install Mellanox drivers and SR-IOV plugin + restrictions: + - settings:common.libvirt_type.value != 'kvm' or not (cluster:net_provider + == 'neutron' and networking_parameters:segmentation_type == 'vlan') + weight: 60 + vf_num: + description: Note that one virtual function will be reserved to the storage + network, in case of choosing iSER. + label: Number of virtual NICs + restrictions: + - settings:neutron_mellanox.plugin.value != 'ethernet' + type: text + value: '16' + weight: 70 + provision: + metadata: + label: Provision + restrictions: + - action: hide + condition: 'true' + weight: 80 + method: + type: hidden + value: image + public_network_assignment: + assign_to_all_nodes: + description: When disabled, public network will be assigned to controllers only + label: Assign public network to all nodes + type: checkbox + value: false + weight: 10 + metadata: + label: Public network assignment + restrictions: + - action: hide + condition: cluster:net_provider != 'neutron' + weight: 50 + public_ssl: + cert_data: + description: Certificate and private key data, concatenated into a single file + label: Certificate + restrictions: + - action: hide + condition: (settings:public_ssl.cert_source.value != 'user_uploaded') or (settings:public_ssl.horizon.value + == false and settings:public_ssl.services.value == false) + type: file + value: '' + weight: 40 + cert_source: + description: From where we'll get certificate and private key + label: Select source for certificate + restrictions: + - action: hide + condition: settings:public_ssl.horizon.value == false and settings:public_ssl.services.value + == false + type: radio + value: self_signed + values: + - data: self_signed + description: Generate private key and certificate that will be signed by this + key + label: Self-signed + - data: user_uploaded + description: Use pre-generated key and certificate + label: I have my own keypair with certificate + weight: 30 + horizon: + description: Secure access to Horizon enabling HTTPS instead of HTTP + label: HTTPS for Horizon + type: checkbox + value: false + weight: 10 + hostname: + description: Your DNS entries should point to this name. Self-signed certificates + also will use this hostname + label: DNS hostname for public TLS endpoints + restrictions: + - action: hide + condition: settings:public_ssl.horizon.value == false and settings:public_ssl.services.value + == false + type: text + value: public.fuel.local + weight: 50 + metadata: + label: Public TLS + weight: 110 + services: + description: Enable TLS termination on HAProxy for OpenStack services + label: TLS for OpenStack public endpoints + type: checkbox + value: false + weight: 20 + repo_setup: + metadata: + always_editable: true + label: Repositories + weight: 50 + repos: + description: 'Please note: the first repository will be considered the operating + system mirror that will be used during node provisioning. + + To create a local repository mirror on the Fuel master node, please follow + the instructions provided by running "fuel-createmirror --help" on the Fuel + master node. + + Please make sure your Fuel master node has Internet access to the repository + before attempting to create a mirror. + + For more details, please refer to the documentation (https://docs.mirantis.com/openstack/fuel/fuel-7.0/operations.html#external-ubuntu-ops). + + ' + extra_priority: null + type: custom_repo_configuration + value: + - name: ubuntu + priority: null + section: main universe multiverse + suite: trusty + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: ubuntu-updates + priority: null + section: main universe multiverse + suite: trusty-updates + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: ubuntu-security + priority: null + section: main universe multiverse + suite: trusty-security + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: mos + priority: 1050 + section: main restricted + suite: mos7.0 + type: deb + uri: http://10.20.0.2:8080/2015.1.0-7.0/ubuntu/x86_64 + - name: mos-updates + priority: 1050 + section: main restricted + suite: mos7.0-updates + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: mos-security + priority: 1050 + section: main restricted + suite: mos7.0-security + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: mos-holdback + priority: 1100 + section: main restricted + suite: mos7.0-holdback + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: Auxiliary + priority: 1150 + section: main restricted + suite: auxiliary + type: deb + uri: http://10.20.0.2:8080/2015.1.0-7.0/ubuntu/auxiliary + storage: + ephemeral_ceph: + description: Configures Nova to store ephemeral volumes in RBD. This works best + if Ceph is enabled for volumes and images, too. Enables live migration of + all types of Ceph backed VMs (without this option, live migration will only + work with VMs launched from Cinder volumes). + label: Ceph RBD for ephemeral volumes (Nova) + type: checkbox + value: true + weight: 75 + images_ceph: + description: Configures Glance to use the Ceph RBD backend to store images. + If enabled, this option will prevent Swift from installing. + label: Ceph RBD for images (Glance) + restrictions: + - settings:storage.images_vcenter.value == true: Only one Glance backend could + be selected. + type: checkbox + value: true + weight: 30 + images_vcenter: + description: Configures Glance to use the vCenter/ESXi backend to store images. + If enabled, this option will prevent Swift from installing. + label: VMWare vCenter/ESXi datastore for images (Glance) + restrictions: + - action: hide + condition: settings:common.use_vcenter.value != true + - condition: settings:storage.images_ceph.value == true + message: Only one Glance backend could be selected. + type: checkbox + value: false + weight: 35 + iser: + description: 'High performance block storage: Cinder volumes over iSER protocol + (iSCSI over RDMA). This feature requires SR-IOV capabilities in the NIC, and + will use a dedicated virtual function for the storage network.' + label: iSER protocol for volumes (Cinder) + restrictions: + - settings:storage.volumes_lvm.value != true or settings:common.libvirt_type.value + != 'kvm' + - action: hide + condition: not ('experimental' in version:feature_groups) + type: checkbox + value: false + weight: 11 + metadata: + label: Storage + weight: 60 + objects_ceph: + description: Configures RadosGW front end for Ceph RBD. This exposes S3 and + Swift API Interfaces. If enabled, this option will prevent Swift from installing. + label: Ceph RadosGW for objects (Swift API) + restrictions: + - settings:storage.images_ceph.value == false + type: checkbox + value: false + weight: 80 + osd_pool_size: + description: Configures the default number of object replicas in Ceph. This + number must be equal to or lower than the number of deployed 'Storage - Ceph + OSD' nodes. + label: Ceph object replication factor + regex: + error: Invalid number + source: ^[1-9]\d*$ + type: text + value: '2' + weight: 85 + volumes_ceph: + description: Configures Cinder to store volumes in Ceph RBD images. + label: Ceph RBD for volumes (Cinder) + restrictions: + - settings:storage.volumes_lvm.value == true + type: checkbox + value: true + weight: 20 + volumes_lvm: + description: It is recommended to have at least one Storage - Cinder LVM node. + label: Cinder LVM over iSCSI for volumes + restrictions: + - settings:storage.volumes_ceph.value == true + type: checkbox + value: false + weight: 10 + syslog: + metadata: + label: Syslog + weight: 50 + syslog_port: + description: Remote syslog port + label: Port + regex: + error: Invalid syslog port + source: ^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$ + type: text + value: '514' + weight: 20 + syslog_server: + description: Remote syslog hostname + label: Hostname + type: text + value: '' + weight: 10 + syslog_transport: + label: Syslog transport protocol + type: radio + value: tcp + values: + - data: udp + description: '' + label: UDP + - data: tcp + description: '' + label: TCP + weight: 30 + workloads_collector: + enabled: + type: hidden + value: true + metadata: + label: Workloads Collector User + restrictions: + - action: hide + condition: 'true' + weight: 10 + password: + type: password + value: sbfn1c8b + tenant: + type: text + value: services + user: + type: text + value: fuel_stats_user
\ No newline at end of file diff --git a/deploy/templates/hardware_environment/conf/ericsson_montreal_lab/dha.yaml b/deploy/templates/hardware_environment/conf/ericsson_montreal_lab/dha.yaml new file mode 100644 index 000000000..ca446f680 --- /dev/null +++ b/deploy/templates/hardware_environment/conf/ericsson_montreal_lab/dha.yaml @@ -0,0 +1,54 @@ +title: Deployment Hardware Adapter (DHA) +# DHA API version supported +version: +created: +comment: Config for Ericsson Montreal Lab + +# Adapter to use for this definition +adapter: hp + +# Node list. +# Mandatory property is id, all other properties are adapter specific. + +nodes: +- id: 1 + pxeMac: 14:58:D0:54:7A:D8 + ipmiIp: 10.118.32.198 + ipmiUser: <username> + ipmiPass: <password> +- id: 2 + pxeMac: 14:58:D0:55:E2:E0 + ipmiIp: 10.118.32.202 + ipmiUser: <username> + ipmiPass: <password> +- id: 3 + pxeMac: 9C:B6:54:8A:25:C0 + ipmiIp: 10.118.32.213 + ipmiUser: <username> + ipmiPass: <password> +- id: 4 + pxeMac: 14:58:D0:54:28:80 + ipmiIp: 10.118.32.201 + ipmiUser: <username> + ipmiPass: <password> +- id: 5 + pxeMac: 14:58:D0:54:E7:88 + ipmiIp: 10.118.32.203 + ipmiUser: <username> + ipmiPass: <password> +- id: 6 + pxeMac: 14:58:D0:54:7A:28 + ipmiIp: 10.118.32.205 + ipmiUser: <username> + ipmiPass: <password> +# Adding the Fuel node as node id 7 which may not be correct - please +# adjust as needed. +- id: 7 + libvirtName: fuel-opnfv + libvirtTemplate: templates/hardware_environment/vms/fuel.xml + isFuel: yes + username: root + password: r00tme + +disks: + fuel: 50G
\ No newline at end of file diff --git a/deploy/templates/hardware_environment/conf/ericsson_montreal_lab/plugins_conf/.gitkeep b/deploy/templates/hardware_environment/conf/ericsson_montreal_lab/plugins_conf/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/deploy/templates/hardware_environment/conf/ericsson_montreal_lab/plugins_conf/.gitkeep diff --git a/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod1/dea.yaml b/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod1/dea.yaml new file mode 100644 index 000000000..e2d752198 --- /dev/null +++ b/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod1/dea.yaml @@ -0,0 +1,898 @@ +title: Deployment Environment Adapter (DEA) +# DEA API version supported +version: +created: +comment: Config for LF POD1 - HA deployment with Ceph and Opendaylight +environment: + name: opnfv + mode: ha + net_segment_type: tun +wanted_release: Kilo on Ubuntu 14.04 +nodes: +- id: 1 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 2 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 3 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 4 + interfaces: interfaces_1 + transformations: transformations_2 + role: ceph-osd,compute +- id: 5 + interfaces: interfaces_1 + transformations: transformations_2 + role: ceph-osd,compute +fuel: + ADMIN_NETWORK: + cidr: 10.20.0.0/24 + dhcp_gateway: 10.20.0.2 + dhcp_pool_end: 10.20.0.254 + dhcp_pool_start: 10.20.0.3 + ipaddress: 10.20.0.2 + netmask: 255.255.0.0 + DNS_DOMAIN: domain.tld + DNS_SEARCH: domain.tld + DNS_UPSTREAM: 8.8.8.8 + FUEL_ACCESS: + password: admin + user: admin + HOSTNAME: opnfv + NTP1: 0.pool.ntp.org + NTP2: 1.pool.ntp.org + NTP3: 2.pool.ntp.org +interfaces_1: + eth0: + - public + eth1: + - fuelweb_admin + - management + - storage + - private +transformations_1: + transformations: + - action: add-br + name: br-fw-admin + - action: add-br + name: br-mgmt + - action: add-br + name: br-storage + - action: add-br + name: br-ex + - action: add-br + name: br-floating + provider: ovs + - action: add-patch + bridges: + - br-floating + - br-ex + mtu: 65000 + provider: ovs + - action: add-br + name: br-mesh + - action: add-port + bridge: br-fw-admin + name: eth0 + - action: add-port + bridge: br-mgmt + name: eth1.300 + - action: add-port + bridge: br-storage + name: eth1.301 + - action: add-port + bridge: br-mesh + name: eth1.302 + - action: add-port + bridge: br-ex + name: eth0 +transformations_2: + transformations: + - action: add-br + name: br-fw-admin + - action: add-br + name: br-mgmt + - action: add-br + name: br-storage + - action: add-br + name: br-mesh + - action: add-port + bridge: br-fw-admin + name: eth0 + - action: add-port + bridge: br-mgmt + name: eth1.300 + - action: add-port + bridge: br-storage + name: eth1.301 + - action: add-port + bridge: br-mesh + name: eth1.302 +network: + management_vip: 192.168.0.2 + management_vrouter_vip: 192.168.0.1 + networking_parameters: + base_mac: fa:16:3e:00:00:00 + configuration_template: null + dns_nameservers: + - 8.8.4.4 + - 8.8.8.8 + floating_ranges: + - - 172.30.9.160 + - 172.30.9.254 + gre_id_range: + - 2 + - 65535 + internal_cidr: 192.168.111.0/24 + internal_gateway: 192.168.111.1 + net_l23_provider: ovs + segmentation_type: tun + vlan_range: + - 1000 + - 1030 + networks: + - cidr: 172.30.9.0/24 + gateway: 172.30.9.1 + ip_ranges: + - - 172.30.9.64 + - 172.30.9.159 + meta: + cidr: 172.16.0.0/24 + configurable: true + floating_range_var: floating_ranges + ip_range: + - 172.16.0.2 + - 172.16.0.126 + map_priority: 1 + name: public + notation: ip_ranges + render_addr_mask: public + render_type: null + use_gateway: true + vips: + - haproxy + - vrouter + vlan_start: null + name: public + vlan_start: null + - cidr: 192.168.0.0/24 + gateway: null + ip_ranges: + - - 192.168.0.1 + - 192.168.0.254 + meta: + cidr: 192.168.0.0/24 + configurable: true + map_priority: 2 + name: management + notation: cidr + render_addr_mask: internal + render_type: cidr + use_gateway: false + vips: + - haproxy + - vrouter + vlan_start: 101 + name: management + vlan_start: 300 + - cidr: 192.168.1.0/24 + gateway: null + ip_ranges: + - - 192.168.1.1 + - 192.168.1.254 + meta: + cidr: 192.168.1.0/24 + configurable: true + map_priority: 2 + name: storage + notation: cidr + render_addr_mask: storage + render_type: cidr + use_gateway: false + vlan_start: 102 + name: storage + vlan_start: 301 + - cidr: 192.168.2.0/24 + gateway: null + ip_ranges: + - - 192.168.2.1 + - 192.168.2.254 + meta: + cidr: 192.168.2.0/24 + configurable: true + map_priority: 2 + name: private + notation: cidr + render_addr_mask: null + render_type: cidr + seg_type: tun + use_gateway: false + vlan_start: 103 + name: private + vlan_start: 302 + - cidr: 10.20.0.0/16 + gateway: 10.20.0.2 + ip_ranges: + - - 10.20.0.3 + - 10.20.0.254 + meta: + configurable: false + map_priority: 0 + notation: ip_ranges + render_addr_mask: null + render_type: null + unmovable: true + use_gateway: true + name: fuelweb_admin + vlan_start: null + public_vip: 172.30.9.64 + public_vrouter_vip: 172.30.9.65 + vips: + management: + ipaddr: 192.168.0.2 + namespace: haproxy + network_role: mgmt/vip + node_roles: + - controller + - primary-controller + public: + ipaddr: 172.30.9.64 + namespace: haproxy + network_role: public/vip + node_roles: + - controller + - primary-controller + vrouter: + ipaddr: 192.168.0.1 + namespace: vrouter + network_role: mgmt/vip + node_roles: + - controller + - primary-controller + vrouter_pub: + ipaddr: 172.30.9.65 + namespace: vrouter + network_role: public/vip + node_roles: + - controller + - primary-controller +settings: + editable: + access: + email: + description: Email address for Administrator + label: Email + regex: + error: Invalid email + source: ^\S+@\S+$ + type: text + value: admin@localhost + weight: 40 + metadata: + label: Access + weight: 10 + password: + description: Password for Administrator + label: Password + regex: + error: Empty password + source: \S + type: password + value: admin + weight: 20 + tenant: + description: Tenant (project) name for Administrator + label: Tenant + regex: + error: Invalid tenant name + source: ^(?!services$)(?!nova$)(?!glance$)(?!keystone$)(?!neutron$)(?!cinder$)(?!swift$)(?!ceph$)(?![Gg]uest$)(?!.* + +.*$).+ + type: text + value: admin + weight: 30 + user: + description: Username for Administrator + label: Username + regex: + error: Invalid username + source: ^(?!services$)(?!nova$)(?!glance$)(?!keystone$)(?!neutron$)(?!cinder$)(?!swift$)(?!ceph$)(?![Gg]uest$)(?!.* + +.*$).+ + type: text + value: admin + weight: 10 + additional_components: + ceilometer: + description: If selected, Ceilometer component will be installed + label: Install Ceilometer + type: checkbox + value: false + weight: 40 + heat: + description: '' + label: '' + type: hidden + value: true + weight: 30 + metadata: + label: Additional Components + weight: 20 + mongo: + description: If selected, You can use external Mongo DB as ceilometer backend + label: Use external Mongo DB + restrictions: + - settings:additional_components.ceilometer.value == false + type: checkbox + value: false + weight: 40 + murano: + description: If selected, Murano component will be installed + label: Install Murano + type: checkbox + value: false + weight: 20 + sahara: + description: If selected, Sahara component will be installed + label: Install Sahara + type: checkbox + value: false + weight: 10 + common: + auth_key: + description: Public key(s) to include in authorized_keys on deployed nodes + label: Public Key + type: textarea + value: '' + weight: 70 + auto_assign_floating_ip: + description: If selected, OpenStack will automatically assign a floating IP + to a new instance + label: Auto assign floating IP + restrictions: + - action: hide + condition: cluster:net_provider == 'neutron' + type: checkbox + value: false + weight: 40 + debug: + description: Debug logging mode provides more information, but requires more + disk space. + label: OpenStack debug logging + type: checkbox + value: false + weight: 20 + libvirt_type: + label: Hypervisor type + type: radio + value: kvm + values: + - data: kvm + description: Choose this type of hypervisor if you run OpenStack on hardware + label: KVM + - data: qemu + description: Choose this type of hypervisor if you run OpenStack on virtual + hosts. + label: QEMU + weight: 30 + metadata: + label: Common + weight: 30 + nova_quota: + description: Quotas are used to limit CPU and memory usage for tenants. Enabling + quotas will increase load on the Nova database. + label: Nova quotas + type: checkbox + value: false + weight: 25 + puppet_debug: + description: Debug puppet logging mode provides more information, but requires + more disk space. + label: Puppet debug logging + type: checkbox + value: true + weight: 20 + resume_guests_state_on_host_boot: + description: Whether to resume previous guests state when the host reboots. + If enabled, this option causes guests assigned to the host to resume their + previous state. If the guest was running a restart will be attempted when + nova-compute starts. If the guest was not running previously, a restart will + not be attempted. + label: Resume guests state on host boot + type: checkbox + value: true + weight: 60 + use_cow_images: + description: For most cases you will want qcow format. If it's disabled, raw + image format will be used to run VMs. OpenStack with raw format currently + does not support snapshotting. + label: Use qcow format for images + type: checkbox + value: true + weight: 50 + use_vcenter: + restrictions: + - condition: cluster:net_provider == 'nova_network' + type: hidden + value: false + weight: 30 + corosync: + group: + description: '' + label: Group + type: text + value: 226.94.1.1 + weight: 10 + metadata: + label: Corosync + restrictions: + - action: hide + condition: 'true' + weight: 50 + port: + description: '' + label: Port + type: text + value: '12000' + weight: 20 + verified: + description: Set True only if multicast is configured correctly on router. + label: Need to pass network verification. + type: checkbox + value: false + weight: 10 + external_dns: + dns_list: + description: List of upstream DNS servers, separated by comma + label: DNS list + regex: + error: Invalid IP address list + source: ^\*$|^(?:\d|1?\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|1?\d\d|2[0-4]\d|25[0-5])){3}(?:\s*,\s*(?:\d|1?\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|1?\d\d|2[0-4]\d|25[0-5])){3})*$ + type: text + value: 8.8.8.8, 8.8.4.4 + weight: 10 + metadata: + label: Host OS DNS Servers + weight: 90 + external_mongo: + hosts_ip: + description: IP Addresses of MongoDB. Use comma to split IPs + label: MongoDB hosts IP + regex: + error: Invalid hosts ip sequence + source: ^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?),)*((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ + type: text + value: '' + weight: 30 + metadata: + label: External MongoDB + restrictions: + - action: hide + condition: settings:additional_components.mongo.value == false + message: Ceilometer and MongoDB are not enabled on the Additional Components + section + weight: 20 + mongo_db_name: + description: Mongo database name + label: Database name + regex: + error: Invalid database name + source: ^\w+$ + type: text + value: ceilometer + weight: 30 + mongo_password: + description: Mongo database password + label: Password + regex: + error: Password contains spaces + source: ^\S*$ + type: password + value: ceilometer + weight: 30 + mongo_replset: + description: Name for Mongo replication set + label: Replset + type: text + value: '' + weight: 30 + mongo_user: + description: Mongo database username + label: Username + regex: + error: Empty username + source: ^\w+$ + type: text + value: ceilometer + weight: 30 + external_ntp: + metadata: + label: Host OS NTP Servers + weight: 100 + ntp_list: + description: List of upstream NTP servers, separated by comma + label: NTP server list + regex: + error: Invalid NTP server list + source: ^\s*(?:(?:\w+(?:-+\w+)*\.)+[a-z]+|\d{1,3}(?:\.\d{1,3}){3})\s*(?:,\s*(?:(?:\w+(?:-+\w+)*\.)+[a-z]+|\d{1,3}(\.\d{1,3}){3})\s*)*$ + type: text + value: 0.pool.ntp.org, 1.pool.ntp.org, 2.pool.ntp.org + weight: 10 + kernel_params: + kernel: + description: Default kernel parameters + label: Initial parameters + type: text + value: console=tty0 net.ifnames=0 biosdevname=0 rootdelay=90 nomodeset + weight: 45 + metadata: + label: Kernel parameters + weight: 40 + murano_settings: + metadata: + label: Murano Settings + restrictions: + - action: hide + condition: settings:additional_components.murano.value == false + message: Murano is not enabled on the Additional Components section + weight: 20 + murano_repo_url: + description: '' + label: Murano Repository URL + type: text + value: http://storage.apps.openstack.org/ + weight: 10 + neutron_advanced_configuration: + metadata: + label: Neutron Advanced Configuration + restrictions: + - action: hide + condition: cluster:net_provider != 'neutron' + weight: 45 + neutron_dvr: + description: Enable Distributed Virtual Routers in Neutron + label: Neutron DVR + restrictions: + - ? networking_parameters:segmentation_type != 'vlan' and settings:neutron_advanced_configuration.neutron_l2_pop.value + == false + : DVR requires L2 population to be enabled. + type: checkbox + value: false + weight: 20 + neutron_l2_pop: + description: Enable L2 population mechanism in Neutron + label: Neutron L2 population + restrictions: + - action: hide + condition: networking_parameters:segmentation_type == 'vlan' + type: checkbox + value: false + weight: 10 + neutron_mellanox: + metadata: + enabled: true + label: Mellanox Neutron components + restrictions: + - action: hide + condition: not ('experimental' in version:feature_groups) + toggleable: false + weight: 50 + plugin: + label: Mellanox drivers and SR-IOV plugin + type: radio + value: disabled + values: + - data: disabled + description: If selected, Mellanox drivers, Neutron and Cinder plugin will + not be installed. + label: Mellanox drivers and plugins disabled + restrictions: + - settings:storage.iser.value == true + - data: drivers_only + description: If selected, Mellanox Ethernet drivers will be installed to support + networking over Mellanox NIC. Mellanox Neutron plugin will not be installed. + label: Install only Mellanox drivers + restrictions: + - settings:common.libvirt_type.value != 'kvm' + - data: ethernet + description: If selected, both Mellanox Ethernet drivers and Mellanox network + acceleration (Neutron) plugin will be installed. + label: Install Mellanox drivers and SR-IOV plugin + restrictions: + - settings:common.libvirt_type.value != 'kvm' or not (cluster:net_provider + == 'neutron' and networking_parameters:segmentation_type == 'vlan') + weight: 60 + vf_num: + description: Note that one virtual function will be reserved to the storage + network, in case of choosing iSER. + label: Number of virtual NICs + restrictions: + - settings:neutron_mellanox.plugin.value != 'ethernet' + type: text + value: '16' + weight: 70 + provision: + metadata: + label: Provision + restrictions: + - action: hide + condition: 'true' + weight: 80 + method: + type: hidden + value: image + public_network_assignment: + assign_to_all_nodes: + description: When disabled, public network will be assigned to controllers only + label: Assign public network to all nodes + type: checkbox + value: false + weight: 10 + metadata: + label: Public network assignment + restrictions: + - action: hide + condition: cluster:net_provider != 'neutron' + weight: 50 + public_ssl: + cert_data: + description: Certificate and private key data, concatenated into a single file + label: Certificate + restrictions: + - action: hide + condition: (settings:public_ssl.cert_source.value != 'user_uploaded') or (settings:public_ssl.horizon.value + == false and settings:public_ssl.services.value == false) + type: file + value: '' + weight: 40 + cert_source: + description: From where we'll get certificate and private key + label: Select source for certificate + restrictions: + - action: hide + condition: settings:public_ssl.horizon.value == false and settings:public_ssl.services.value + == false + type: radio + value: self_signed + values: + - data: self_signed + description: Generate private key and certificate that will be signed by this + key + label: Self-signed + - data: user_uploaded + description: Use pre-generated key and certificate + label: I have my own keypair with certificate + weight: 30 + horizon: + description: Secure access to Horizon enabling HTTPS instead of HTTP + label: HTTPS for Horizon + type: checkbox + value: false + weight: 10 + hostname: + description: Your DNS entries should point to this name. Self-signed certificates + also will use this hostname + label: DNS hostname for public TLS endpoints + restrictions: + - action: hide + condition: settings:public_ssl.horizon.value == false and settings:public_ssl.services.value + == false + type: text + value: public.fuel.local + weight: 50 + metadata: + label: Public TLS + weight: 110 + services: + description: Enable TLS termination on HAProxy for OpenStack services + label: TLS for OpenStack public endpoints + type: checkbox + value: false + weight: 20 + repo_setup: + metadata: + always_editable: true + label: Repositories + weight: 50 + repos: + description: 'Please note: the first repository will be considered the operating + system mirror that will be used during node provisioning. + + To create a local repository mirror on the Fuel master node, please follow + the instructions provided by running "fuel-createmirror --help" on the Fuel + master node. + + Please make sure your Fuel master node has Internet access to the repository + before attempting to create a mirror. + + For more details, please refer to the documentation (https://docs.mirantis.com/openstack/fuel/fuel-7.0/operations.html#external-ubuntu-ops). + + ' + extra_priority: null + type: custom_repo_configuration + value: + - name: ubuntu + priority: null + section: main universe multiverse + suite: trusty + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: ubuntu-updates + priority: null + section: main universe multiverse + suite: trusty-updates + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: ubuntu-security + priority: null + section: main universe multiverse + suite: trusty-security + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: mos + priority: 1050 + section: main restricted + suite: mos7.0 + type: deb + uri: http://10.20.0.2:8080/2015.1.0-7.0/ubuntu/x86_64 + - name: mos-updates + priority: 1050 + section: main restricted + suite: mos7.0-updates + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: mos-security + priority: 1050 + section: main restricted + suite: mos7.0-security + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: mos-holdback + priority: 1100 + section: main restricted + suite: mos7.0-holdback + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: Auxiliary + priority: 1150 + section: main restricted + suite: auxiliary + type: deb + uri: http://10.20.0.2:8080/2015.1.0-7.0/ubuntu/auxiliary + storage: + ephemeral_ceph: + description: Configures Nova to store ephemeral volumes in RBD. This works best + if Ceph is enabled for volumes and images, too. Enables live migration of + all types of Ceph backed VMs (without this option, live migration will only + work with VMs launched from Cinder volumes). + label: Ceph RBD for ephemeral volumes (Nova) + type: checkbox + value: true + weight: 75 + images_ceph: + description: Configures Glance to use the Ceph RBD backend to store images. + If enabled, this option will prevent Swift from installing. + label: Ceph RBD for images (Glance) + restrictions: + - settings:storage.images_vcenter.value == true: Only one Glance backend could + be selected. + type: checkbox + value: true + weight: 30 + images_vcenter: + description: Configures Glance to use the vCenter/ESXi backend to store images. + If enabled, this option will prevent Swift from installing. + label: VMWare vCenter/ESXi datastore for images (Glance) + restrictions: + - action: hide + condition: settings:common.use_vcenter.value != true + - condition: settings:storage.images_ceph.value == true + message: Only one Glance backend could be selected. + type: checkbox + value: false + weight: 35 + iser: + description: 'High performance block storage: Cinder volumes over iSER protocol + (iSCSI over RDMA). This feature requires SR-IOV capabilities in the NIC, and + will use a dedicated virtual function for the storage network.' + label: iSER protocol for volumes (Cinder) + restrictions: + - settings:storage.volumes_lvm.value != true or settings:common.libvirt_type.value + != 'kvm' + - action: hide + condition: not ('experimental' in version:feature_groups) + type: checkbox + value: false + weight: 11 + metadata: + label: Storage + weight: 60 + objects_ceph: + description: Configures RadosGW front end for Ceph RBD. This exposes S3 and + Swift API Interfaces. If enabled, this option will prevent Swift from installing. + label: Ceph RadosGW for objects (Swift API) + restrictions: + - settings:storage.images_ceph.value == false + type: checkbox + value: false + weight: 80 + osd_pool_size: + description: Configures the default number of object replicas in Ceph. This + number must be equal to or lower than the number of deployed 'Storage - Ceph + OSD' nodes. + label: Ceph object replication factor + regex: + error: Invalid number + source: ^[1-9]\d*$ + type: text + value: '2' + weight: 85 + volumes_ceph: + description: Configures Cinder to store volumes in Ceph RBD images. + label: Ceph RBD for volumes (Cinder) + restrictions: + - settings:storage.volumes_lvm.value == true + type: checkbox + value: true + weight: 20 + volumes_lvm: + description: It is recommended to have at least one Storage - Cinder LVM node. + label: Cinder LVM over iSCSI for volumes + restrictions: + - settings:storage.volumes_ceph.value == true + type: checkbox + value: false + weight: 10 + syslog: + metadata: + label: Syslog + weight: 50 + syslog_port: + description: Remote syslog port + label: Port + regex: + error: Invalid syslog port + source: ^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$ + type: text + value: '514' + weight: 20 + syslog_server: + description: Remote syslog hostname + label: Hostname + type: text + value: '' + weight: 10 + syslog_transport: + label: Syslog transport protocol + type: radio + value: tcp + values: + - data: udp + description: '' + label: UDP + - data: tcp + description: '' + label: TCP + weight: 30 + workloads_collector: + enabled: + type: hidden + value: true + metadata: + label: Workloads Collector User + restrictions: + - action: hide + condition: 'true' + weight: 10 + password: + type: password + value: sbfn1c8b + tenant: + type: text + value: services + user: + type: text + value: fuel_stats_user
\ No newline at end of file diff --git a/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod1/dha.yaml b/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod1/dha.yaml new file mode 100644 index 000000000..724d6d833 --- /dev/null +++ b/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod1/dha.yaml @@ -0,0 +1,49 @@ +title: Deployment Hardware Adapter (DHA) +# DHA API version supported +version: +created: +comment: Config for LF POD1 + +# Adapter to use for this definition +adapter: ipmi + +# Node list. +# Mandatory property is id, all other properties are adapter specific. + +nodes: +- id: 1 + pxeMac: 00:25:b5:b0:00:ef + ipmiIp: 172.30.8.69 + ipmiUser: admin + ipmiPass: octopus +- id: 2 + pxeMac: 00:25:b5:b0:00:cf + ipmiIp: 172.30.8.78 + ipmiUser: admin + ipmiPass: octopus +- id: 3 + pxeMac: 00:25:b5:b0:00:8f + ipmiIp: 172.30.8.68 + ipmiUser: admin + ipmiPass: octopus +- id: 4 + pxeMac: 00:25:b5:b0:00:6f + ipmiIp: 172.30.8.77 + ipmiUser: admin + ipmiPass: octopus +- id: 5 + pxeMac: 00:25:b5:b0:00:4f + ipmiIp: 172.30.8.67 + ipmiUser: admin + ipmiPass: octopus +# Adding the Fuel node as node id 6 which may not be correct - please +# adjust as needed. +- id: 6 + libvirtName: fuel-opnfv + libvirtTemplate: templates/hardware_environment/vms/fuel.xml + isFuel: yes + username: root + password: r00tme + +disks: + fuel: 50G
\ No newline at end of file diff --git a/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod1/plugins_conf/.gitkeep b/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod1/plugins_conf/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod1/plugins_conf/.gitkeep diff --git a/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod2/dea.yaml b/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod2/dea.yaml new file mode 100644 index 000000000..64362793f --- /dev/null +++ b/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod2/dea.yaml @@ -0,0 +1,898 @@ +title: Deployment Environment Adapter (DEA) +# DEA API version supported +version: +created: +comment: Config for LF POD2 - HA deployment with Ceph +environment: + name: opnfv + mode: ha + net_segment_type: tun +wanted_release: Kilo on Ubuntu 14.04 +nodes: +- id: 1 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 2 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 3 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 4 + interfaces: interfaces_1 + transformations: transformations_2 + role: ceph-osd,compute +- id: 5 + interfaces: interfaces_1 + transformations: transformations_2 + role: ceph-osd,compute +fuel: + ADMIN_NETWORK: + cidr: 10.20.0.0/24 + dhcp_gateway: 10.20.0.2 + dhcp_pool_end: 10.20.0.254 + dhcp_pool_start: 10.20.0.3 + ipaddress: 10.20.0.2 + netmask: 255.255.0.0 + DNS_DOMAIN: domain.tld + DNS_SEARCH: domain.tld + DNS_UPSTREAM: 8.8.8.8 + FUEL_ACCESS: + password: admin + user: admin + HOSTNAME: opnfv + NTP1: 0.pool.ntp.org + NTP2: 1.pool.ntp.org + NTP3: 2.pool.ntp.org +interfaces_1: + eth0: + - fuelweb_admin + - management + - storage + - private + eth2: + - public +transformations_1: + transformations: + - action: add-br + name: br-fw-admin + - action: add-br + name: br-mgmt + - action: add-br + name: br-storage + - action: add-br + name: br-ex + - action: add-br + name: br-floating + provider: ovs + - action: add-patch + bridges: + - br-floating + - br-ex + mtu: 65000 + provider: ovs + - action: add-br + name: br-mesh + - action: add-port + bridge: br-fw-admin + name: eth0 + - action: add-port + bridge: br-mgmt + name: eth0.300 + - action: add-port + bridge: br-storage + name: eth0.301 + - action: add-port + bridge: br-mesh + name: eth0.302 + - action: add-port + bridge: br-ex + name: eth2 +transformations_2: + transformations: + - action: add-br + name: br-fw-admin + - action: add-br + name: br-mgmt + - action: add-br + name: br-storage + - action: add-br + name: br-mesh + - action: add-port + bridge: br-fw-admin + name: eth0 + - action: add-port + bridge: br-mgmt + name: eth0.300 + - action: add-port + bridge: br-storage + name: eth0.301 + - action: add-port + bridge: br-mesh + name: eth0.302 +network: + management_vip: 192.168.0.2 + management_vrouter_vip: 192.168.0.1 + networking_parameters: + base_mac: fa:16:3e:00:00:00 + configuration_template: null + dns_nameservers: + - 8.8.4.4 + - 8.8.8.8 + floating_ranges: + - - 172.30.10.160 + - 172.30.10.254 + gre_id_range: + - 2 + - 65535 + internal_cidr: 192.168.111.0/24 + internal_gateway: 192.168.111.1 + net_l23_provider: ovs + segmentation_type: tun + vlan_range: + - 1000 + - 1030 + networks: + - cidr: 172.30.10.0/24 + gateway: 172.30.10.1 + ip_ranges: + - - 172.30.10.64 + - 172.30.10.159 + meta: + cidr: 172.16.0.0/24 + configurable: true + floating_range_var: floating_ranges + ip_range: + - 172.16.0.2 + - 172.16.0.126 + map_priority: 1 + name: public + notation: ip_ranges + render_addr_mask: public + render_type: null + use_gateway: true + vips: + - haproxy + - vrouter + vlan_start: null + name: public + vlan_start: null + - cidr: 192.168.0.0/24 + gateway: null + ip_ranges: + - - 192.168.0.1 + - 192.168.0.254 + meta: + cidr: 192.168.0.0/24 + configurable: true + map_priority: 2 + name: management + notation: cidr + render_addr_mask: internal + render_type: cidr + use_gateway: false + vips: + - haproxy + - vrouter + vlan_start: 101 + name: management + vlan_start: 300 + - cidr: 192.168.1.0/24 + gateway: null + ip_ranges: + - - 192.168.1.1 + - 192.168.1.254 + meta: + cidr: 192.168.1.0/24 + configurable: true + map_priority: 2 + name: storage + notation: cidr + render_addr_mask: storage + render_type: cidr + use_gateway: false + vlan_start: 102 + name: storage + vlan_start: 301 + - cidr: 192.168.2.0/24 + gateway: null + ip_ranges: + - - 192.168.2.1 + - 192.168.2.254 + meta: + cidr: 192.168.2.0/24 + configurable: true + map_priority: 2 + name: private + notation: cidr + render_addr_mask: null + render_type: cidr + seg_type: tun + use_gateway: false + vlan_start: 103 + name: private + vlan_start: 302 + - cidr: 10.20.0.0/16 + gateway: 10.20.0.2 + ip_ranges: + - - 10.20.0.3 + - 10.20.0.254 + meta: + configurable: false + map_priority: 0 + notation: ip_ranges + render_addr_mask: null + render_type: null + unmovable: true + use_gateway: true + name: fuelweb_admin + vlan_start: null + public_vip: 172.30.10.64 + public_vrouter_vip: 172.30.10.65 + vips: + management: + ipaddr: 192.168.0.2 + namespace: haproxy + network_role: mgmt/vip + node_roles: + - controller + - primary-controller + public: + ipaddr: 172.30.10.64 + namespace: haproxy + network_role: public/vip + node_roles: + - controller + - primary-controller + vrouter: + ipaddr: 192.168.0.1 + namespace: vrouter + network_role: mgmt/vip + node_roles: + - controller + - primary-controller + vrouter_pub: + ipaddr: 172.30.10.65 + namespace: vrouter + network_role: public/vip + node_roles: + - controller + - primary-controller +settings: + editable: + access: + email: + description: Email address for Administrator + label: Email + regex: + error: Invalid email + source: ^\S+@\S+$ + type: text + value: admin@localhost + weight: 40 + metadata: + label: Access + weight: 10 + password: + description: Password for Administrator + label: Password + regex: + error: Empty password + source: \S + type: password + value: admin + weight: 20 + tenant: + description: Tenant (project) name for Administrator + label: Tenant + regex: + error: Invalid tenant name + source: ^(?!services$)(?!nova$)(?!glance$)(?!keystone$)(?!neutron$)(?!cinder$)(?!swift$)(?!ceph$)(?![Gg]uest$)(?!.* + +.*$).+ + type: text + value: admin + weight: 30 + user: + description: Username for Administrator + label: Username + regex: + error: Invalid username + source: ^(?!services$)(?!nova$)(?!glance$)(?!keystone$)(?!neutron$)(?!cinder$)(?!swift$)(?!ceph$)(?![Gg]uest$)(?!.* + +.*$).+ + type: text + value: admin + weight: 10 + additional_components: + ceilometer: + description: If selected, Ceilometer component will be installed + label: Install Ceilometer + type: checkbox + value: false + weight: 40 + heat: + description: '' + label: '' + type: hidden + value: true + weight: 30 + metadata: + label: Additional Components + weight: 20 + mongo: + description: If selected, You can use external Mongo DB as ceilometer backend + label: Use external Mongo DB + restrictions: + - settings:additional_components.ceilometer.value == false + type: checkbox + value: false + weight: 40 + murano: + description: If selected, Murano component will be installed + label: Install Murano + type: checkbox + value: false + weight: 20 + sahara: + description: If selected, Sahara component will be installed + label: Install Sahara + type: checkbox + value: false + weight: 10 + common: + auth_key: + description: Public key(s) to include in authorized_keys on deployed nodes + label: Public Key + type: textarea + value: '' + weight: 70 + auto_assign_floating_ip: + description: If selected, OpenStack will automatically assign a floating IP + to a new instance + label: Auto assign floating IP + restrictions: + - action: hide + condition: cluster:net_provider == 'neutron' + type: checkbox + value: false + weight: 40 + debug: + description: Debug logging mode provides more information, but requires more + disk space. + label: OpenStack debug logging + type: checkbox + value: false + weight: 20 + libvirt_type: + label: Hypervisor type + type: radio + value: kvm + values: + - data: kvm + description: Choose this type of hypervisor if you run OpenStack on hardware + label: KVM + - data: qemu + description: Choose this type of hypervisor if you run OpenStack on virtual + hosts. + label: QEMU + weight: 30 + metadata: + label: Common + weight: 30 + nova_quota: + description: Quotas are used to limit CPU and memory usage for tenants. Enabling + quotas will increase load on the Nova database. + label: Nova quotas + type: checkbox + value: false + weight: 25 + puppet_debug: + description: Debug puppet logging mode provides more information, but requires + more disk space. + label: Puppet debug logging + type: checkbox + value: true + weight: 20 + resume_guests_state_on_host_boot: + description: Whether to resume previous guests state when the host reboots. + If enabled, this option causes guests assigned to the host to resume their + previous state. If the guest was running a restart will be attempted when + nova-compute starts. If the guest was not running previously, a restart will + not be attempted. + label: Resume guests state on host boot + type: checkbox + value: true + weight: 60 + use_cow_images: + description: For most cases you will want qcow format. If it's disabled, raw + image format will be used to run VMs. OpenStack with raw format currently + does not support snapshotting. + label: Use qcow format for images + type: checkbox + value: true + weight: 50 + use_vcenter: + restrictions: + - condition: cluster:net_provider == 'nova_network' + type: hidden + value: false + weight: 30 + corosync: + group: + description: '' + label: Group + type: text + value: 226.94.1.1 + weight: 10 + metadata: + label: Corosync + restrictions: + - action: hide + condition: 'true' + weight: 50 + port: + description: '' + label: Port + type: text + value: '12000' + weight: 20 + verified: + description: Set True only if multicast is configured correctly on router. + label: Need to pass network verification. + type: checkbox + value: false + weight: 10 + external_dns: + dns_list: + description: List of upstream DNS servers, separated by comma + label: DNS list + regex: + error: Invalid IP address list + source: ^\*$|^(?:\d|1?\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|1?\d\d|2[0-4]\d|25[0-5])){3}(?:\s*,\s*(?:\d|1?\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|1?\d\d|2[0-4]\d|25[0-5])){3})*$ + type: text + value: 8.8.8.8, 8.8.4.4 + weight: 10 + metadata: + label: Host OS DNS Servers + weight: 90 + external_mongo: + hosts_ip: + description: IP Addresses of MongoDB. Use comma to split IPs + label: MongoDB hosts IP + regex: + error: Invalid hosts ip sequence + source: ^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?),)*((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ + type: text + value: '' + weight: 30 + metadata: + label: External MongoDB + restrictions: + - action: hide + condition: settings:additional_components.mongo.value == false + message: Ceilometer and MongoDB are not enabled on the Additional Components + section + weight: 20 + mongo_db_name: + description: Mongo database name + label: Database name + regex: + error: Invalid database name + source: ^\w+$ + type: text + value: ceilometer + weight: 30 + mongo_password: + description: Mongo database password + label: Password + regex: + error: Password contains spaces + source: ^\S*$ + type: password + value: ceilometer + weight: 30 + mongo_replset: + description: Name for Mongo replication set + label: Replset + type: text + value: '' + weight: 30 + mongo_user: + description: Mongo database username + label: Username + regex: + error: Empty username + source: ^\w+$ + type: text + value: ceilometer + weight: 30 + external_ntp: + metadata: + label: Host OS NTP Servers + weight: 100 + ntp_list: + description: List of upstream NTP servers, separated by comma + label: NTP server list + regex: + error: Invalid NTP server list + source: ^\s*(?:(?:\w+(?:-+\w+)*\.)+[a-z]+|\d{1,3}(?:\.\d{1,3}){3})\s*(?:,\s*(?:(?:\w+(?:-+\w+)*\.)+[a-z]+|\d{1,3}(\.\d{1,3}){3})\s*)*$ + type: text + value: 0.pool.ntp.org, 1.pool.ntp.org, 2.pool.ntp.org + weight: 10 + kernel_params: + kernel: + description: Default kernel parameters + label: Initial parameters + type: text + value: console=tty0 net.ifnames=0 biosdevname=0 rootdelay=90 nomodeset + weight: 45 + metadata: + label: Kernel parameters + weight: 40 + murano_settings: + metadata: + label: Murano Settings + restrictions: + - action: hide + condition: settings:additional_components.murano.value == false + message: Murano is not enabled on the Additional Components section + weight: 20 + murano_repo_url: + description: '' + label: Murano Repository URL + type: text + value: http://storage.apps.openstack.org/ + weight: 10 + neutron_advanced_configuration: + metadata: + label: Neutron Advanced Configuration + restrictions: + - action: hide + condition: cluster:net_provider != 'neutron' + weight: 45 + neutron_dvr: + description: Enable Distributed Virtual Routers in Neutron + label: Neutron DVR + restrictions: + - ? networking_parameters:segmentation_type != 'vlan' and settings:neutron_advanced_configuration.neutron_l2_pop.value + == false + : DVR requires L2 population to be enabled. + type: checkbox + value: false + weight: 20 + neutron_l2_pop: + description: Enable L2 population mechanism in Neutron + label: Neutron L2 population + restrictions: + - action: hide + condition: networking_parameters:segmentation_type == 'vlan' + type: checkbox + value: false + weight: 10 + neutron_mellanox: + metadata: + enabled: true + label: Mellanox Neutron components + restrictions: + - action: hide + condition: not ('experimental' in version:feature_groups) + toggleable: false + weight: 50 + plugin: + label: Mellanox drivers and SR-IOV plugin + type: radio + value: disabled + values: + - data: disabled + description: If selected, Mellanox drivers, Neutron and Cinder plugin will + not be installed. + label: Mellanox drivers and plugins disabled + restrictions: + - settings:storage.iser.value == true + - data: drivers_only + description: If selected, Mellanox Ethernet drivers will be installed to support + networking over Mellanox NIC. Mellanox Neutron plugin will not be installed. + label: Install only Mellanox drivers + restrictions: + - settings:common.libvirt_type.value != 'kvm' + - data: ethernet + description: If selected, both Mellanox Ethernet drivers and Mellanox network + acceleration (Neutron) plugin will be installed. + label: Install Mellanox drivers and SR-IOV plugin + restrictions: + - settings:common.libvirt_type.value != 'kvm' or not (cluster:net_provider + == 'neutron' and networking_parameters:segmentation_type == 'vlan') + weight: 60 + vf_num: + description: Note that one virtual function will be reserved to the storage + network, in case of choosing iSER. + label: Number of virtual NICs + restrictions: + - settings:neutron_mellanox.plugin.value != 'ethernet' + type: text + value: '16' + weight: 70 + provision: + metadata: + label: Provision + restrictions: + - action: hide + condition: 'true' + weight: 80 + method: + type: hidden + value: image + public_network_assignment: + assign_to_all_nodes: + description: When disabled, public network will be assigned to controllers only + label: Assign public network to all nodes + type: checkbox + value: false + weight: 10 + metadata: + label: Public network assignment + restrictions: + - action: hide + condition: cluster:net_provider != 'neutron' + weight: 50 + public_ssl: + cert_data: + description: Certificate and private key data, concatenated into a single file + label: Certificate + restrictions: + - action: hide + condition: (settings:public_ssl.cert_source.value != 'user_uploaded') or (settings:public_ssl.horizon.value + == false and settings:public_ssl.services.value == false) + type: file + value: '' + weight: 40 + cert_source: + description: From where we'll get certificate and private key + label: Select source for certificate + restrictions: + - action: hide + condition: settings:public_ssl.horizon.value == false and settings:public_ssl.services.value + == false + type: radio + value: self_signed + values: + - data: self_signed + description: Generate private key and certificate that will be signed by this + key + label: Self-signed + - data: user_uploaded + description: Use pre-generated key and certificate + label: I have my own keypair with certificate + weight: 30 + horizon: + description: Secure access to Horizon enabling HTTPS instead of HTTP + label: HTTPS for Horizon + type: checkbox + value: false + weight: 10 + hostname: + description: Your DNS entries should point to this name. Self-signed certificates + also will use this hostname + label: DNS hostname for public TLS endpoints + restrictions: + - action: hide + condition: settings:public_ssl.horizon.value == false and settings:public_ssl.services.value + == false + type: text + value: public.fuel.local + weight: 50 + metadata: + label: Public TLS + weight: 110 + services: + description: Enable TLS termination on HAProxy for OpenStack services + label: TLS for OpenStack public endpoints + type: checkbox + value: false + weight: 20 + repo_setup: + metadata: + always_editable: true + label: Repositories + weight: 50 + repos: + description: 'Please note: the first repository will be considered the operating + system mirror that will be used during node provisioning. + + To create a local repository mirror on the Fuel master node, please follow + the instructions provided by running "fuel-createmirror --help" on the Fuel + master node. + + Please make sure your Fuel master node has Internet access to the repository + before attempting to create a mirror. + + For more details, please refer to the documentation (https://docs.mirantis.com/openstack/fuel/fuel-7.0/operations.html#external-ubuntu-ops). + + ' + extra_priority: null + type: custom_repo_configuration + value: + - name: ubuntu + priority: null + section: main universe multiverse + suite: trusty + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: ubuntu-updates + priority: null + section: main universe multiverse + suite: trusty-updates + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: ubuntu-security + priority: null + section: main universe multiverse + suite: trusty-security + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: mos + priority: 1050 + section: main restricted + suite: mos7.0 + type: deb + uri: http://10.20.0.2:8080/2015.1.0-7.0/ubuntu/x86_64 + - name: mos-updates + priority: 1050 + section: main restricted + suite: mos7.0-updates + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: mos-security + priority: 1050 + section: main restricted + suite: mos7.0-security + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: mos-holdback + priority: 1100 + section: main restricted + suite: mos7.0-holdback + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: Auxiliary + priority: 1150 + section: main restricted + suite: auxiliary + type: deb + uri: http://10.20.0.2:8080/2015.1.0-7.0/ubuntu/auxiliary + storage: + ephemeral_ceph: + description: Configures Nova to store ephemeral volumes in RBD. This works best + if Ceph is enabled for volumes and images, too. Enables live migration of + all types of Ceph backed VMs (without this option, live migration will only + work with VMs launched from Cinder volumes). + label: Ceph RBD for ephemeral volumes (Nova) + type: checkbox + value: true + weight: 75 + images_ceph: + description: Configures Glance to use the Ceph RBD backend to store images. + If enabled, this option will prevent Swift from installing. + label: Ceph RBD for images (Glance) + restrictions: + - settings:storage.images_vcenter.value == true: Only one Glance backend could + be selected. + type: checkbox + value: true + weight: 30 + images_vcenter: + description: Configures Glance to use the vCenter/ESXi backend to store images. + If enabled, this option will prevent Swift from installing. + label: VMWare vCenter/ESXi datastore for images (Glance) + restrictions: + - action: hide + condition: settings:common.use_vcenter.value != true + - condition: settings:storage.images_ceph.value == true + message: Only one Glance backend could be selected. + type: checkbox + value: false + weight: 35 + iser: + description: 'High performance block storage: Cinder volumes over iSER protocol + (iSCSI over RDMA). This feature requires SR-IOV capabilities in the NIC, and + will use a dedicated virtual function for the storage network.' + label: iSER protocol for volumes (Cinder) + restrictions: + - settings:storage.volumes_lvm.value != true or settings:common.libvirt_type.value + != 'kvm' + - action: hide + condition: not ('experimental' in version:feature_groups) + type: checkbox + value: false + weight: 11 + metadata: + label: Storage + weight: 60 + objects_ceph: + description: Configures RadosGW front end for Ceph RBD. This exposes S3 and + Swift API Interfaces. If enabled, this option will prevent Swift from installing. + label: Ceph RadosGW for objects (Swift API) + restrictions: + - settings:storage.images_ceph.value == false + type: checkbox + value: false + weight: 80 + osd_pool_size: + description: Configures the default number of object replicas in Ceph. This + number must be equal to or lower than the number of deployed 'Storage - Ceph + OSD' nodes. + label: Ceph object replication factor + regex: + error: Invalid number + source: ^[1-9]\d*$ + type: text + value: '2' + weight: 85 + volumes_ceph: + description: Configures Cinder to store volumes in Ceph RBD images. + label: Ceph RBD for volumes (Cinder) + restrictions: + - settings:storage.volumes_lvm.value == true + type: checkbox + value: true + weight: 20 + volumes_lvm: + description: It is recommended to have at least one Storage - Cinder LVM node. + label: Cinder LVM over iSCSI for volumes + restrictions: + - settings:storage.volumes_ceph.value == true + type: checkbox + value: false + weight: 10 + syslog: + metadata: + label: Syslog + weight: 50 + syslog_port: + description: Remote syslog port + label: Port + regex: + error: Invalid syslog port + source: ^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$ + type: text + value: '514' + weight: 20 + syslog_server: + description: Remote syslog hostname + label: Hostname + type: text + value: '' + weight: 10 + syslog_transport: + label: Syslog transport protocol + type: radio + value: tcp + values: + - data: udp + description: '' + label: UDP + - data: tcp + description: '' + label: TCP + weight: 30 + workloads_collector: + enabled: + type: hidden + value: true + metadata: + label: Workloads Collector User + restrictions: + - action: hide + condition: 'true' + weight: 10 + password: + type: password + value: sbfn1c8b + tenant: + type: text + value: services + user: + type: text + value: fuel_stats_user
\ No newline at end of file diff --git a/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod2/dha.yaml b/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod2/dha.yaml new file mode 100644 index 000000000..cfc97094c --- /dev/null +++ b/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod2/dha.yaml @@ -0,0 +1,49 @@ +title: Deployment Hardware Adapter (DHA) +# DHA API version supported +version: +created: +comment: Config for LF POD2 and Opendaylight + +# Adapter to use for this definition +adapter: ipmi + +# Node list. +# Mandatory property is id, all other properties are adapter specific. + +nodes: +- id: 1 + pxeMac: 00:25:b5:a0:00:2a + ipmiIp: 172.30.8.75 + ipmiUser: admin + ipmiPass: octopus +- id: 2 + pxeMac: 00:25:b5:a0:00:3a + ipmiIp: 172.30.8.65 + ipmiUser: admin + ipmiPass: octopus +- id: 3 + pxeMac: 00:25:b5:a0:00:4a + ipmiIp: 172.30.8.74 + ipmiUser: admin + ipmiPass: octopus +- id: 4 + pxeMac: 00:25:b5:a0:00:5a + ipmiIp: 172.30.8.73 + ipmiUser: admin + ipmiPass: octopus +- id: 5 + pxeMac: 00:25:b5:a0:00:6a + ipmiIp: 172.30.8.72 + ipmiUser: admin + ipmiPass: octopus +# Adding the Fuel node as node id 6 which may not be correct - please +# adjust as needed. +- id: 6 + libvirtName: fuel-opnfv + libvirtTemplate: templates/hardware_environment/vms/fuel.xml + isFuel: yes + username: root + password: r00tme + +disks: + fuel: 50G
\ No newline at end of file diff --git a/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod2/plugins_conf/.gitkeep b/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod2/plugins_conf/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/deploy/templates/hardware_environment/conf/linux_foundation_lab/pod2/plugins_conf/.gitkeep diff --git a/deploy/templates/hardware_environment/conf/opnfv_box/dea.yaml b/deploy/templates/hardware_environment/conf/opnfv_box/dea.yaml new file mode 100644 index 000000000..fa6da9c96 --- /dev/null +++ b/deploy/templates/hardware_environment/conf/opnfv_box/dea.yaml @@ -0,0 +1,898 @@ +title: Deployment Environment Adapter (DEA) +# DEA API version supported +version: +created: +comment: Config for OPNFV BOX - HA deployment with Ceph +environment: + name: opnfv_box + mode: ha + net_segment_type: tun +wanted_release: Kilo on Ubuntu 14.04 +nodes: +- id: 1 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 2 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 3 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 4 + interfaces: interfaces_1 + transformations: transformations_2 + role: ceph-osd,compute +- id: 5 + interfaces: interfaces_1 + transformations: transformations_2 + role: ceph-osd,compute +fuel: + ADMIN_NETWORK: + cidr: 10.20.0.0/24 + dhcp_gateway: 10.20.0.2 + dhcp_pool_end: 10.20.0.254 + dhcp_pool_start: 10.20.0.3 + ipaddress: 10.20.0.2 + netmask: 255.255.0.0 + DNS_DOMAIN: domain.tld + DNS_SEARCH: domain.tld + DNS_UPSTREAM: 8.8.8.8 + FUEL_ACCESS: + password: admin + user: admin + HOSTNAME: opnfv + NTP1: 0.pool.ntp.org + NTP2: 1.pool.ntp.org + NTP3: 2.pool.ntp.org +interfaces_1: + eth0: + - public + eth1: + - fuelweb_admin + - management + - storage + - private +transformations_1: + transformations: + - action: add-br + name: br-fw-admin + - action: add-br + name: br-mgmt + - action: add-br + name: br-storage + - action: add-br + name: br-ex + - action: add-br + name: br-floating + provider: ovs + - action: add-patch + bridges: + - br-floating + - br-ex + mtu: 65000 + provider: ovs + - action: add-br + name: br-mesh + - action: add-port + bridge: br-fw-admin + name: eth0 + - action: add-port + bridge: br-mgmt + name: eth1.300 + - action: add-port + bridge: br-storage + name: eth1.301 + - action: add-port + bridge: br-mesh + name: eth1.302 + - action: add-port + bridge: br-ex + name: eth0 +transformations_2: + transformations: + - action: add-br + name: br-fw-admin + - action: add-br + name: br-mgmt + - action: add-br + name: br-storage + - action: add-br + name: br-mesh + - action: add-port + bridge: br-fw-admin + name: eth0 + - action: add-port + bridge: br-mgmt + name: eth1.300 + - action: add-port + bridge: br-storage + name: eth1.301 + - action: add-port + bridge: br-mesh + name: eth1.302 +network: + management_vip: 192.168.0.2 + management_vrouter_vip: 192.168.0.1 + networking_parameters: + base_mac: fa:16:3e:00:00:00 + configuration_template: null + dns_nameservers: + - 8.8.4.4 + - 8.8.8.8 + floating_ranges: + - - 172.30.10.83 + - 172.30.10.92 + gre_id_range: + - 2 + - 65535 + internal_cidr: 192.168.111.0/24 + internal_gateway: 192.168.111.1 + net_l23_provider: ovs + segmentation_type: tun + vlan_range: + - 1000 + - 1030 + networks: + - cidr: 172.30.10.0/24 + gateway: 172.30.10.1 + ip_ranges: + - - 172.30.10.73 + - 172.30.10.82 + meta: + cidr: 172.16.0.0/24 + configurable: true + floating_range_var: floating_ranges + ip_range: + - 172.16.0.2 + - 172.16.0.126 + map_priority: 1 + name: public + notation: ip_ranges + render_addr_mask: public + render_type: null + use_gateway: true + vips: + - haproxy + - vrouter + vlan_start: null + name: public + vlan_start: null + - cidr: 192.168.0.0/24 + gateway: null + ip_ranges: + - - 192.168.0.1 + - 192.168.0.254 + meta: + cidr: 192.168.0.0/24 + configurable: true + map_priority: 2 + name: management + notation: cidr + render_addr_mask: internal + render_type: cidr + use_gateway: false + vips: + - haproxy + - vrouter + vlan_start: 101 + name: management + vlan_start: 300 + - cidr: 192.168.1.0/24 + gateway: null + ip_ranges: + - - 192.168.1.1 + - 192.168.1.254 + meta: + cidr: 192.168.1.0/24 + configurable: true + map_priority: 2 + name: storage + notation: cidr + render_addr_mask: storage + render_type: cidr + use_gateway: false + vlan_start: 102 + name: storage + vlan_start: 301 + - cidr: 192.168.2.0/24 + gateway: null + ip_ranges: + - - 192.168.2.1 + - 192.168.2.254 + meta: + cidr: 192.168.2.0/24 + configurable: true + map_priority: 2 + name: private + notation: cidr + render_addr_mask: null + render_type: cidr + seg_type: tun + use_gateway: false + vlan_start: 103 + name: private + vlan_start: 302 + - cidr: 10.20.0.0/16 + gateway: 10.20.0.2 + ip_ranges: + - - 10.20.0.3 + - 10.20.0.254 + meta: + configurable: false + map_priority: 0 + notation: ip_ranges + render_addr_mask: null + render_type: null + unmovable: true + use_gateway: true + name: fuelweb_admin + vlan_start: null + public_vip: 172.30.10.3 + public_vrouter_vip: 172.30.10.2 + vips: + management: + ipaddr: 192.168.0.2 + namespace: haproxy + network_role: mgmt/vip + node_roles: + - controller + - primary-controller + public: + ipaddr: 172.30.10.3 + namespace: haproxy + network_role: public/vip + node_roles: + - controller + - primary-controller + vrouter: + ipaddr: 192.168.0.1 + namespace: vrouter + network_role: mgmt/vip + node_roles: + - controller + - primary-controller + vrouter_pub: + ipaddr: 172.30.10.2 + namespace: vrouter + network_role: public/vip + node_roles: + - controller + - primary-controller +settings: + editable: + access: + email: + description: Email address for Administrator + label: Email + regex: + error: Invalid email + source: ^\S+@\S+$ + type: text + value: admin@localhost + weight: 40 + metadata: + label: Access + weight: 10 + password: + description: Password for Administrator + label: Password + regex: + error: Empty password + source: \S + type: password + value: admin + weight: 20 + tenant: + description: Tenant (project) name for Administrator + label: Tenant + regex: + error: Invalid tenant name + source: ^(?!services$)(?!nova$)(?!glance$)(?!keystone$)(?!neutron$)(?!cinder$)(?!swift$)(?!ceph$)(?![Gg]uest$)(?!.* + +.*$).+ + type: text + value: admin + weight: 30 + user: + description: Username for Administrator + label: Username + regex: + error: Invalid username + source: ^(?!services$)(?!nova$)(?!glance$)(?!keystone$)(?!neutron$)(?!cinder$)(?!swift$)(?!ceph$)(?![Gg]uest$)(?!.* + +.*$).+ + type: text + value: admin + weight: 10 + additional_components: + ceilometer: + description: If selected, Ceilometer component will be installed + label: Install Ceilometer + type: checkbox + value: false + weight: 40 + heat: + description: '' + label: '' + type: hidden + value: true + weight: 30 + metadata: + label: Additional Components + weight: 20 + mongo: + description: If selected, You can use external Mongo DB as ceilometer backend + label: Use external Mongo DB + restrictions: + - settings:additional_components.ceilometer.value == false + type: checkbox + value: false + weight: 40 + murano: + description: If selected, Murano component will be installed + label: Install Murano + type: checkbox + value: false + weight: 20 + sahara: + description: If selected, Sahara component will be installed + label: Install Sahara + type: checkbox + value: false + weight: 10 + common: + auth_key: + description: Public key(s) to include in authorized_keys on deployed nodes + label: Public Key + type: textarea + value: '' + weight: 70 + auto_assign_floating_ip: + description: If selected, OpenStack will automatically assign a floating IP + to a new instance + label: Auto assign floating IP + restrictions: + - action: hide + condition: cluster:net_provider == 'neutron' + type: checkbox + value: false + weight: 40 + debug: + description: Debug logging mode provides more information, but requires more + disk space. + label: OpenStack debug logging + type: checkbox + value: false + weight: 20 + libvirt_type: + label: Hypervisor type + type: radio + value: kvm + values: + - data: kvm + description: Choose this type of hypervisor if you run OpenStack on hardware + label: KVM + - data: qemu + description: Choose this type of hypervisor if you run OpenStack on virtual + hosts. + label: QEMU + weight: 30 + metadata: + label: Common + weight: 30 + nova_quota: + description: Quotas are used to limit CPU and memory usage for tenants. Enabling + quotas will increase load on the Nova database. + label: Nova quotas + type: checkbox + value: false + weight: 25 + puppet_debug: + description: Debug puppet logging mode provides more information, but requires + more disk space. + label: Puppet debug logging + type: checkbox + value: true + weight: 20 + resume_guests_state_on_host_boot: + description: Whether to resume previous guests state when the host reboots. + If enabled, this option causes guests assigned to the host to resume their + previous state. If the guest was running a restart will be attempted when + nova-compute starts. If the guest was not running previously, a restart will + not be attempted. + label: Resume guests state on host boot + type: checkbox + value: true + weight: 60 + use_cow_images: + description: For most cases you will want qcow format. If it's disabled, raw + image format will be used to run VMs. OpenStack with raw format currently + does not support snapshotting. + label: Use qcow format for images + type: checkbox + value: true + weight: 50 + use_vcenter: + restrictions: + - condition: cluster:net_provider == 'nova_network' + type: hidden + value: false + weight: 30 + corosync: + group: + description: '' + label: Group + type: text + value: 226.94.1.1 + weight: 10 + metadata: + label: Corosync + restrictions: + - action: hide + condition: 'true' + weight: 50 + port: + description: '' + label: Port + type: text + value: '12000' + weight: 20 + verified: + description: Set True only if multicast is configured correctly on router. + label: Need to pass network verification. + type: checkbox + value: false + weight: 10 + external_dns: + dns_list: + description: List of upstream DNS servers, separated by comma + label: DNS list + regex: + error: Invalid IP address list + source: ^\*$|^(?:\d|1?\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|1?\d\d|2[0-4]\d|25[0-5])){3}(?:\s*,\s*(?:\d|1?\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|1?\d\d|2[0-4]\d|25[0-5])){3})*$ + type: text + value: 8.8.8.8, 8.8.4.4 + weight: 10 + metadata: + label: Host OS DNS Servers + weight: 90 + external_mongo: + hosts_ip: + description: IP Addresses of MongoDB. Use comma to split IPs + label: MongoDB hosts IP + regex: + error: Invalid hosts ip sequence + source: ^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?),)*((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ + type: text + value: '' + weight: 30 + metadata: + label: External MongoDB + restrictions: + - action: hide + condition: settings:additional_components.mongo.value == false + message: Ceilometer and MongoDB are not enabled on the Additional Components + section + weight: 20 + mongo_db_name: + description: Mongo database name + label: Database name + regex: + error: Invalid database name + source: ^\w+$ + type: text + value: ceilometer + weight: 30 + mongo_password: + description: Mongo database password + label: Password + regex: + error: Password contains spaces + source: ^\S*$ + type: password + value: ceilometer + weight: 30 + mongo_replset: + description: Name for Mongo replication set + label: Replset + type: text + value: '' + weight: 30 + mongo_user: + description: Mongo database username + label: Username + regex: + error: Empty username + source: ^\w+$ + type: text + value: ceilometer + weight: 30 + external_ntp: + metadata: + label: Host OS NTP Servers + weight: 100 + ntp_list: + description: List of upstream NTP servers, separated by comma + label: NTP server list + regex: + error: Invalid NTP server list + source: ^\s*(?:(?:\w+(?:-+\w+)*\.)+[a-z]+|\d{1,3}(?:\.\d{1,3}){3})\s*(?:,\s*(?:(?:\w+(?:-+\w+)*\.)+[a-z]+|\d{1,3}(\.\d{1,3}){3})\s*)*$ + type: text + value: 0.pool.ntp.org, 1.pool.ntp.org, 2.pool.ntp.org + weight: 10 + kernel_params: + kernel: + description: Default kernel parameters + label: Initial parameters + type: text + value: console=tty0 net.ifnames=0 biosdevname=0 rootdelay=90 nomodeset + weight: 45 + metadata: + label: Kernel parameters + weight: 40 + murano_settings: + metadata: + label: Murano Settings + restrictions: + - action: hide + condition: settings:additional_components.murano.value == false + message: Murano is not enabled on the Additional Components section + weight: 20 + murano_repo_url: + description: '' + label: Murano Repository URL + type: text + value: http://storage.apps.openstack.org/ + weight: 10 + neutron_advanced_configuration: + metadata: + label: Neutron Advanced Configuration + restrictions: + - action: hide + condition: cluster:net_provider != 'neutron' + weight: 45 + neutron_dvr: + description: Enable Distributed Virtual Routers in Neutron + label: Neutron DVR + restrictions: + - ? networking_parameters:segmentation_type != 'vlan' and settings:neutron_advanced_configuration.neutron_l2_pop.value + == false + : DVR requires L2 population to be enabled. + type: checkbox + value: false + weight: 20 + neutron_l2_pop: + description: Enable L2 population mechanism in Neutron + label: Neutron L2 population + restrictions: + - action: hide + condition: networking_parameters:segmentation_type == 'vlan' + type: checkbox + value: false + weight: 10 + neutron_mellanox: + metadata: + enabled: true + label: Mellanox Neutron components + restrictions: + - action: hide + condition: not ('experimental' in version:feature_groups) + toggleable: false + weight: 50 + plugin: + label: Mellanox drivers and SR-IOV plugin + type: radio + value: disabled + values: + - data: disabled + description: If selected, Mellanox drivers, Neutron and Cinder plugin will + not be installed. + label: Mellanox drivers and plugins disabled + restrictions: + - settings:storage.iser.value == true + - data: drivers_only + description: If selected, Mellanox Ethernet drivers will be installed to support + networking over Mellanox NIC. Mellanox Neutron plugin will not be installed. + label: Install only Mellanox drivers + restrictions: + - settings:common.libvirt_type.value != 'kvm' + - data: ethernet + description: If selected, both Mellanox Ethernet drivers and Mellanox network + acceleration (Neutron) plugin will be installed. + label: Install Mellanox drivers and SR-IOV plugin + restrictions: + - settings:common.libvirt_type.value != 'kvm' or not (cluster:net_provider + == 'neutron' and networking_parameters:segmentation_type == 'vlan') + weight: 60 + vf_num: + description: Note that one virtual function will be reserved to the storage + network, in case of choosing iSER. + label: Number of virtual NICs + restrictions: + - settings:neutron_mellanox.plugin.value != 'ethernet' + type: text + value: '16' + weight: 70 + provision: + metadata: + label: Provision + restrictions: + - action: hide + condition: 'true' + weight: 80 + method: + type: hidden + value: image + public_network_assignment: + assign_to_all_nodes: + description: When disabled, public network will be assigned to controllers only + label: Assign public network to all nodes + type: checkbox + value: false + weight: 10 + metadata: + label: Public network assignment + restrictions: + - action: hide + condition: cluster:net_provider != 'neutron' + weight: 50 + public_ssl: + cert_data: + description: Certificate and private key data, concatenated into a single file + label: Certificate + restrictions: + - action: hide + condition: (settings:public_ssl.cert_source.value != 'user_uploaded') or (settings:public_ssl.horizon.value + == false and settings:public_ssl.services.value == false) + type: file + value: '' + weight: 40 + cert_source: + description: From where we'll get certificate and private key + label: Select source for certificate + restrictions: + - action: hide + condition: settings:public_ssl.horizon.value == false and settings:public_ssl.services.value + == false + type: radio + value: self_signed + values: + - data: self_signed + description: Generate private key and certificate that will be signed by this + key + label: Self-signed + - data: user_uploaded + description: Use pre-generated key and certificate + label: I have my own keypair with certificate + weight: 30 + horizon: + description: Secure access to Horizon enabling HTTPS instead of HTTP + label: HTTPS for Horizon + type: checkbox + value: false + weight: 10 + hostname: + description: Your DNS entries should point to this name. Self-signed certificates + also will use this hostname + label: DNS hostname for public TLS endpoints + restrictions: + - action: hide + condition: settings:public_ssl.horizon.value == false and settings:public_ssl.services.value + == false + type: text + value: public.fuel.local + weight: 50 + metadata: + label: Public TLS + weight: 110 + services: + description: Enable TLS termination on HAProxy for OpenStack services + label: TLS for OpenStack public endpoints + type: checkbox + value: false + weight: 20 + repo_setup: + metadata: + always_editable: true + label: Repositories + weight: 50 + repos: + description: 'Please note: the first repository will be considered the operating + system mirror that will be used during node provisioning. + + To create a local repository mirror on the Fuel master node, please follow + the instructions provided by running "fuel-createmirror --help" on the Fuel + master node. + + Please make sure your Fuel master node has Internet access to the repository + before attempting to create a mirror. + + For more details, please refer to the documentation (https://docs.mirantis.com/openstack/fuel/fuel-7.0/operations.html#external-ubuntu-ops). + + ' + extra_priority: null + type: custom_repo_configuration + value: + - name: ubuntu + priority: null + section: main universe multiverse + suite: trusty + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: ubuntu-updates + priority: null + section: main universe multiverse + suite: trusty-updates + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: ubuntu-security + priority: null + section: main universe multiverse + suite: trusty-security + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: mos + priority: 1050 + section: main restricted + suite: mos7.0 + type: deb + uri: http://10.20.0.2:8080/2015.1.0-7.0/ubuntu/x86_64 + - name: mos-updates + priority: 1050 + section: main restricted + suite: mos7.0-updates + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: mos-security + priority: 1050 + section: main restricted + suite: mos7.0-security + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: mos-holdback + priority: 1100 + section: main restricted + suite: mos7.0-holdback + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: Auxiliary + priority: 1150 + section: main restricted + suite: auxiliary + type: deb + uri: http://10.20.0.2:8080/2015.1.0-7.0/ubuntu/auxiliary + storage: + ephemeral_ceph: + description: Configures Nova to store ephemeral volumes in RBD. This works best + if Ceph is enabled for volumes and images, too. Enables live migration of + all types of Ceph backed VMs (without this option, live migration will only + work with VMs launched from Cinder volumes). + label: Ceph RBD for ephemeral volumes (Nova) + type: checkbox + value: true + weight: 75 + images_ceph: + description: Configures Glance to use the Ceph RBD backend to store images. + If enabled, this option will prevent Swift from installing. + label: Ceph RBD for images (Glance) + restrictions: + - settings:storage.images_vcenter.value == true: Only one Glance backend could + be selected. + type: checkbox + value: true + weight: 30 + images_vcenter: + description: Configures Glance to use the vCenter/ESXi backend to store images. + If enabled, this option will prevent Swift from installing. + label: VMWare vCenter/ESXi datastore for images (Glance) + restrictions: + - action: hide + condition: settings:common.use_vcenter.value != true + - condition: settings:storage.images_ceph.value == true + message: Only one Glance backend could be selected. + type: checkbox + value: false + weight: 35 + iser: + description: 'High performance block storage: Cinder volumes over iSER protocol + (iSCSI over RDMA). This feature requires SR-IOV capabilities in the NIC, and + will use a dedicated virtual function for the storage network.' + label: iSER protocol for volumes (Cinder) + restrictions: + - settings:storage.volumes_lvm.value != true or settings:common.libvirt_type.value + != 'kvm' + - action: hide + condition: not ('experimental' in version:feature_groups) + type: checkbox + value: false + weight: 11 + metadata: + label: Storage + weight: 60 + objects_ceph: + description: Configures RadosGW front end for Ceph RBD. This exposes S3 and + Swift API Interfaces. If enabled, this option will prevent Swift from installing. + label: Ceph RadosGW for objects (Swift API) + restrictions: + - settings:storage.images_ceph.value == false + type: checkbox + value: false + weight: 80 + osd_pool_size: + description: Configures the default number of object replicas in Ceph. This + number must be equal to or lower than the number of deployed 'Storage - Ceph + OSD' nodes. + label: Ceph object replication factor + regex: + error: Invalid number + source: ^[1-9]\d*$ + type: text + value: '2' + weight: 85 + volumes_ceph: + description: Configures Cinder to store volumes in Ceph RBD images. + label: Ceph RBD for volumes (Cinder) + restrictions: + - settings:storage.volumes_lvm.value == true + type: checkbox + value: true + weight: 20 + volumes_lvm: + description: It is recommended to have at least one Storage - Cinder LVM node. + label: Cinder LVM over iSCSI for volumes + restrictions: + - settings:storage.volumes_ceph.value == true + type: checkbox + value: false + weight: 10 + syslog: + metadata: + label: Syslog + weight: 50 + syslog_port: + description: Remote syslog port + label: Port + regex: + error: Invalid syslog port + source: ^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$ + type: text + value: '514' + weight: 20 + syslog_server: + description: Remote syslog hostname + label: Hostname + type: text + value: '' + weight: 10 + syslog_transport: + label: Syslog transport protocol + type: radio + value: tcp + values: + - data: udp + description: '' + label: UDP + - data: tcp + description: '' + label: TCP + weight: 30 + workloads_collector: + enabled: + type: hidden + value: true + metadata: + label: Workloads Collector User + restrictions: + - action: hide + condition: 'true' + weight: 10 + password: + type: password + value: sbfn1c8b + tenant: + type: text + value: services + user: + type: text + value: fuel_stats_user
\ No newline at end of file diff --git a/deploy/templates/hardware_environment/conf/opnfv_box/dha.yaml b/deploy/templates/hardware_environment/conf/opnfv_box/dha.yaml new file mode 100644 index 000000000..b127ac919 --- /dev/null +++ b/deploy/templates/hardware_environment/conf/opnfv_box/dha.yaml @@ -0,0 +1,49 @@ +title: Deployment Hardware Adapter (DHA) +# DHA API version supported +version: +created: +comment: Config for OPNFV BOX + +# Adapter to use for this definition +adapter: amt + +# Node list. +# Mandatory property is id, all other properties are adapter specific. + +nodes: +- id: 1 + pxeMac: b8:ae:ed:76:4d:a4 + amtIp: <amt_ip> + amtUser: <username> + amtPass: <password> +- id: 2 + pxeMac: b8:ae:ed:76:4d:94 + amtIp: <amt_ip> + amtUser: <username> + amtPass: <password> +- id: 3 + pxeMac: b8:ae:ed:76:4c:eb + amtIp: <amt_ip> + amtUser: <username> + amtPass: <password> +- id: 4 + pxeMac: b8:ae:ed:76:37:62 + amtIp: <amt_ip> + amtUser: <username> + amtPass: <password> +- id: 5 + pxeMac: b8:ae:ed:76:4d:95 + amtIp: <amt_ip> + amtUser: <username> + amtPass: <password> +# Adding the Fuel node as node id 6 which may not be correct - please +# adjust as needed. +- id: 6 + libvirtName: fuel-opnfv + libvirtTemplate: templates/hardware_environment/vms/fuel.xml + isFuel: yes + username: root + password: r00tme + +disks: + fuel: 50G
\ No newline at end of file diff --git a/deploy/templates/hardware_environment/conf/opnfv_box/plugins_conf/.gitkeep b/deploy/templates/hardware_environment/conf/opnfv_box/plugins_conf/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/deploy/templates/hardware_environment/conf/opnfv_box/plugins_conf/.gitkeep diff --git a/deploy/templates/hardware_environment/vms/fuel.xml b/deploy/templates/hardware_environment/vms/fuel.xml new file mode 100644 index 000000000..e3e3f80bb --- /dev/null +++ b/deploy/templates/hardware_environment/vms/fuel.xml @@ -0,0 +1,91 @@ +<domain type='kvm' id='62'> + <name>fuel</name> + <memory unit='KiB'>8290304</memory> + <currentMemory unit='KiB'>8290304</currentMemory> + <vcpu placement='static'>4</vcpu> + <resource> + <partition>/machine</partition> + </resource> + <os> + <type arch='x86_64' machine='pc-i440fx-rhel7.0.0'>hvm</type> + <boot dev='cdrom'/> + <boot dev='hd'/> + <bootmenu enable='no'/> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <cpu mode='custom' match='exact'> + <model fallback='allow'>SandyBridge</model> + </cpu> + <clock offset='utc'> + <timer name='rtc' tickpolicy='catchup'/> + <timer name='pit' tickpolicy='delay'/> + <timer name='hpet' present='no'/> + </clock> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <pm> + <suspend-to-mem enabled='no'/> + <suspend-to-disk enabled='no'/> + </pm> + <devices> + <emulator>/usr/libexec/qemu-kvm</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='raw'/> + <target dev='vda' bus='virtio'/> + </disk> + <disk type='block' device='cdrom'> + <driver name='qemu' type='raw'/> + <target dev='hdb' bus='ide'/> + <readonly/> + </disk> + <controller type='usb' index='0' model='ich9-ehci1'> + </controller> + <controller type='usb' index='0' model='ich9-uhci1'> + <master startport='0'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci2'> + <master startport='2'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci3'> + <master startport='4'/> + </controller> + <controller type='pci' index='0' model='pci-root'> + </controller> + <controller type='ide' index='0'> + </controller> + <controller type='virtio-serial' index='0'> + </controller> + <interface type='bridge'> + <model type='virtio'/> + </interface> + <serial type='pty'> + <source path='/dev/pts/0'/> + <target port='0'/> + </serial> + <console type='pty' tty='/dev/pts/0'> + <source path='/dev/pts/0'/> + <target type='serial' port='0'/> + </console> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='sv'> + <listen type='address' address='127.0.0.1'/> + </graphics> + <sound model='ich6'> + </sound> + <video> + <model type='cirrus' vram='9216' heads='1'/> + </video> + <memballoon model='virtio'> + </memballoon> + </devices> + <seclabel type='dynamic' model='selinux' relabel='yes'> + <label>system_u:system_r:svirt_t:s0:c52,c932</label> + <imagelabel>system_u:object_r:svirt_image_t:s0:c52,c932</imagelabel> + </seclabel> +</domain>
\ No newline at end of file diff --git a/deploy/templates/plugins/.gitkeep b/deploy/templates/plugins/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/deploy/templates/plugins/.gitkeep diff --git a/deploy/templates/virtual_environment/conf/dea.yaml b/deploy/templates/virtual_environment/conf/dea.yaml new file mode 100644 index 000000000..c764e03e5 --- /dev/null +++ b/deploy/templates/virtual_environment/conf/dea.yaml @@ -0,0 +1,903 @@ +title: Deployment Environment Adapter (DEA) +# DEA API version supported +version: +created: +comment: Config for Virtual Environment - HA deployment with Ceph +environment: + name: opnfv_virt + mode: ha + net_segment_type: tun +wanted_release: Kilo on Ubuntu 14.04 +nodes: +- id: 1 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 2 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 3 + interfaces: interfaces_1 + transformations: transformations_1 + role: ceph-osd,controller +- id: 4 + interfaces: interfaces_1 + transformations: transformations_2 + role: ceph-osd,compute +- id: 5 + interfaces: interfaces_1 + transformations: transformations_2 + role: ceph-osd,compute +- id: 6 + interfaces: interfaces_1 + transformations: transformations_2 + role: ceph-osd,compute +fuel: + ADMIN_NETWORK: + cidr: 10.20.0.0/24 + dhcp_gateway: 10.20.0.2 + dhcp_pool_end: 10.20.0.254 + dhcp_pool_start: 10.20.0.3 + ipaddress: 10.20.0.2 + netmask: 255.255.0.0 + DNS_DOMAIN: opnfvericsson.ca + DNS_SEARCH: opnfvericsson.ca + DNS_UPSTREAM: 10.118.32.193 + FUEL_ACCESS: + password: admin + user: admin + HOSTNAME: opnfv_virt + NTP1: 10.118.32.193 + NTP2: 10.118.34.193 + NTP3: null +interfaces_1: + eth0: + - fuelweb_admin + - management + eth1: + - storage + eth2: + - private + eth3: + - public +transformations_1: + transformations: + - action: add-br + name: br-fw-admin + - action: add-br + name: br-mgmt + - action: add-br + name: br-storage + - action: add-br + name: br-ex + - action: add-br + name: br-floating + provider: ovs + - action: add-patch + bridges: + - br-floating + - br-ex + mtu: 65000 + provider: ovs + - action: add-br + name: br-mesh + - action: add-port + bridge: br-fw-admin + name: eth0 + - action: add-port + bridge: br-mgmt + name: eth0.101 + - action: add-port + bridge: br-storage + name: eth1.102 + - action: add-port + bridge: br-mesh + name: eth2.103 + - action: add-port + bridge: br-ex + name: eth3 +transformations_2: + transformations: + - action: add-br + name: br-fw-admin + - action: add-br + name: br-mgmt + - action: add-br + name: br-storage + - action: add-br + name: br-mesh + - action: add-port + bridge: br-fw-admin + name: eth0 + - action: add-port + bridge: br-mgmt + name: eth0.101 + - action: add-port + bridge: br-storage + name: eth1.102 + - action: add-port + bridge: br-mesh + name: eth2.103 +network: + management_vip: 192.168.0.2 + management_vrouter_vip: 192.168.0.1 + networking_parameters: + base_mac: fa:16:3e:00:00:00 + configuration_template: null + dns_nameservers: + - 10.118.32.193 + floating_ranges: + - - 172.16.0.130 + - 172.16.0.254 + gre_id_range: + - 2 + - 65535 + internal_cidr: 192.168.111.0/24 + internal_gateway: 192.168.111.1 + net_l23_provider: ovs + segmentation_type: tun + vlan_range: + - 1000 + - 1030 + networks: + - cidr: 172.16.0.0/24 + gateway: 172.16.0.1 + ip_ranges: + - - 172.16.0.2 + - 172.16.0.126 + meta: + cidr: 172.16.0.0/24 + configurable: true + floating_range_var: floating_ranges + ip_range: + - 172.16.0.2 + - 172.16.0.126 + map_priority: 1 + name: public + notation: ip_ranges + render_addr_mask: public + render_type: null + use_gateway: true + vips: + - haproxy + - vrouter + vlan_start: null + name: public + vlan_start: null + - cidr: 192.168.0.0/24 + gateway: null + ip_ranges: + - - 192.168.0.1 + - 192.168.0.254 + meta: + cidr: 192.168.0.0/24 + configurable: true + map_priority: 2 + name: management + notation: cidr + render_addr_mask: internal + render_type: cidr + use_gateway: false + vips: + - haproxy + - vrouter + vlan_start: 101 + name: management + vlan_start: 101 + - cidr: 192.168.1.0/24 + gateway: null + ip_ranges: + - - 192.168.1.1 + - 192.168.1.254 + meta: + cidr: 192.168.1.0/24 + configurable: true + map_priority: 2 + name: storage + notation: cidr + render_addr_mask: storage + render_type: cidr + use_gateway: false + vlan_start: 102 + name: storage + vlan_start: 102 + - cidr: 192.168.2.0/24 + gateway: null + ip_ranges: + - - 192.168.2.1 + - 192.168.2.254 + meta: + cidr: 192.168.2.0/24 + configurable: true + map_priority: 2 + name: private + notation: cidr + render_addr_mask: null + render_type: cidr + seg_type: tun + use_gateway: false + vlan_start: 103 + name: private + vlan_start: 103 + - cidr: 10.20.0.0/16 + gateway: 10.20.0.2 + ip_ranges: + - - 10.20.0.3 + - 10.20.0.254 + meta: + configurable: false + map_priority: 0 + notation: ip_ranges + render_addr_mask: null + render_type: null + unmovable: true + use_gateway: true + name: fuelweb_admin + vlan_start: null + public_vip: 172.16.0.3 + public_vrouter_vip: 172.16.0.2 + vips: + management: + ipaddr: 192.168.0.2 + namespace: haproxy + network_role: mgmt/vip + node_roles: + - controller + - primary-controller + public: + ipaddr: 172.16.0.3 + namespace: haproxy + network_role: public/vip + node_roles: + - controller + - primary-controller + vrouter: + ipaddr: 192.168.0.1 + namespace: vrouter + network_role: mgmt/vip + node_roles: + - controller + - primary-controller + vrouter_pub: + ipaddr: 172.16.0.2 + namespace: vrouter + network_role: public/vip + node_roles: + - controller + - primary-controller +settings: + editable: + access: + email: + description: Email address for Administrator + label: Email + regex: + error: Invalid email + source: ^\S+@\S+$ + type: text + value: admin@localhost + weight: 40 + metadata: + label: Access + weight: 10 + password: + description: Password for Administrator + label: Password + regex: + error: Empty password + source: \S + type: password + value: admin + weight: 20 + tenant: + description: Tenant (project) name for Administrator + label: Tenant + regex: + error: Invalid tenant name + source: ^(?!services$)(?!nova$)(?!glance$)(?!keystone$)(?!neutron$)(?!cinder$)(?!swift$)(?!ceph$)(?![Gg]uest$)(?!.* + +.*$).+ + type: text + value: admin + weight: 30 + user: + description: Username for Administrator + label: Username + regex: + error: Invalid username + source: ^(?!services$)(?!nova$)(?!glance$)(?!keystone$)(?!neutron$)(?!cinder$)(?!swift$)(?!ceph$)(?![Gg]uest$)(?!.* + +.*$).+ + type: text + value: admin + weight: 10 + additional_components: + ceilometer: + description: If selected, Ceilometer component will be installed + label: Install Ceilometer + type: checkbox + value: false + weight: 40 + heat: + description: '' + label: '' + type: hidden + value: true + weight: 30 + metadata: + label: Additional Components + weight: 20 + mongo: + description: If selected, You can use external Mongo DB as ceilometer backend + label: Use external Mongo DB + restrictions: + - settings:additional_components.ceilometer.value == false + type: checkbox + value: false + weight: 40 + murano: + description: If selected, Murano component will be installed + label: Install Murano + type: checkbox + value: false + weight: 20 + sahara: + description: If selected, Sahara component will be installed + label: Install Sahara + type: checkbox + value: false + weight: 10 + common: + auth_key: + description: Public key(s) to include in authorized_keys on deployed nodes + label: Public Key + type: textarea + value: '' + weight: 70 + auto_assign_floating_ip: + description: If selected, OpenStack will automatically assign a floating IP + to a new instance + label: Auto assign floating IP + restrictions: + - action: hide + condition: cluster:net_provider == 'neutron' + type: checkbox + value: false + weight: 40 + debug: + description: Debug logging mode provides more information, but requires more + disk space. + label: OpenStack debug logging + type: checkbox + value: false + weight: 20 + libvirt_type: + label: Hypervisor type + type: radio + value: kvm + values: + - data: kvm + description: Choose this type of hypervisor if you run OpenStack on hardware + label: KVM + - data: qemu + description: Choose this type of hypervisor if you run OpenStack on virtual + hosts. + label: QEMU + weight: 30 + metadata: + label: Common + weight: 30 + nova_quota: + description: Quotas are used to limit CPU and memory usage for tenants. Enabling + quotas will increase load on the Nova database. + label: Nova quotas + type: checkbox + value: false + weight: 25 + puppet_debug: + description: Debug puppet logging mode provides more information, but requires + more disk space. + label: Puppet debug logging + type: checkbox + value: true + weight: 20 + resume_guests_state_on_host_boot: + description: Whether to resume previous guests state when the host reboots. + If enabled, this option causes guests assigned to the host to resume their + previous state. If the guest was running a restart will be attempted when + nova-compute starts. If the guest was not running previously, a restart will + not be attempted. + label: Resume guests state on host boot + type: checkbox + value: true + weight: 60 + use_cow_images: + description: For most cases you will want qcow format. If it's disabled, raw + image format will be used to run VMs. OpenStack with raw format currently + does not support snapshotting. + label: Use qcow format for images + type: checkbox + value: true + weight: 50 + use_vcenter: + restrictions: + - condition: cluster:net_provider == 'nova_network' + type: hidden + value: false + weight: 30 + corosync: + group: + description: '' + label: Group + type: text + value: 226.94.1.1 + weight: 10 + metadata: + label: Corosync + restrictions: + - action: hide + condition: 'true' + weight: 50 + port: + description: '' + label: Port + type: text + value: '12000' + weight: 20 + verified: + description: Set True only if multicast is configured correctly on router. + label: Need to pass network verification. + type: checkbox + value: false + weight: 10 + external_dns: + dns_list: + description: List of upstream DNS servers, separated by comma + label: DNS list + regex: + error: Invalid IP address list + source: ^\*$|^(?:\d|1?\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|1?\d\d|2[0-4]\d|25[0-5])){3}(?:\s*,\s*(?:\d|1?\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|1?\d\d|2[0-4]\d|25[0-5])){3})*$ + type: text + value: 10.118.32.193 + weight: 10 + metadata: + label: Host OS DNS Servers + weight: 90 + external_mongo: + hosts_ip: + description: IP Addresses of MongoDB. Use comma to split IPs + label: MongoDB hosts IP + regex: + error: Invalid hosts ip sequence + source: ^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?),)*((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ + type: text + value: '' + weight: 30 + metadata: + label: External MongoDB + restrictions: + - action: hide + condition: settings:additional_components.mongo.value == false + message: Ceilometer and MongoDB are not enabled on the Additional Components + section + weight: 20 + mongo_db_name: + description: Mongo database name + label: Database name + regex: + error: Invalid database name + source: ^\w+$ + type: text + value: ceilometer + weight: 30 + mongo_password: + description: Mongo database password + label: Password + regex: + error: Password contains spaces + source: ^\S*$ + type: password + value: ceilometer + weight: 30 + mongo_replset: + description: Name for Mongo replication set + label: Replset + type: text + value: '' + weight: 30 + mongo_user: + description: Mongo database username + label: Username + regex: + error: Empty username + source: ^\w+$ + type: text + value: ceilometer + weight: 30 + external_ntp: + metadata: + label: Host OS NTP Servers + weight: 100 + ntp_list: + description: List of upstream NTP servers, separated by comma + label: NTP server list + regex: + error: Invalid NTP server list + source: ^\s*(?:(?:\w+(?:-+\w+)*\.)+[a-z]+|\d{1,3}(?:\.\d{1,3}){3})\s*(?:,\s*(?:(?:\w+(?:-+\w+)*\.)+[a-z]+|\d{1,3}(\.\d{1,3}){3})\s*)*$ + type: text + value: 10.118.32.193, 10.118.34.193 + weight: 10 + kernel_params: + kernel: + description: Default kernel parameters + label: Initial parameters + type: text + value: console=tty0 net.ifnames=0 biosdevname=0 rootdelay=90 nomodeset + weight: 45 + metadata: + label: Kernel parameters + weight: 40 + murano_settings: + metadata: + label: Murano Settings + restrictions: + - action: hide + condition: settings:additional_components.murano.value == false + message: Murano is not enabled on the Additional Components section + weight: 20 + murano_repo_url: + description: '' + label: Murano Repository URL + type: text + value: http://storage.apps.openstack.org/ + weight: 10 + neutron_advanced_configuration: + metadata: + label: Neutron Advanced Configuration + restrictions: + - action: hide + condition: cluster:net_provider != 'neutron' + weight: 45 + neutron_dvr: + description: Enable Distributed Virtual Routers in Neutron + label: Neutron DVR + restrictions: + - ? networking_parameters:segmentation_type != 'vlan' and settings:neutron_advanced_configuration.neutron_l2_pop.value + == false + : DVR requires L2 population to be enabled. + type: checkbox + value: false + weight: 20 + neutron_l2_pop: + description: Enable L2 population mechanism in Neutron + label: Neutron L2 population + restrictions: + - action: hide + condition: networking_parameters:segmentation_type == 'vlan' + type: checkbox + value: false + weight: 10 + neutron_mellanox: + metadata: + enabled: true + label: Mellanox Neutron components + restrictions: + - action: hide + condition: not ('experimental' in version:feature_groups) + toggleable: false + weight: 50 + plugin: + label: Mellanox drivers and SR-IOV plugin + type: radio + value: disabled + values: + - data: disabled + description: If selected, Mellanox drivers, Neutron and Cinder plugin will + not be installed. + label: Mellanox drivers and plugins disabled + restrictions: + - settings:storage.iser.value == true + - data: drivers_only + description: If selected, Mellanox Ethernet drivers will be installed to support + networking over Mellanox NIC. Mellanox Neutron plugin will not be installed. + label: Install only Mellanox drivers + restrictions: + - settings:common.libvirt_type.value != 'kvm' + - data: ethernet + description: If selected, both Mellanox Ethernet drivers and Mellanox network + acceleration (Neutron) plugin will be installed. + label: Install Mellanox drivers and SR-IOV plugin + restrictions: + - settings:common.libvirt_type.value != 'kvm' or not (cluster:net_provider + == 'neutron' and networking_parameters:segmentation_type == 'vlan') + weight: 60 + vf_num: + description: Note that one virtual function will be reserved to the storage + network, in case of choosing iSER. + label: Number of virtual NICs + restrictions: + - settings:neutron_mellanox.plugin.value != 'ethernet' + type: text + value: '16' + weight: 70 + provision: + metadata: + label: Provision + restrictions: + - action: hide + condition: 'true' + weight: 80 + method: + type: hidden + value: image + public_network_assignment: + assign_to_all_nodes: + description: When disabled, public network will be assigned to controllers only + label: Assign public network to all nodes + type: checkbox + value: false + weight: 10 + metadata: + label: Public network assignment + restrictions: + - action: hide + condition: cluster:net_provider != 'neutron' + weight: 50 + public_ssl: + cert_data: + description: Certificate and private key data, concatenated into a single file + label: Certificate + restrictions: + - action: hide + condition: (settings:public_ssl.cert_source.value != 'user_uploaded') or (settings:public_ssl.horizon.value + == false and settings:public_ssl.services.value == false) + type: file + value: '' + weight: 40 + cert_source: + description: From where we'll get certificate and private key + label: Select source for certificate + restrictions: + - action: hide + condition: settings:public_ssl.horizon.value == false and settings:public_ssl.services.value + == false + type: radio + value: self_signed + values: + - data: self_signed + description: Generate private key and certificate that will be signed by this + key + label: Self-signed + - data: user_uploaded + description: Use pre-generated key and certificate + label: I have my own keypair with certificate + weight: 30 + horizon: + description: Secure access to Horizon enabling HTTPS instead of HTTP + label: HTTPS for Horizon + type: checkbox + value: false + weight: 10 + hostname: + description: Your DNS entries should point to this name. Self-signed certificates + also will use this hostname + label: DNS hostname for public TLS endpoints + restrictions: + - action: hide + condition: settings:public_ssl.horizon.value == false and settings:public_ssl.services.value + == false + type: text + value: public.fuel.local + weight: 50 + metadata: + label: Public TLS + weight: 110 + services: + description: Enable TLS termination on HAProxy for OpenStack services + label: TLS for OpenStack public endpoints + type: checkbox + value: false + weight: 20 + repo_setup: + metadata: + always_editable: true + label: Repositories + weight: 50 + repos: + description: 'Please note: the first repository will be considered the operating + system mirror that will be used during node provisioning. + + To create a local repository mirror on the Fuel master node, please follow + the instructions provided by running "fuel-createmirror --help" on the Fuel + master node. + + Please make sure your Fuel master node has Internet access to the repository + before attempting to create a mirror. + + For more details, please refer to the documentation (https://docs.mirantis.com/openstack/fuel/fuel-7.0/operations.html#external-ubuntu-ops). + + ' + extra_priority: null + type: custom_repo_configuration + value: + - name: ubuntu + priority: null + section: main universe multiverse + suite: trusty + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: ubuntu-updates + priority: null + section: main universe multiverse + suite: trusty-updates + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: ubuntu-security + priority: null + section: main universe multiverse + suite: trusty-security + type: deb + uri: http://archive.ubuntu.com/ubuntu/ + - name: mos + priority: 1050 + section: main restricted + suite: mos7.0 + type: deb + uri: http://10.20.0.2:8080/2015.1.0-7.0/ubuntu/x86_64 + - name: mos-updates + priority: 1050 + section: main restricted + suite: mos7.0-updates + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: mos-security + priority: 1050 + section: main restricted + suite: mos7.0-security + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: mos-holdback + priority: 1100 + section: main restricted + suite: mos7.0-holdback + type: deb + uri: http://mirror.fuel-infra.org/mos-repos/ubuntu/7.0/ + - name: Auxiliary + priority: 1150 + section: main restricted + suite: auxiliary + type: deb + uri: http://10.20.0.2:8080/2015.1.0-7.0/ubuntu/auxiliary + storage: + ephemeral_ceph: + description: Configures Nova to store ephemeral volumes in RBD. This works best + if Ceph is enabled for volumes and images, too. Enables live migration of + all types of Ceph backed VMs (without this option, live migration will only + work with VMs launched from Cinder volumes). + label: Ceph RBD for ephemeral volumes (Nova) + type: checkbox + value: true + weight: 75 + images_ceph: + description: Configures Glance to use the Ceph RBD backend to store images. + If enabled, this option will prevent Swift from installing. + label: Ceph RBD for images (Glance) + restrictions: + - settings:storage.images_vcenter.value == true: Only one Glance backend could + be selected. + type: checkbox + value: true + weight: 30 + images_vcenter: + description: Configures Glance to use the vCenter/ESXi backend to store images. + If enabled, this option will prevent Swift from installing. + label: VMWare vCenter/ESXi datastore for images (Glance) + restrictions: + - action: hide + condition: settings:common.use_vcenter.value != true + - condition: settings:storage.images_ceph.value == true + message: Only one Glance backend could be selected. + type: checkbox + value: false + weight: 35 + iser: + description: 'High performance block storage: Cinder volumes over iSER protocol + (iSCSI over RDMA). This feature requires SR-IOV capabilities in the NIC, and + will use a dedicated virtual function for the storage network.' + label: iSER protocol for volumes (Cinder) + restrictions: + - settings:storage.volumes_lvm.value != true or settings:common.libvirt_type.value + != 'kvm' + - action: hide + condition: not ('experimental' in version:feature_groups) + type: checkbox + value: false + weight: 11 + metadata: + label: Storage + weight: 60 + objects_ceph: + description: Configures RadosGW front end for Ceph RBD. This exposes S3 and + Swift API Interfaces. If enabled, this option will prevent Swift from installing. + label: Ceph RadosGW for objects (Swift API) + restrictions: + - settings:storage.images_ceph.value == false + type: checkbox + value: false + weight: 80 + osd_pool_size: + description: Configures the default number of object replicas in Ceph. This + number must be equal to or lower than the number of deployed 'Storage - Ceph + OSD' nodes. + label: Ceph object replication factor + regex: + error: Invalid number + source: ^[1-9]\d*$ + type: text + value: '2' + weight: 85 + volumes_ceph: + description: Configures Cinder to store volumes in Ceph RBD images. + label: Ceph RBD for volumes (Cinder) + restrictions: + - settings:storage.volumes_lvm.value == true + type: checkbox + value: true + weight: 20 + volumes_lvm: + description: It is recommended to have at least one Storage - Cinder LVM node. + label: Cinder LVM over iSCSI for volumes + restrictions: + - settings:storage.volumes_ceph.value == true + type: checkbox + value: false + weight: 10 + syslog: + metadata: + label: Syslog + weight: 50 + syslog_port: + description: Remote syslog port + label: Port + regex: + error: Invalid syslog port + source: ^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$ + type: text + value: '514' + weight: 20 + syslog_server: + description: Remote syslog hostname + label: Hostname + type: text + value: '' + weight: 10 + syslog_transport: + label: Syslog transport protocol + type: radio + value: tcp + values: + - data: udp + description: '' + label: UDP + - data: tcp + description: '' + label: TCP + weight: 30 + workloads_collector: + enabled: + type: hidden + value: true + metadata: + label: Workloads Collector User + restrictions: + - action: hide + condition: 'true' + weight: 10 + password: + type: password + value: sbfn1c8b + tenant: + type: text + value: services + user: + type: text + value: fuel_stats_user
\ No newline at end of file diff --git a/deploy/templates/virtual_environment/conf/dha.yaml b/deploy/templates/virtual_environment/conf/dha.yaml new file mode 100644 index 000000000..b671dd311 --- /dev/null +++ b/deploy/templates/virtual_environment/conf/dha.yaml @@ -0,0 +1,44 @@ +title: Deployment Hardware Adapter (DHA) +# DHA API version supported +version: +created: +comment: Config for Virtual Environment + +# Adapter to use for this definition +adapter: libvirt + +# Node list. +# Mandatory property is id, all other properties are adapter specific. + +nodes: +- id: 1 + libvirtName: controller1 + libvirtTemplate: templates/virtual_environment/vms/controller.xml +- id: 2 + libvirtName: controller2 + libvirtTemplate: templates/virtual_environment/vms/controller.xml +- id: 3 + libvirtName: controller3 + libvirtTemplate: templates/virtual_environment/vms/controller.xml +- id: 4 + libvirtName: compute1 + libvirtTemplate: templates/virtual_environment/vms/compute.xml +- id: 5 + libvirtName: compute2 + libvirtTemplate: templates/virtual_environment/vms/compute.xml +- id: 6 + libvirtName: compute3 + libvirtTemplate: templates/virtual_environment/vms/compute.xml +- id: 7 + libvirtName: fuel-master + libvirtTemplate: templates/virtual_environment/vms/fuel.xml + isFuel: yes + username: root + password: r00tme + +virtNetConfDir: templates/virtual_environment/networks + +disks: + fuel: 50G + controller: 60G + compute: 50G
\ No newline at end of file diff --git a/deploy/templates/virtual_environment/conf/plugins_conf/.gitkeep b/deploy/templates/virtual_environment/conf/plugins_conf/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/deploy/templates/virtual_environment/conf/plugins_conf/.gitkeep diff --git a/deploy/templates/virtual_environment/networks/fuel1.xml b/deploy/templates/virtual_environment/networks/fuel1.xml new file mode 100644 index 000000000..7b2b15423 --- /dev/null +++ b/deploy/templates/virtual_environment/networks/fuel1.xml @@ -0,0 +1,12 @@ +<network> + <name>fuel1</name> + <forward mode='nat'> + <nat> + <port start='1024' end='65535'/> + </nat> + </forward> + <bridge name='fuel1' stp='on' delay='0'/> + <ip address='10.20.0.1' netmask='255.255.255.0'> + </ip> +</network> + diff --git a/deploy/templates/virtual_environment/networks/fuel2.xml b/deploy/templates/virtual_environment/networks/fuel2.xml new file mode 100644 index 000000000..615c92094 --- /dev/null +++ b/deploy/templates/virtual_environment/networks/fuel2.xml @@ -0,0 +1,5 @@ +<network> + <name>fuel2</name> + <bridge name='fuel2' stp='on' delay='0'/> +</network> + diff --git a/deploy/templates/virtual_environment/networks/fuel3.xml b/deploy/templates/virtual_environment/networks/fuel3.xml new file mode 100644 index 000000000..2383e6c1f --- /dev/null +++ b/deploy/templates/virtual_environment/networks/fuel3.xml @@ -0,0 +1,5 @@ +<network> + <name>fuel3</name> + <bridge name='fuel3' stp='on' delay='0'/> +</network> + diff --git a/deploy/templates/virtual_environment/networks/fuel4.xml b/deploy/templates/virtual_environment/networks/fuel4.xml new file mode 100644 index 000000000..5b69f912d --- /dev/null +++ b/deploy/templates/virtual_environment/networks/fuel4.xml @@ -0,0 +1,12 @@ +<network> + <name>fuel4</name> + <forward mode='nat'> + <nat> + <port start='1024' end='65535'/> + </nat> + </forward> + <bridge name='fuel4' stp='on' delay='0'/> + <ip address='172.16.0.1' netmask='255.255.255.0'> + </ip> +</network> + diff --git a/deploy/templates/virtual_environment/vms/compute.xml b/deploy/templates/virtual_environment/vms/compute.xml new file mode 100644 index 000000000..fbef4bda7 --- /dev/null +++ b/deploy/templates/virtual_environment/vms/compute.xml @@ -0,0 +1,91 @@ +<domain type='kvm'> + <name>compute</name> + <memory unit='KiB'>8388608</memory> + <currentMemory unit='KiB'>8388608</currentMemory> + <vcpu placement='static'>2</vcpu> + <os> + <type arch='x86_64' machine='pc-1.0'>hvm</type> + <boot dev='network'/> + <boot dev='hd'/> + <bios rebootTimeout='30000'/> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <cpu mode='custom' match='exact'> + <model fallback='allow'>SandyBridge</model> + <vendor>Intel</vendor> + <feature policy='require' name='vme'/> + <feature policy='require' name='dtes64'/> + <feature policy='require' name='vmx'/> + <feature policy='require' name='erms'/> + <feature policy='require' name='xtpr'/> + <feature policy='require' name='smep'/> + <feature policy='require' name='pcid'/> + <feature policy='require' name='est'/> + <feature policy='require' name='monitor'/> + <feature policy='require' name='smx'/> + <feature policy='require' name='tm'/> + <feature policy='require' name='acpi'/> + <feature policy='require' name='osxsave'/> + <feature policy='require' name='ht'/> + <feature policy='require' name='pdcm'/> + <feature policy='require' name='fsgsbase'/> + <feature policy='require' name='f16c'/> + <feature policy='require' name='ds'/> + <feature policy='require' name='tm2'/> + <feature policy='require' name='ss'/> + <feature policy='require' name='pbe'/> + <feature policy='require' name='ds_cpl'/> + <feature policy='require' name='rdrand'/> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/bin/kvm</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='raw'/> + <source file='disk.raw'/> + <target dev='vda' bus='virtio'/> + </disk> + <controller type='usb' index='0'> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <interface type='network'> + <source network='fuel1'/> + <model type='virtio'/> + </interface> + <interface type='network'> + <source network='fuel2'/> + <model type='virtio'/> + </interface> + <interface type='network'> + <source network='fuel3'/> + <model type='virtio'/> + </interface> + <interface type='network'> + <source network='fuel4'/> + <model type='virtio'/> + </interface> + <serial type='pty'> + <target port='0'/> + </serial> + <console type='pty'> + <target type='serial' port='0'/> + </console> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <graphics type='vnc' port='-1' autoport='yes' keymap='sv'/> + <sound model='ich6'> + </sound> + <video> + <model type='cirrus' vram='9216' heads='1'/> + </video> + <memballoon model='virtio'> + </memballoon> + </devices> +</domain> diff --git a/deploy/templates/virtual_environment/vms/controller.xml b/deploy/templates/virtual_environment/vms/controller.xml new file mode 100644 index 000000000..3ff28218d --- /dev/null +++ b/deploy/templates/virtual_environment/vms/controller.xml @@ -0,0 +1,91 @@ +<domain type='kvm'> + <name>controller</name> + <memory unit='KiB'>8388608</memory> + <currentMemory unit='KiB'>8388608</currentMemory> + <vcpu placement='static'>2</vcpu> + <os> + <type arch='x86_64' machine='pc-1.0'>hvm</type> + <boot dev='network'/> + <boot dev='hd'/> + <bios rebootTimeout='30000'/> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <cpu mode='custom' match='exact'> + <model fallback='allow'>SandyBridge</model> + <vendor>Intel</vendor> + <feature policy='require' name='vme'/> + <feature policy='require' name='dtes64'/> + <feature policy='require' name='vmx'/> + <feature policy='require' name='erms'/> + <feature policy='require' name='xtpr'/> + <feature policy='require' name='smep'/> + <feature policy='require' name='pcid'/> + <feature policy='require' name='est'/> + <feature policy='require' name='monitor'/> + <feature policy='require' name='smx'/> + <feature policy='require' name='tm'/> + <feature policy='require' name='acpi'/> + <feature policy='require' name='osxsave'/> + <feature policy='require' name='ht'/> + <feature policy='require' name='pdcm'/> + <feature policy='require' name='fsgsbase'/> + <feature policy='require' name='f16c'/> + <feature policy='require' name='ds'/> + <feature policy='require' name='tm2'/> + <feature policy='require' name='ss'/> + <feature policy='require' name='pbe'/> + <feature policy='require' name='ds_cpl'/> + <feature policy='require' name='rdrand'/> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/bin/kvm</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='raw'/> + <source file='disk.raw'/> + <target dev='vda' bus='virtio'/> + </disk> + <controller type='usb' index='0'> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <interface type='network'> + <source network='fuel1'/> + <model type='virtio'/> + </interface> + <interface type='network'> + <source network='fuel2'/> + <model type='virtio'/> + </interface> + <interface type='network'> + <source network='fuel3'/> + <model type='virtio'/> + </interface> + <interface type='network'> + <source network='fuel4'/> + <model type='virtio'/> + </interface> + <serial type='pty'> + <target port='0'/> + </serial> + <console type='pty'> + <target type='serial' port='0'/> + </console> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <graphics type='vnc' port='-1' autoport='yes' keymap='sv'/> + <sound model='ich6'> + </sound> + <video> + <model type='cirrus' vram='9216' heads='1'/> + </video> + <memballoon model='virtio'> + </memballoon> + </devices> +</domain> diff --git a/deploy/templates/virtual_environment/vms/fuel.xml b/deploy/templates/virtual_environment/vms/fuel.xml new file mode 100644 index 000000000..1a3286001 --- /dev/null +++ b/deploy/templates/virtual_environment/vms/fuel.xml @@ -0,0 +1,93 @@ +<domain type='kvm'> + <name>fuel</name> + <memory unit='KiB'>2097152</memory> + <currentMemory unit='KiB'>2097152</currentMemory> + <vcpu placement='static'>2</vcpu> + <resource> + <partition>/machine</partition> + </resource> + <os> + <type arch='x86_64' machine='pc-1.0'>hvm</type> + <boot dev='hd'/> + <boot dev='cdrom'/> + <bootmenu enable='no'/> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <cpu mode='custom' match='exact'> + <model fallback='allow'>SandyBridge</model> + <vendor>Intel</vendor> + <feature policy='require' name='vme'/> + <feature policy='require' name='dtes64'/> + <feature policy='require' name='vmx'/> + <feature policy='require' name='erms'/> + <feature policy='require' name='xtpr'/> + <feature policy='require' name='smep'/> + <feature policy='require' name='pcid'/> + <feature policy='require' name='est'/> + <feature policy='require' name='monitor'/> + <feature policy='require' name='smx'/> + <feature policy='require' name='tm'/> + <feature policy='require' name='acpi'/> + <feature policy='require' name='osxsave'/> + <feature policy='require' name='ht'/> + <feature policy='require' name='pdcm'/> + <feature policy='require' name='fsgsbase'/> + <feature policy='require' name='f16c'/> + <feature policy='require' name='ds'/> + <feature policy='require' name='tm2'/> + <feature policy='require' name='ss'/> + <feature policy='require' name='pbe'/> + <feature policy='require' name='ds_cpl'/> + <feature policy='require' name='rdrand'/> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/bin/kvm</emulator> + <disk type='block' device='cdrom'> + <driver name='qemu' type='raw'/> + <target dev='hdc' bus='ide'/> + <readonly/> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='raw'/> + <source file='disk.raw'/> + <target dev='vda' bus='virtio'/> + </disk> + <controller type='ide' index='0'> + </controller> + <controller type='usb' index='0'> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <interface type='network'> + <source network='fuel1'/> + <model type='virtio'/> + </interface> + <serial type='pty'> + <target port='0'/> + </serial> + <console type='pty'> + <target type='serial' port='0'/> + </console> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='sv'> + <listen type='address' address='127.0.0.1'/> + </graphics> + <sound model='ich6'> + </sound> + <video> + <model type='cirrus' vram='9216' heads='1'/> + </video> + <memballoon model='virtio'> + </memballoon> + </devices> + <seclabel type='dynamic' model='apparmor' relabel='yes'/> +</domain> + diff --git a/deploy/transplant_fuel_settings.py b/deploy/transplant_fuel_settings.py new file mode 100644 index 000000000..e57a4fbc6 --- /dev/null +++ b/deploy/transplant_fuel_settings.py @@ -0,0 +1,62 @@ +############################################################################### +# Copyright (c) 2015 Ericsson AB and others. +# szilard.cserey@ericsson.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 sys +import io +import yaml +from dea import DeploymentEnvironmentAdapter + +from common import ( + check_file_exists, +) + +ASTUTE_YAML = '/etc/fuel/astute.yaml' + + +def usage(): + print ''' + Usage: + python transplant_fuel_settings.py <deafile> + ''' + + +def parse_arguments(): + if len(sys.argv) != 2: + usage() + sys.exit(1) + dea_file = sys.argv[-1] + check_file_exists(dea_file) + return dea_file + + +def transplant(dea, astute): + fuel_conf = dea.get_fuel_config() + for key in fuel_conf.iterkeys(): + if key == 'ADMIN_NETWORK': + for skey in fuel_conf[key].iterkeys(): + astute[key][skey] = fuel_conf[key][skey] + else: + astute[key] = fuel_conf[key] + return astute + + +def main(): + dea_file = parse_arguments() + check_file_exists(ASTUTE_YAML) + dea = DeploymentEnvironmentAdapter(dea_file) + with io.open(ASTUTE_YAML) as stream: + astute = yaml.load(stream) + transplant(dea, astute) + with io.open(ASTUTE_YAML, 'w') as stream: + yaml.dump(astute, stream, default_flow_style=False) + + +if __name__ == '__main__': + main() |