diff --git a/.gitignore b/.gitignore
index af35fca762..cb04c27d0d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+!*j2
nosetests.xml
ansible.cfg
*.py[co]
@@ -15,4 +16,5 @@ hosts*
*infrared.cfg
*.egg-info/
tmp/
+playbooks/*.retry
diff --git a/README.rst b/README.rst
index 43923e11dd..d3b6fe15b8 100644
--- a/README.rst
+++ b/README.rst
@@ -25,16 +25,17 @@ So, After cloning repo from GitHub::
$ pip install -e .
-Conf
-====
-
-``infrared`` will look for ``infrared.cfg`` in the following order:
+.. note:: ``infrared`` will look for ``infrared.cfg`` in the following order:
#. In working directory: ``./infrared.cfg``
#. In user home directory: ``~/.infrared.cfg``
#. In system settings: ``/etc/infrared/infrared.cfg``
-.. note:: To specify a different directory or different filename, override the
+ If the configuration file ``infrared.cfg`` doesn't exist in any of
+ the locations above, the InfraRed project's dir will be used as the default
+ location for configurations.
+
+ To specify a different directory or different filename, override the
lookup order with ``IR_CONFIG`` environment variable::
$ IR_CONFIG=/my/config/file.ini ir-provision --help
@@ -74,22 +75,74 @@ The merging priority order is:
InfraRed input arguments
------------------------
-InfraRed accepts the next sources of the input arguments (in priority order):
+InfraRed extends the ``clg`` and ``argpars`` packages with the following types
+that need to be defined in `.spec` files:
+
+* **Value**: String values
+* **YamlFile**: Expects path to YAML files. Will search for files in the settings directory before trying to resolve
+ absolute path. For the argument name is "arg-name" and of subparser "SUBCOMMAND" of command "COMMAND", the default
+ search path would be::
+
+ settings_dir/COMMAND/SUBCOMMAND/arg/name/arg_value
+
+* **Topology**: Provisioners allow to dynamically define the provisioned
+ nodes topology. InfraRed provides several
+ 'mini' YAML files to describe different roles: ``controller``, ``compute``,
+ ``undercloud``, etc...
+ These 'mini' files are then merged into one topology file according to the
+ provided ``--topology-nodes`` argument value.
+
+ The ``--topology-nodes`` argument can have the following format:
+ * ``--topology-nodes=1_controller,1_compute``
+ * ``--topology-nodes=1_controller``
+ * ``--topology-nodes=3_controller,1_compute,1_undercloud``
+
+ InfraRed will read dynamic topology by following the next steps:
+ #. Split the topology value with ','.
+ #. Split each node with '_' and get pair (number, role). For every pair
+ look for the topology folder (configured in the infrared.cfg file) for
+ the appropriate mini file (controller.yaml, compute.yaml, etc). Load the
+ role the defined number of times into the settings.
+
+ .. note:: The default search path for topology files is
+ ``settings/provivisioner/topology``. Users can add their own topology
+ roles there and reference them on runtime
+
+These arguments will accept input from sources in the following priority
+order:
+
+#. Command line arguments:
+ ``ir-provision virsh --host-address=some.host.com --host-user=root``
+#. Environment variables: ``HOST_ADRRESS=earth.example.com ir-provision virsh --host-user=root``
+#. Predefined arguments in ini file specified using ``--from-file`` option::
+
+ ir-provision virsh --host-address=some.host.com --from-file=user.ini
-1. Command line arguments: ``ir-provision virsh --host=some.host.com --ssh_user=root``
-2. Predefined arguments in ini file. Use the --from-file option to specify ini file::
-
- ir-provision virsh --host=some.host.com --from-file=user.ini
-
cat user.ini
[virsh]
- ssh_user=root
- ssh_key=mkey.pm
+ host-user=root
+ host-key=mkey.pm
+
+#. Defaults defined in ``.spec`` file for each argument.
+
+ .. note:: The sample `ini` file with the default values can be generated with:
+ ``ir-povision virsh --generate-conf-file=virsh.ini``. Generated file will contain
+ all the default arguments values defined in the spec file.
+
+Arguments of the above types will be automatically injected into settings
+YAML tree in a nested dict from.
+
+Example:
+The input for ``ir-COMMAND`` and argument ``--arg-name=arg-value`` maps to:
+ .. code-block:: yaml
-3. Environment variables: ``HOST=earth ir-provision virsh --ssh_user=root``
+ COMMAND:
+ arg:
+ name: "arg-value"
-Command line arguments have the highest priority. All the undefined variables will be replaced by that arguments from file or from environment.
+"arg-value" can be a simple string or be resolved into a more advanced
+dictionary depending on the argument type in ``.spec`` file
Extra-Vars
----------
@@ -109,7 +162,7 @@ Add new Plugins
There are two steps that should be done when adding a new plugin to InfraRed:
-1. Creating a specification file:
+#. Creating a specification file:
InfraRed uses ArgParse wrapper module called 'clg' in order to create a parser that based on `spec` file
(YAML format file) containing the plugin options.
The spec file should be named as the new plugin name with '.spec' extension and located inside the plugin dir
@@ -117,16 +170,7 @@ There are two steps that should be done when adding a new plugin to InfraRed:
For more details on how to use this module, please visit the 'clg' module `homepage `_.
-2. Creating a default spec file (default.ini).
- This file should contain the default values for the command line arguments. All the default values should go under the name section names as a new plugin. Example::
-
- [virsh]
- topology=all-in-one.yml
- network=default.yml
- ssh-key=~/.ssh/id_rsa
- ssh-user=root
-
-3. Creating settings files.
+#. Creating settings files.
Settings files are files containing data which defines how the end result of the playbook execution will be
looked like. Settings file are file in YAML format, end with ".yml" extension. Those files located under the
plugin's dir which itself located under the 'settings' dir in the InfraRed project's dir.
@@ -134,3 +178,10 @@ There are two steps that should be done when adding a new plugin to InfraRed:
with other values, all are received by the user.
When adding a new plugin, there is a need to create those settings files containing the needed data for the
playbook execution.
+
+
+Known issues
+============
+
+#. PROBLEM: sshpass package cannot be installed during virsh provisioning.
+ SOLUTION: install rhos-release tool. Install osp-d with rhos-release: ``rhos-release 7-director``
diff --git a/cli/conf.py b/cli/conf.py
index b141bac2e7..a9035371bf 100644
--- a/cli/conf.py
+++ b/cli/conf.py
@@ -1,13 +1,18 @@
import ConfigParser
-import clg
+import exceptions
import os
-import yaml
-import yamlordereddictloader
-from cli import exceptions
+
from cli import utils
+from cli import logger
-DEFAULT_INI = 'default.ini'
+LOG = logger.LOG
+DEFAULT_CONF_DIRS = dict(
+ settings='settings',
+ modules='library',
+ roles='roles',
+ playbooks='playbooks'
+)
def load_config_file():
@@ -29,112 +34,23 @@ def load_config_file():
for path in (env_path, cwd_path, utils.USER_PATH, utils.SYSTEM_PATH):
if path is not None and os.path.exists(path):
_config.read(path)
- return _config
-
- conf_file_paths = "\n".join([cwd_path, utils.USER_PATH, utils.SYSTEM_PATH])
- raise exceptions.IRFileNotFoundException(
- conf_file_paths,
- "IR configuration not found. "
- "Please set it in one of the following paths:\n")
-
-
-def IniFileType(value):
- """
- The custom type for clg spec
- :param value: the argument value.
- :return: dict based on a provided ini file.
- """
- _config = ConfigParser.ConfigParser()
- _config.read(value)
-
- d = dict(_config._sections)
- for k in d:
- d[k] = dict(_config._defaults, **d[k])
- for key, value in d[k].iteritems():
- # check if we have lists
- value = utils.string_to_list(value, append_to_list=False)
- d[k][key] = value
- d[k].pop('__name__', None)
-
- return d
+ break
+ else:
+ LOG.warning("Configuration file not found, using InfraRed project dir")
+ project_dir = os.path.dirname(os.path.dirname(__file__))
+ _config.add_section('defaults')
+ for option, value in DEFAULT_CONF_DIRS.iteritems():
+ _config.set('defaults', option, os.path.join(project_dir, value))
-class SpecManager(object):
- """
- Holds everything related to specs.
- """
-
- SPEC_EXTENSION = '.spec'
+ # Validates settings dir exists
+ settings_dir = _config.get('defaults', 'settings')
+ if not os.path.exists(settings_dir):
+ raise exceptions.IRFileNotFoundException(
+ settings_dir,
+ "Settings directory doesn't exist: ")
- @classmethod
- def parse_args(cls, module_name, config, args=None):
- """
- Looks for all the specs for specified module
- and parses the commandline input arguments accordingly.
-
- :param module_name: the module name: installer|provisioner|tester
- """
- cmd = clg.CommandLine(cls._get_specs(module_name, config))
- res_args = vars(cmd.parse(args))
-
- # always load default values for command0
- default_file = os.path.join(
- config.get('defaults', 'settings'),
- module_name,
- res_args['command0'],
- DEFAULT_INI
- )
- defaults = IniFileType(default_file)[res_args['command0']]
-
- # override defaults with env variables
- for arg_name, arg_value in res_args.iteritems():
- upper_arg_name = arg_name.upper()
- if arg_value is None and upper_arg_name in os.environ:
- defaults[arg_name] = os.getenv(upper_arg_name)
-
- # override defaults with the ini file args
- if 'from-file' in res_args:
- file_args = res_args['from-file']
- if file_args is not None and res_args['command0'] in file_args:
- defaults = utils.dict_merge(
- file_args[res_args['command0']],
- defaults)
-
- # replace defaults with cli
- utils.dict_merge(res_args, defaults)
-
- return res_args
-
- @classmethod
- def _get_specs(cls, module_name, config):
- """
- Gets specs files as a dict from settings/ folder.
- :param module_name: the module name: installer|provisioner|tester
- """
- res = {}
- for spec_file in cls._get_all_specs(config, subfolder=module_name):
- spec = yaml.load(open(spec_file),
- Loader=yamlordereddictloader.Loader)
- utils.dict_merge(res, spec)
- return res
-
- @classmethod
- def _get_all_specs(cls, config, subfolder=None):
- root_dir = utils.validate_settings_dir(
- config.get('defaults', 'settings'))
- if subfolder:
- root_dir = os.path.join(root_dir, subfolder)
-
- res = []
- for dirpath, _, filenames in os.walk(root_dir):
- for filename in [f for f in filenames
- if f.endswith(cls.SPEC_EXTENSION)]:
- res.append(os.path.join(dirpath, filename))
-
- return res
+ return _config
config = load_config_file()
-
-# update clg types
-clg.TYPES.update({'IniFile': IniFileType})
diff --git a/cli/exceptions.py b/cli/exceptions.py
index ef9966936e..9e0326cfcf 100644
--- a/cli/exceptions.py
+++ b/cli/exceptions.py
@@ -77,3 +77,9 @@ class IRConfigurationException(IRException):
General exception for any kind of configuration issues.
"""
pass
+
+
+class IRInfiniteLookupException(IRException):
+ def __init__(self, value):
+ message = "Lookup circular reference detected for: {}".format(value)
+ super(IRInfiniteLookupException, self).__init__(message)
diff --git a/cli/execute.py b/cli/execute.py
index 887b3aa4ff..b9a58fe126 100644
--- a/cli/execute.py
+++ b/cli/execute.py
@@ -1,154 +1,90 @@
from os import path
+from collections import namedtuple
-import ansible.color
-import ansible.inventory
-import ansible.playbook
-import ansible.utils
-from ansible import callbacks
+# this import loads ansible.cfg
+import ansible.constants
+
+from ansible.parsing.dataloader import DataLoader
+from ansible.vars import VariableManager
+from ansible.inventory import Inventory
+from ansible.executor.playbook_executor import PlaybookExecutor
+from ansible.utils.display import Display
from cli import conf, exceptions, logger
LOG = logger.LOG
CONF = conf.config
-VERBOSITY = 0
-HOSTS_FILE = "hosts"
-PLAYBOOKS = ["provision", "install", "test", "collect-logs", "cleanup"]
-
-assert "playbooks" == path.basename(
- CONF.get('defaults', 'playbooks')), "Bad path to playbooks"
-
-
-# ansible-playbook
-# https://github.com/ansible/ansible/blob/devel/bin/ansible-playbook
-
-# From ansible-playbook
-def colorize(lead, num, color):
- """ Print 'lead' = 'num' in 'color' """
- if num != 0 and color is not None:
- return "%s%s%-15s" % (ansible.color.stringc(lead, color),
- ansible.color.stringc("=", color),
- ansible.color.stringc(str(num), color))
- else:
- return "%s=%-4s" % (lead, str(num))
+def ansible_playbook(playbook, verbose=2, settings=None,
+ inventory="local_hosts"):
+ """Wraps the 'ansible-playbook' CLI.
-
-def hostcolor(host, stats, color=True):
- if color:
- if stats['failures'] != 0 or stats['unreachable'] != 0:
- return "%-37s" % ansible.color.stringc(host, 'red')
- elif stats['changed'] != 0:
- return "%-37s" % ansible.color.stringc(host, 'yellow')
- else:
- return "%-37s" % ansible.color.stringc(host, 'green')
- return "%-26s" % host
-
-
-def ansible_playbook(playbook, args, inventory="local_hosts"):
- """
- Wraps the 'ansible-playbook' CLI.
- :playbook: the playbook to invoke
- :args: all arguments passed for the playbook
- :inventory: the inventory file to use, default: local_hosts
+ :param playbook: the playbook to invoke
+ :param verbose: Ansible verbosity level
+ :param settings: dict with Ansible variables.
+ :param inventory: the inventory file to use, default: local_hosts
"""
+ settings = settings or {}
- if not playbook:
- LOG.error("No playbook to execute (%s)" % PLAYBOOKS)
+ display = Display(verbosity=verbose)
+ import __main__ as main
+ setattr(main, "display", display)
+ if not playbook:
# TODO: remove all IRexceptions and change to regular Python exceptions
raise exceptions.IRFileNotFoundException
- print "Executing Playbook: %s" % playbook
-
- ansible.utils.VERBOSITY = args['verbose']
- path_to_playbook = path.join(CONF.get('defaults', 'playbooks'), playbook)
-
- # From ansible-playbook:
- stats = callbacks.AggregateStats()
- playbook_cb = callbacks.PlaybookCallbacks(verbose=ansible.utils.VERBOSITY)
- if args.get('step'):
- # execute step by step
- playbook_cb.step = args.step
-
- if args.get('start_at'):
- # start execution at a specific task
- playbook_cb.start_at = args.start_at
-
- runner_cb = callbacks.PlaybookRunnerCallbacks(
- stats,
- verbose=ansible.utils.VERBOSITY
- )
+ LOG.info("Executing Playbook: %s" % playbook)
+
+ loader = DataLoader()
+
+ # ------------------ Mocking ansible-playbook cli input ------------------
+ # These values were extracted from ansible-playbook runtime.
+ # todo(yfried): decide which options are hardcoded and which should be
+ # exposed to user
+ hacked_options = {'subset': None, 'ask_pass': False, 'listtags': None,
+ 'become_user': 'root', 'sudo': False,
+ 'private_key_file': None,
+ 'syntax': None, 'skip_tags': None, 'diff': False,
+ 'sftp_extra_args': '', 'check': False,
+ 'force_handlers': False,
+ 'remote_user': None, 'become_method': 'sudo',
+ 'vault_password_file': None, 'listtasks': None,
+ 'output_file': None, 'ask_su_pass': False,
+ 'new_vault_password_file': None,
+ 'listhosts': None, 'ssh_extra_args': '',
+ 'tags': 'all', 'become_ask_pass': False,
+ 'start_at_task': None,
+ 'flush_cache': None, 'step': None, 'module_path': None,
+ 'su_user': None, 'ask_sudo_pass': False,
+ 'su': False,
+ 'scp_extra_args': '', 'connection': 'smart',
+ 'ask_vault_pass': False, 'timeout': 30, 'become': False,
+ 'sudo_user': None, 'ssh_common_args': ''}
module_path = CONF.get('defaults', 'modules')
+ path_to_playbook = path.join(CONF.get('defaults', 'playbooks'), playbook)
- pb = ansible.playbook.PlayBook(
- # From ansible-playbook:
- playbook=path_to_playbook,
- inventory=ansible.inventory.Inventory(inventory),
- extra_vars=args['settings'],
- callbacks=playbook_cb,
- runner_callbacks=runner_cb,
- stats=stats,
- module_path=module_path
+ hacked_options.update(
+ module_path=module_path,
+ verbosity=verbose,
+ forks=ansible.constants.DEFAULT_FORKS,
+ remote_user=ansible.constants.DEFAULT_REMOTE_USER,
+ private_key_file=ansible.constants.DEFAULT_PRIVATE_KEY_FILE,
)
-
- failed_hosts = []
- unreachable_hosts = []
-
- if args['verbose']:
- ansible_cmd = ["ansible-playbook"]
- if module_path:
- ansible_cmd.append("-M " + module_path)
- ansible_cmd.append("-" + "v" * args['verbose'])
- ansible_cmd.append("-i " + inventory)
- extra_vars = args['output'] or ""
- ansible_cmd.append("--extra-vars @" + extra_vars)
- ansible_cmd.append(path_to_playbook)
- print "ANSIBLE COMMAND: " + " ".join(ansible_cmd)
-
- pb.run()
-
- hosts = sorted(pb.stats.processed.keys())
- callbacks.display(callbacks.banner("PLAY RECAP"))
- playbook_cb.on_stats(pb.stats)
-
- for host in hosts:
- status = pb.stats.summarize(host)
- if status['failures'] > 0:
- failed_hosts.append(host)
- if status['unreachable'] > 0:
- unreachable_hosts.append(host)
-
- retries = failed_hosts + unreachable_hosts
-
- if len(retries) > 0:
- filename = pb.generate_retry_inventory(retries)
- if filename:
- callbacks.display(
- " to retry, use: --limit @%s\n" % filename)
-
- for host in hosts:
- status = pb.stats.summarize(host)
-
- callbacks.display("%s : %s %s %s %s" % (
- hostcolor(host, status),
- colorize('ok', status['ok'], 'green'),
- colorize('changed', status['changed'], 'yellow'),
- colorize('unreachable', status['unreachable'], 'red'),
- colorize('failed', status['failures'], 'red')), screen_only=True)
-
- callbacks.display("%s : %s %s %s %s" % (
- hostcolor(host, status, False),
- colorize('ok', status['ok'], None),
- colorize('changed', status['changed'], None),
- colorize('unreachable', status['unreachable'], None),
- colorize('failed', status['failures'], None)), log_only=True)
-
- print ""
- if len(failed_hosts) > 0:
- raise exceptions.IRPlaybookFailedException(
- playbook, "Failed hosts: %s" % failed_hosts)
- if len(unreachable_hosts) > 0:
- raise exceptions.IRPlaybookFailedException(
- playbook, "Unreachable hosts: %s" % unreachable_hosts)
+ options = namedtuple('Options', hacked_options.keys())(**hacked_options)
+
+ passwords = dict(vault_pass='secret')
+ variable_manager = VariableManager()
+ variable_manager.extra_vars = settings
+ inventory = Inventory(loader=loader, variable_manager=variable_manager,
+ host_list=inventory)
+ variable_manager.set_inventory(inventory)
+
+ pbex = PlaybookExecutor(playbooks=[path_to_playbook], inventory=inventory,
+ variable_manager=variable_manager, loader=loader,
+ options=options, passwords=passwords)
+ results = pbex.run()
+ if results:
+ raise exceptions.IRPlaybookFailedException(playbook)
diff --git a/cli/install.py b/cli/install.py
index 8bf4565a94..c855bb0d3e 100755
--- a/cli/install.py
+++ b/cli/install.py
@@ -24,8 +24,7 @@ def get_settings_dir(entry_point, args=None):
:returns: path to settings dir. if args, return path to parser settings
dir. If args.command0 exists, return path to subparser settings dir.
"""
- settings_dir = utils.validate_settings_dir(
- CONF.get('defaults', 'settings'))
+ settings_dir = CONF.get('defaults', 'settings')
if args:
settings_dir = os.path.join(settings_dir, entry_point)
if hasattr(args, "command0"):
diff --git a/cli/provision.py b/cli/provision.py
index 04453a1815..c79474520c 100755
--- a/cli/provision.py
+++ b/cli/provision.py
@@ -1,8 +1,6 @@
#!/usr/bin/env python
-
import os
import logging
-from cli.install import set_network_template as set_network
import yaml
@@ -10,35 +8,72 @@
from cli import conf
from cli import utils
from cli import exceptions
+from cli import spec
import cli.yamls
import cli.execute
LOG = logger.LOG
CONF = conf.config
+APPLICATION = 'provisioner'
PROVISION_PLAYBOOK = "provision.yaml"
CLEANUP_PLAYBOOK = "cleanup.yaml"
NON_SETTINGS_OPTIONS = ['command0', 'verbose', 'extra-vars', 'output',
'input', 'dry-run', 'cleanup', 'inventory']
+def get_arguments_dict(spec_args):
+ """
+ Collect all ValueArgument args in dict according to arg names
+
+ some-key=value will be nested in dict as:
+
+ {"some": {
+ "key": value}
+ }
+
+ For `YamlFileArgument` value is path to yaml so file content
+ will be loaded as a nested dict.
+
+ :param spec_args: Dictionary based on cmd-line args parsed from spec file
+ :return: dict
+ """
+ settings_dict = {}
+ for _name, argument in spec_args.iteritems():
+ if isinstance(argument, spec.ValueArgument):
+ utils.dict_insert(settings_dict, argument.value,
+ *argument.arg_name.split("-"))
+ return settings_dict
+
+
class IRFactory(object):
"""
Creates and configures the IR applications.
"""
@classmethod
- def create(cls, app_name, config):
+ def create(cls, app_name, config, args=None):
"""
Create the application object
by module name and provided configuration.
+
+ :param app_name: str
+ :param config: dict. configuration details
+ :param args: list. If given parse it instead of stdin input.
"""
+ settings_dir = config.get('defaults', 'settings')
+
if app_name in ["provisioner", ]:
- args = conf.SpecManager.parse_args(app_name, config)
- cls.configure_environment(args)
- setting_dir = utils.validate_settings_dir(
- CONF.get('defaults', 'settings'))
- app_instance = IRApplication(app_name, setting_dir, args)
+ app_settings_dir = os.path.join(settings_dir, app_name)
+ spec_args = spec.parse_args(app_settings_dir, args)
+ cls.configure_environment(spec_args)
+
+ if spec_args.get('generate-conf-file', None):
+ LOG.debug('Conf file "{}" has been generated. Exiting'.format(
+ spec_args['generate-conf-file']))
+ app_instance = None
+ else:
+ app_instance = IRApplication(app_name, settings_dir, spec_args)
else:
raise exceptions.IRUnknownApplicationException(
@@ -54,6 +89,7 @@ def configure_environment(cls, args):
"""
if args['debug']:
LOG.setLevel(logging.DEBUG)
+ # todo(yfried): load exception hook now and not at init.
class IRSubCommand(object):
@@ -99,68 +135,9 @@ def get_settings_files(self):
self.settings_dir,
self.name + '.yml'))
- settings_files.extend(self._load_yaml_files())
-
LOG.debug("All settings files to be loaded:\n%s" % settings_files)
return settings_files
- def _load_yaml_files(self):
- # load directly from args
- settings_files = []
- for key, val in vars(self.args).iteritems():
- if val is not None and key not in NON_SETTINGS_OPTIONS:
- settings_file = os.path.join(
- self.settings_dir, key, val + '.yml')
- LOG.debug('Searching settings file for the "%s" key...' % key)
- if not os.path.isfile(settings_file):
- settings_file = utils.normalize_file(val)
- settings_files.append(settings_file)
- LOG.debug('"%s" was added to settings '
- 'files list as an argument '
- 'for "%s" key' % (settings_file, key))
- return settings_files
-
- def get_settings_dict(self):
- return {}
-
-
-class VirshCommand(IRSubCommand):
-
- def get_settings_dict(self):
-
- # todo(obaranov) this is virsh specific
- # rework that and make this part of lookup or something.
- image = dict(
- name=self.args['image-file'],
- base_url=self.args['image-server']
- )
- host = dict(
- ssh_host=self.args['host'],
- ssh_user=self.args['ssh-user'],
- ssh_key_file=self.args['ssh-key']
- )
-
- settings_dict = utils.dict_merge(
- {'provisioner': {'image': image}},
- {'provisioner': {'hosts': {'host1': host}}})
-
- # load network and image settings
- for arg_dir in ('network', 'topology'):
- if self.args[arg_dir] is None:
- raise exceptions.IRConfigurationException(
- "A value for for the '{}' "
- "argument should be provided!".format(arg_dir))
- with open(set_network(self.args[arg_dir], os.path.join(
- self.settings_dir, arg_dir))) as settings_file:
- settings = yaml.load(settings_file)
- utils.dict_merge(settings_dict, settings)
-
- return settings_dict
-
- def _load_yaml_files(self):
- # do not load additional yaml files.
- return []
-
class IRApplication(object):
"""
@@ -172,7 +149,7 @@ def __init__(self, name, settings_dir, args):
self.settings_dir = settings_dir
# todo(obaranov) replace with subcommand factory
- self.sub_command = VirshCommand.create(name, settings_dir, args)
+ self.sub_command = IRSubCommand.create(name, settings_dir, args)
def run(self):
"""
@@ -187,15 +164,24 @@ def run(self):
default_flow_style=False))
if self.args['cleanup']:
- cli.execute.ansible_playbook(CLEANUP_PLAYBOOK, self.args)
+ cli.execute.ansible_playbook(CLEANUP_PLAYBOOK,
+ verbose=self.args["verbose"],
+ settings=self.args["settings"])
else:
- cli.execute.ansible_playbook(PROVISION_PLAYBOOK, self.args)
+ cli.execute.ansible_playbook(PROVISION_PLAYBOOK,
+ verbose=self.args["verbose"],
+ settings=self.args["settings"])
def collect_settings(self):
settings_files = self.sub_command.get_settings_files()
- settings_dict = self.sub_command.get_settings_dict()
+ arguments_dict = {APPLICATION: get_arguments_dict(self.args)}
+
+ # todo(yfried): fix after lookup refactor
+ # utils.dict_merge(settings_files, arguments_dict)
+ # return self.lookup(settings_files)
- return self.lookup(settings_files, settings_dict)
+ # todo(yfried) remove this line after lookup refactor
+ return self.lookup(settings_files, arguments_dict)
def lookup(self, settings_files, settings_dict):
"""
@@ -205,6 +191,7 @@ def lookup(self, settings_files, settings_dict):
"""
cli.yamls.Lookup.settings = utils.generate_settings(settings_files)
+ # todo(yfried) remove this line after refactor
cli.yamls.Lookup.settings.merge(settings_dict)
cli.yamls.Lookup.settings = utils.merge_extra_vars(
cli.yamls.Lookup.settings,
@@ -228,8 +215,9 @@ def dump_settings(self, settings):
def main():
- app = IRFactory.create('provisioner', CONF)
- app.run()
+ app = IRFactory.create(APPLICATION, CONF)
+ if app:
+ app.run()
if __name__ == '__main__':
main()
diff --git a/cli/spec.py b/cli/spec.py
new file mode 100644
index 0000000000..442fb1994e
--- /dev/null
+++ b/cli/spec.py
@@ -0,0 +1,441 @@
+import ConfigParser
+
+import clg
+import os
+import yaml
+import yamlordereddictloader
+
+from cli import exceptions
+from cli import logger
+from cli import utils
+
+LOG = logger.LOG
+
+SPEC_EXTENSION = '.spec'
+BUILTINS_REPLACEMENT = {
+ '__DEFAULT__': "default"
+}
+
+TRIM_PARAMS = ['default', 'required']
+
+
+class ValueArgument(object):
+ """
+ Default argument type for InfraRed Spec
+ Resolves "None" Types with priority defaults.
+ """
+ settings_dir = None
+ subcommand = None
+
+ def __init__(self, value=None, required=None):
+ self.value = value
+ self.arg_name = None
+ self.required = None
+
+ @classmethod
+ def get_app_attr(cls, attribute):
+ """Get class attributes from ancestor tree.
+
+ If attribute isn't initialized, search ancestor tree for it and
+ initialize attribute with the first match found.
+
+ :param attribute: class attribute.
+ :return:
+ """
+ if getattr(cls, attribute):
+ return getattr(cls, attribute)
+ try:
+ setattr(cls, attribute,
+ super(ValueArgument, cls).get_app_attr(attribute))
+ except AttributeError:
+ pass
+ return getattr(cls, attribute)
+
+ @classmethod
+ def init_missing_args(cls, spec, args, settings_dir=None, subcommand=None):
+ """Initialize defaults for ValueArgument class.
+
+ argparse loads all unprovided arguments as None objects, instead of
+ the argument type specified in spec file. This method will set
+ argument types according to spec, so later we can iterate of arguments
+ by type.
+
+ :param spec: dict. spec file
+ :param args: dict. argparse arguments.
+ :param app_settings_dir: path to the base directory holding the
+ application's settings. App can be provisioner\installer\tester
+ and the path would be: settings//
+ """
+ cls.settings_dir = cls.get_app_attr("settings_dir") or settings_dir
+ cls.subcommand = cls.get_app_attr("subcommand") or subcommand
+
+ # todo(yfried): consider simply searching for option_name instead of
+ # "options"
+ for option_tree in utils.search_tree("options", spec):
+ for option_name, option_dict in option_tree.iteritems():
+ if option_name in args and issubclass(
+ clg.TYPES.get(option_dict.get(
+ "type", object),
+ object),
+ cls) and args[option_name] is None:
+ args[option_name] = clg.TYPES.get(option_dict["type"])(
+ required=option_dict.get("required")
+ )
+ # todo(yfried): this is a good place to trigger
+ # args[option_name].resolve_value()
+
+ def resolve_value(self, arg_name, defaults=None):
+ """
+ Resolve the argument value with alternative input origins
+
+ Search order:
+ 1. Given value from CLI parsing
+ 2. Environment variables:
+ [Aa-Zz] -> [A-Z]
+ "-" -> "_"
+ 3. defaults dict
+
+ :param arg_name: argument name from clg
+ :param defaults: dict containing default values.
+ """
+ defaults = defaults or {}
+ self.arg_name = arg_name
+ # override get default from env variables
+ # TODO (aopincar): IR env vars should be more uniques
+ if self.value is None:
+ self.value = os.getenv(self.arg_name.upper().replace("-", "_"))
+ # override get default from conf file
+ if self.value is None:
+ self.value = defaults.get(self.arg_name)
+
+
+class YamlFileArgument(ValueArgument):
+ """
+ YAML file input argument.
+ Loads legal YAML from file.
+ Will search for files in the settings directory before trying to resolve
+ absolute path.
+
+ For the argument name is "arg-name" and of subparser "SUBCOMMAND" of
+ application "APP", the default search path would be:
+
+ settings_dir/APP/SUBCOMMAND/arg/name/arg_value
+ """
+
+ def resolve_value(self, arg_name, defaults=None):
+ super(YamlFileArgument, self).resolve_value(arg_name, defaults)
+ search_first = os.path.join(self.get_app_attr("settings_dir"),
+ self.get_app_attr("subcommand"),
+ *arg_name.split("-"))
+ self.value = utils.load_yaml(self.value, search_first)
+
+
+class TopologyArgument(ValueArgument):
+ """Build topology dict from smaller YAML files by parsing input. """
+
+ def resolve_value(self, arg_name, defaults=None):
+ """
+ Merges topology files in a single topology dict.
+
+ :param clg_args: Dictionary based on cmd-line args parsed by clg
+ :param app_settings_dir: path to the base directory holding the
+ application's settings. App can be provisioner\installer\tester
+ and the path would be: settings//
+ """
+ super(TopologyArgument, self).resolve_value(arg_name, defaults)
+
+ # post process topology
+ topology_dir = os.path.join(self.get_app_attr("settings_dir"),
+ 'topology')
+ topology_dict = {}
+ for topology_item in self.value.split(','):
+ if '_' in topology_item:
+ number, node_type = topology_item.split('_')
+ else:
+ raise exceptions.IRConfigurationException(
+ "Topology node should be in format _. "
+ "Current value: '{}' ".format(topology_item))
+ # todo(obaraov): consider moving topology to config on constant.
+ topology_dict[node_type] = utils.load_yaml(node_type + ".yaml",
+ topology_dir)
+ topology_dict[node_type]['amount'] = int(number)
+
+ self.value = topology_dict
+
+
+class IniFileArgument(object):
+ """Creates a dictionary based on given configuration file."""
+
+ def __init__(self, value):
+ """Loads dict based on a provided configuration file.
+
+ :param value: Path to configuration file.
+ """
+
+ _config = ConfigParser.ConfigParser()
+ with open(value) as fd:
+ _config.readfp(fd)
+
+ res_dict = {}
+ for section in _config.sections():
+ res_dict[section] = {}
+ for option, val in _config.items(section):
+ # todo(obaranov) checking if val is list (for extra-vars).
+ # rework that check to be more beautiful later.
+ if val.startswith('[') and val.endswith(']'):
+ val = eval(str(val))
+ res_dict[section][option] = val
+
+ res_dict[section].pop('__name__', None)
+
+ self.value = res_dict
+
+
+def parse_args(app_settings_dir, args=None):
+ """
+ Looks for all the specs for specified app
+ and parses the commandline input arguments accordingly.
+
+ Trim clg spec from customized input and modify help data.
+
+ :param app_settings_dir: path to the base directory holding the
+ application's settings. App can be provisioner\installer\tester
+ and the path would be: settings//
+ :param args: the list of arguments used for directing the method to work
+ on something other than CLI input (for example, in testing).
+ :return: dict. Based on cmd-line args parsed from spec file
+ """
+ # Dict with the merging result of all app's specs
+ app_specs = _get_specs(app_settings_dir)
+
+ # Get the subparsers options as is with all the fields from app's specs.
+ # This also trims some custom fields from options to pass to clg.
+ subparsers_options = _get_subparsers_options(app_specs)
+
+ # Pass trimmed spec to clg with modified help message
+ cmd = clg.CommandLine(app_specs)
+ clg_args = vars(cmd.parse(args))
+ ValueArgument.init_missing_args(app_specs, clg_args, app_settings_dir,
+ subcommand=clg_args["command0"])
+
+ # Current sub-parser options
+ sub_parser_options = subparsers_options.get(clg_args['command0'], {})
+
+ override_default_values(clg_args, sub_parser_options)
+ return clg_args
+
+
+def override_default_values(clg_args, sub_parser_options):
+ """
+ Collects arguments values from the different sources and resolve values.
+
+ Each argument value is resolved in the following priority:
+ 1. Explicitly provided from cmd-line
+ 2. Environment variable
+ 3. Provided configuration file.
+ 4. Spec defaults
+
+ :param clg_args: Dictionary based on cmd-line args parsed by clg
+ :param sub_parser_options: the sub-parser spec options
+ """
+ # Get the sub-parser's default values
+ # todo(yfried): move to init_missing_args
+ defaults = {option: attributes['default']
+ for option, attributes in sub_parser_options.iteritems()
+ if 'default' in attributes}
+
+ # todo(yfried): move this outside
+ # Generate config file if required
+ if clg_args.get('generate-conf-file'):
+ _generate_config_file(
+ file_name=clg_args['generate-conf-file'],
+ subcommand=clg_args['command0'],
+ defaults=defaults)
+ else:
+ # Override defaults with the ini file args if provided
+ file_args = getattr(clg_args.get('from-file'), "value", {}).get(
+ clg_args['command0'], {})
+ utils.dict_merge(defaults, file_args)
+
+ # Resolve defaults and load values to clg_args
+ for arg_name, arg_obj in clg_args.iteritems():
+ if isinstance(arg_obj, ValueArgument):
+ arg_obj.resolve_value(arg_name, defaults)
+
+ _check_required_arguments(clg_args, sub_parser_options)
+
+
+def _check_required_arguments(clg_args, sub_parser_options):
+ """
+ Verify all the required arguments are set.
+
+ :param clg_args: Dictionary based on cmd-line args parsed by clg
+ :param sub_parser_options: Dictionary with all the options attributes for
+ a sub-command.
+ """
+ unset_args = []
+ for option, attributes in sub_parser_options.iteritems():
+ if attributes.get('required'):
+ # safely get the attribute provided value
+ value = clg_args.get(option, None)
+ if hasattr(value, 'value'):
+ value = value.value
+ if value is None:
+ unset_args.append(option)
+ if unset_args:
+ raise exceptions.IRConfigurationException(
+ "Required input arguments {} are not set!".format(unset_args))
+
+
+def _generate_config_file(file_name, subcommand, defaults):
+ """
+ Generates configuration file based on defaults from specs
+
+ :param file_name: Name of the new configuration that will be generated.
+ :param subcommand: The subparser for which the conf file is generated
+ :param defaults: the default options values.
+ """
+ # TODO(yfried): Add required arguments to file
+ # TODO (aopincar): try block is too wide
+ # TODO (aopincar): if file_name exists, update it instead of overwrite it
+ try:
+ out_config = ConfigParser.ConfigParser()
+
+ out_config.add_section(subcommand)
+ for opt, value in defaults.iteritems():
+ out_config.set(subcommand, opt, value)
+ with open(file_name, 'w') as configfile: # save
+ out_config.write(configfile)
+ except Exception as ex:
+ raise exceptions.IRException(ex.message)
+
+
+def _get_subparsers_options(spec_dict):
+ """
+ Goes through all the spec options and modifies them by removing some
+ options parameters (like defaults)
+
+ :param spec_dict: the dictionary with key obtained from spec files
+ """
+ # Collect sub parsers options
+ options = {}
+ for sub_parser, params in spec_dict.get('subparsers', {}).iteritems():
+ parser_options = _get_parser_options(params)
+ group_options = _get_parser_group_options(params)
+
+ utils.dict_merge(parser_options, group_options)
+ options[sub_parser] = parser_options
+
+ return options
+
+
+def _get_parser_group_options(spec_dict):
+ """
+ Gets the dict of options nested within the spec groups
+
+ :param spec_dict: the dictionary to look for new group options.
+ """
+ options = {}
+ for group in spec_dict.get('groups', {}):
+ options.update(_get_parser_options(group))
+
+ return options
+
+
+def _get_parser_options(spec_dict):
+ """
+ Gets the dict of options listed in the spec of group.
+
+ This method will also remove some methods and will replace
+ ____ pattens in the option properties (e.g. __default__, __FILE__)
+
+ :param spec_dict: the dictionary to look for new options.
+ """
+ options_dict = {}
+ for option, attributes in spec_dict.get('options', {}).iteritems():
+ _add_default_to_option_help(attributes)
+ _replace_builtin(attributes)
+
+ # Get a parameters copy with all the keys.
+ options_dict[option] = dict(attributes)
+
+ _trim_option(attributes)
+
+ return options_dict
+
+
+def _add_default_to_option_help(option_attributes):
+ """
+ Update the help by inserting default values if required.
+
+ :param option_attributes: dictionary with option attributes (help, type,
+ default, etc)
+ """
+ # Insert default value into help.
+ if all(attr in option_attributes for attr in ('help', 'default')) \
+ and '__DEFAULT__' not in option_attributes['help']:
+ option_attributes['help'] += " (default: {})".format(
+ option_attributes['default'])
+
+
+def _replace_builtin(option_attributes):
+ """
+ Modifies existing option parameters by replacing __*__ patterns
+
+ :param option_attributes: dictionary with option attributes (help, type,
+ default, etc)
+ """
+ # check __*__ pattern
+ for attr_key, attr_value in option_attributes.iteritems():
+ for builtin, replacement in BUILTINS_REPLACEMENT.iteritems():
+ if builtin in str(attr_value):
+ option_attributes[attr_key] = \
+ attr_value.replace(builtin, str(
+ option_attributes.get(replacement, builtin)))
+
+
+def _trim_option(option_attributes):
+ """
+ Removes the defined option parameters.
+
+ :param option_attributes: dictionary with option attributes (help, type,
+ default, etc)
+ """
+ for trim_param in TRIM_PARAMS:
+ option_attributes.pop(trim_param, None)
+
+
+def _get_specs(app_settings_dir):
+ """
+ Load all specs files from base settings directory.
+
+ :param app_settings_dir: path to the base directory holding the
+ application's settings. App can be provisioner\installer\tester
+ and the path would be: settings//
+ :return: dict: All spec files merged into a single dict.
+ """
+ if not os.path.exists(app_settings_dir):
+ raise exceptions.IRFileNotFoundException(app_settings_dir)
+
+ # Collect all app's spec
+ spec_files = []
+ for root, _, files in os.walk(app_settings_dir):
+ spec_files.extend([os.path.join(root, a_file) for a_file in files
+ if a_file.endswith(SPEC_EXTENSION)])
+
+ res = {}
+ for spec_file in spec_files:
+ # TODO (aopincar): print spec_file in debug mode
+ with open(spec_file) as fd:
+ spec = yaml.load(fd, Loader=yamlordereddictloader.Loader)
+ # TODO (aopincar): preserve OrderedDict when merging?!?
+ utils.dict_merge(res, spec)
+
+ return res
+
+
+# update clg types
+clg.TYPES.update({'IniFile': IniFileArgument})
+clg.TYPES.update({'Value': ValueArgument})
+clg.TYPES.update({'Topology': TopologyArgument})
+clg.TYPES.update({'YamlFile': YamlFileArgument})
diff --git a/cli/utils.py b/cli/utils.py
index 2eede9fccc..783178d3fd 100644
--- a/cli/utils.py
+++ b/cli/utils.py
@@ -3,12 +3,12 @@
"""
import os
-import re
import configure
import yaml
import cli.yamls
+import exceptions
from cli import exceptions
from cli import logger
@@ -44,62 +44,69 @@ class ConflictResolver(object):
@staticmethod
def none_resolver(first, second, key):
"""
- Replaces value in first dict only if it null
+ Replaces value in first dict only if it is None.
+ Appends second value into the first in type is list.
"""
# tyr to merge lists first
if isinstance(first[key], list):
- if isinstance(first[key], list):
+ if isinstance(second[key], list):
first[key].extend(second[key])
- else:
- first[key].append(first[key])
+ elif second[key] is not None:
+ first[key].append(second[key])
if key not in first or first[key] is None:
first[key] = second[key]
+ @staticmethod
+ def greedy_resolver(first, second, key):
+ """
+ Replace always first with the value from second
+ """
+ first[key] = second[key]
-def dict_merge(first, second, path=None,
- conflict_resolver=ConflictResolver.none_resolver):
- """ Given two dict objects, this function returns
- a dict that is the result of merging them into one.
+
+def dict_merge(first, second,
+ conflict_resolver=ConflictResolver.greedy_resolver):
+ """Merge `second` dict into `first`.
+
+ :param first: Modified dict
+ :param second: Modifier dict
+ :param conflict_resolver: Function that resolves a merge between 2 values
+ when one of them isn't a dict
"""
- if path is None:
- path = []
for key in second:
if key in first:
if isinstance(first[key], dict) and isinstance(second[key], dict):
- dict_merge(first[key], second[key], path + [str(key)])
- elif first[key] == second[key]:
- pass
+ dict_merge(first[key], second[key],
+ conflict_resolver=conflict_resolver)
else:
# replace first value with the value from second
conflict_resolver(first, second, key)
else:
first[key] = second[key]
- return first
-
-# TODO: remove "settings" references in project
-def validate_settings_dir(settings_dir=None):
- """Checks & returns the full path to the settings dir.
- Path is set in the following priority:
- 1. Method argument
- 2. System environment variable
+def search_tree(needle, haystack, _res=None):
+ """Find all values of key `needle` inside a nested dict tree `haystack`.
- :param settings_dir: path given as argument by a user
- :return: path to settings dir (str)
- :raise: IRFileNotFoundException: when the path to the settings dir doesn't
- exist
+ :param haystack: nested dict tree to search
+ :param needle: key name to search for
+ :param _res: helper argument holding return value for internal recursion
+ :return: list. All values of key `needle` in tree `haystack`. Order is not
+ guaranteed.
"""
- settings_dir = settings_dir or os.environ.get(INFRARED_DIR_ENV_VAR)
-
- if not os.path.exists(settings_dir):
- raise exceptions.IRFileNotFoundException(
- settings_dir,
- "Settings dir doesn't exist: ")
-
- return settings_dir
+ if _res is None:
+ _res = []
+ if needle in haystack:
+ _res.append(haystack[needle])
+ for key, value in haystack.iteritems():
+ if isinstance(value, dict):
+ search_tree(needle, value, _res)
+ if isinstance(value, list):
+ for item in value:
+ search_tree(needle, item, _res)
+ return _res
def update_settings(settings, file_path):
@@ -189,16 +196,25 @@ def normalize_file(file_path):
return file_path
-def string_to_list(value, separator=',', append_to_list=True):
- """
- Converts string to list.
+def load_yaml(filename, search_first):
+ """Find YAML file. search default path first.
+
+ :param filename: path to file
+ :param search_first: default path to search first
+ :returns: dict. loaded YAML file.
"""
- if value.startswith('[') and value.endswith(']'):
- value = value[1:-1].split(separator)
+ filename = os.path.join(search_first, filename) if os.path.exists(
+ os.path.join(search_first, filename)) else filename
+ if os.path.exists(os.path.abspath(filename)):
+ LOG.debug("Loading YAML file: %s" %
+ os.path.abspath(filename))
+ path = os.path.abspath(filename)
else:
- if append_to_list:
- value = [value, ]
- return value
+ raise exceptions.IRFileNotFoundException(
+ file_path=os.path.abspath(filename))
+ with open(path) as yaml_file:
+ return yaml.load(yaml_file)
+
ENV_VAR_NAME = "IR_CONFIG"
IR_CONF_FILE = 'infrared.cfg'
diff --git a/cli/yamls.py b/cli/yamls.py
index be50cc83dc..2c629aa861 100644
--- a/cli/yamls.py
+++ b/cli/yamls.py
@@ -141,12 +141,29 @@ def replace_lookup(self):
break
for a_lookup in lookups:
- lookup_key = re.search('(\w+\.?)+ *?\}\}', a_lookup)
- lookup_key = lookup_key.group(0).strip()[:-2].strip()
- lookup_value = self.dict_lookup(lookup_key.split("."))
-
- if isinstance(lookup_value, Lookup):
- return
+ visited_lookups = []
+ lookup_value = a_lookup
+ while True:
+ lookup_key = re.search('(\w+\.?)+ *?\}\}', a_lookup)
+ if lookup_key is None:
+ # if key cannot be found in lookup consider
+ # it as a value and exit
+ break
+
+ lookup_key = lookup_key.group(0).strip()[:-2].strip()
+ # check if lookups are in circular reference
+ if lookup_key in visited_lookups:
+ raise exceptions.IRInfiniteLookupException(
+ visited_lookups)
+ visited_lookups.append(lookup_key)
+ lookup_value = self.dict_lookup(lookup_key.split("."))
+
+ if not isinstance(lookup_value, Lookup):
+ # value was resolved
+ break
+ else:
+ # we have another lookup
+ a_lookup = lookup_value.key
lookup_value = str(lookup_value)
diff --git a/playbooks/installer/ospd/overcloud/post.yml b/playbooks/installer/ospd/overcloud/post.yml
index c6d2838350..d8d8d9e3de 100644
--- a/playbooks/installer/ospd/overcloud/post.yml
+++ b/playbooks/installer/ospd/overcloud/post.yml
@@ -22,12 +22,6 @@
path: "{{ inventory_dir }}/id_rsa_overcloud"
mode: 0600
- - name: update our ansible ssh configuration file
- template:
- src: "templates/ssh.config.ansible.j2"
- dest: "{{ inventory_dir }}/ansible.ssh.config"
- mode: 0755
-
- name: Update Inventory
add_host:
name: "{{ item }}"
diff --git a/playbooks/installer/ospd/overcloud/pre.yml b/playbooks/installer/ospd/overcloud/pre.yml
index e8169f2fa5..a046063cd6 100644
--- a/playbooks/installer/ospd/overcloud/pre.yml
+++ b/playbooks/installer/ospd/overcloud/pre.yml
@@ -7,13 +7,23 @@
become: yes
become_user: "{{ installer.user.name }}"
pre_tasks:
+ - name: copy our public key to inject the images of the overcloud nodes
+ copy:
+ src: "{{ inventory_dir }}/id_rsa.pub"
+ dest: "/home/{{ installer.user.name }}/id_rsa.pub"
+ mode: 0600
+
- fail:
msg: "Stopping before building/importing the images per user request"
when: break is defined and break == "before_images"
+
roles:
- - {role: ospd/overcloud/images/build, when: installer.images.task == "build"}
- - {role: ospd/overcloud/images/import, when: installer.images.task == "import"}
+ - {role: installer/ospd/images/build, when: installer.images.task == "build"}
+ - {role: installer/ospd/images/import, when: installer.images.task == "import"}
tasks:
+ - name: remove tar files for space
+ shell: "rm -f *.tar"
+
- name: upload the overcloud images to glance
shell: "source ~/stackrc; openstack overcloud image upload"
@@ -39,33 +49,21 @@
ResultActive=yes
dest: "/etc/polkit-1/localauthority/50-local.d/50-libvirt-user-{{ installer.user.name }}.pkla"
- - name: make sure sshpass installed
- yum:
- name: sshpass
- state: latest
- delegate_to: undercloud
-
- - name: SSH copy ID to the hypervisor
+ - name: copy our undercloud public key
become: yes
become_user: "{{ installer.user.name }}"
- shell: "sshpass -p '{{ installer.user.password }}' ssh-copy-id {{ installer.user.name }}@{{ provisioner.network.network_list.management.ip_address }} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
+ fetch:
+ src: "/home/{{ installer.user.name }}/.ssh/id_rsa.pub"
+ dest: "{{ inventory_dir }}/id_rsa_undercloud.pub"
+ flat: yes
delegate_to: undercloud
- - name: "DEBUG - print mac addresses for hosts - DEBUG"
- shell: "virsh dumpxml {{ item }} | grep mac"
- with_items: groups['openstack_nodes'] | difference(["virthost"]) | difference(["undercloud"])
- ignore_errors: true
-
- - name: Print the relevant openstack_nodes
- debug:
- msg: "{{ item }}"
- with_items: groups['openstack_nodes'] | difference(["virthost"]) | difference(["undercloud"])
-
- - name: Print the MAC addresses for instackenv.json
- debug:
- msg: "MAC address for instackenv.json: {{ hostvars[item]['ansible_%s' % installer.undercloud.config.local_interface].macaddress }}"
- with_items: groups['openstack_nodes'] | difference(["virthost"]) | difference(["undercloud"])
- ignore_errors: true
+ - name: Add the undercloud public key to the virthost
+ authorized_key:
+ user: "{{ installer.user.name }}"
+ key: "{{ item }}"
+ with_file:
+ - "{{ inventory_dir }}/id_rsa_undercloud.pub"
- name: prepare instack.json
become: yes
@@ -81,13 +79,6 @@
shell: "cat ~/instack_template.json | jq --arg key \"$(cat ~/.ssh/id_rsa)\" '. | .nodes[].pm_password=$key | .[\"ssh-key\"]=$key'> instackenv.json"
delegate_to: undercloud
- - name: "DEBUG - print instackenv.json - DEBUG"
- become: yes
- become_user: "{{ installer.user.name }}"
- shell: "cat ~/instack_template.json"
- delegate_to: undercloud
- ignore_errors: true
-
- name: Validate our instackenv.json file
hosts: undercloud
gather_facts: no
@@ -105,13 +96,6 @@
- name: register our hosts to instack
shell: "source ~/stackrc; openstack baremetal import --json instackenv.json"
- - fail:
- msg: "Stopping before kernel and ramdisk assignment per user request"
- when: break is defined and break == "before_kernel_ramdisk_assignment"
-
- - name: assign the kernel and ramdisk before introspection begins
- shell: "source ~/stackrc; openstack baremetal configure boot"
-
# In case of virthost we need to fix the pxe_ssh limitation of correctly assigning the MAC address to the iPXE script
- name: Fixing the pxe_ssh and iPXE
hosts: virthost
@@ -147,6 +131,13 @@
become_user: "{{ installer.user.name }}"
gather_facts: no
tasks:
+ - fail:
+ msg: "Stopping before kernel and ramdisk assignment per user request"
+ when: break is defined and break == "before_kernel_ramdisk_assignment"
+
+ - name: assign the kernel and ramdisk before introspection begins
+ shell: "source ~/stackrc; openstack baremetal configure boot"
+
- fail:
msg: "Stopping before introspection per user request"
when: break is defined and break == "before_introspection"
@@ -179,11 +170,11 @@
shell: "source ~/stackrc; openstack flavor set --property 'cpu_arch'='x86_64' --property 'capabilities:boot_option'='local' baremetal"
- name: create the flavors for our machines
- shell: "source ~/stackrc; openstack flavor create --id auto --ram {{ item.value.memory | int - 100 }} --disk {{ item.value.disks.disk1.size | regex_replace('(?P[0-9]+).*$', '\\g') | int - 10 }} --vcpus {{ item.value.cpu }} {{ item.key }}-{{ item.key | to_uuid }}"
+ shell: "source ~/stackrc; openstack flavor create --id auto --ram {{ item.value.memory | int - 100 }} --disk {{ item.value.disks.disk1.size | regex_replace('(?P[0-9]+).*$', '\\g') | int - 10 }} --vcpus {{ item.value.cpu | int - 1 }} {{ item.key }}-{{ item.key | to_uuid }}"
register: flavor_result
when: "'{{ item.key }}' != 'undercloud'"
failed_when: "result.rc != 0 and result.stderr.find('Flavor with name {{ item.key }} already exists') != -1"
- with_dict: provisioner.nodes
+ with_dict: provisioner.topology.nodes
- set_fact:
tagged_flavors: "{{ flavor_result.results }}"
diff --git a/playbooks/installer/ospd/overcloud/run.yml b/playbooks/installer/ospd/overcloud/run.yml
index baad40cc12..5ce39a03e7 100644
--- a/playbooks/installer/ospd/overcloud/run.yml
+++ b/playbooks/installer/ospd/overcloud/run.yml
@@ -18,9 +18,10 @@
dest: "~/overcloud_deploy.sh"
mode: 0755
roles:
- - {role: "ospd/overcloud/storage/ceph/", when: provisioner.nodes.ceph is defined or installer.overcloud.storage.external == "yes"}
- - {role: "ospd/overcloud/network/isolation/{{ installer.overcloud.network.isolation.type }}/", when: installer.overcloud.network.isolation.enable == "yes"}
- - {role: "ospd/overcloud/ssl/", when: installer.overcloud.use_ssl == "yes"}
+ - {role: "installer/ospd/overcloud/storage/ceph/", when: installer.overcloud.storage.backend is defined and installer.overcloud.storage.backend == "ceph"}
+ - {role: "installer/ospd/overcloud/storage/netapp/", when: installer.overcloud.storage.backend is defined and installer.overcloud.storage.backend == "netapp"}
+ - {role: "installer/ospd/overcloud/network/isolation/{{ installer.overcloud.network.isolation.type }}/", when: installer.overcloud.network.isolation.enable == "yes"}
+ - {role: "installer/ospd/overcloud/ssl/", when: installer.overcloud.use_ssl == "yes"}
- name: Install the overcloud
hosts: undercloud
diff --git a/playbooks/installer/ospd/overcloud/templates/ssh.config.ansible.j2 b/playbooks/installer/ospd/overcloud/templates/ssh.config.ansible.j2
deleted file mode 100644
index 43c9b85ff0..0000000000
--- a/playbooks/installer/ospd/overcloud/templates/ssh.config.ansible.j2
+++ /dev/null
@@ -1,19 +0,0 @@
-#TODO: take the HostName IP from external rather than default
-{% for host in groups['all'] %}
-{% if hostvars[host].get('ansible_connection', '') != 'local' and host != 'virthost' %}
-Host {{ host }}
- ProxyCommand ssh -i {{ provisioner.hosts.host1.ssh_key_file }} {{ provisioner.hosts.host1.ssh_user }}@{{ provisioner.hosts.host1.ssh_host }} nc %h %p
- HostName {{ hostvars[host].ansible_default_ipv4.address }}
-{% if host == 'undercloud' %}
- User root
- IdentityFile {{ inventory_dir }}/id_rsa
-{% else %}
- User heat-admin
- IdentityFile {{ inventory_dir }}/id_rsa_overcloud
-{% endif %}
- StrictHostKeyChecking no
- UserKnownHostsFile=/dev/null
- ForwardAgent yes
-
-{% endif %}
-{% endfor %}
diff --git a/playbooks/installer/ospd/overcloud/templates/virthost_instack.json.j2 b/playbooks/installer/ospd/overcloud/templates/virthost_instack.json.j2
index e829c99433..3aeb13e8c0 100644
--- a/playbooks/installer/ospd/overcloud/templates/virthost_instack.json.j2
+++ b/playbooks/installer/ospd/overcloud/templates/virthost_instack.json.j2
@@ -2,20 +2,20 @@
"ssh-user": "{{ installer.user.name }}",
"ssh-key": "$(cat ~/.ssh/id_rsa)",
"power_manager": "nova.virt.baremetal.virtual_power_driver.VirtualPowerManager",
- "host-ip": "{{ provisioner.network.network_list.management.ip_address }}",
+ "host-ip": "{{ provisioner.topology.network.management.ip_address }}",
"arch": "x86_64",
"nodes": [
{% for host_name in groups['openstack_nodes'] %}
{% if host_name != 'undercloud' %}
{
"name": "{{ hostvars[host_name].inventory_hostname }}",
- "pm_addr": "{{ provisioner.network.network_list.management.ip_address }}",
+ "pm_addr": "{{ provisioner.topology.network.management.ip_address }}",
"pm_password": "$(cat ~/.ssh/id_rsa)",
"pm_type": "pxe_ssh",
"mac": ["{{ hostvars[host_name]['ansible_%s' % installer.undercloud.config.local_interface].macaddress }}"],
- "cpu": "{{ provisioner.nodes[host_name.rstrip('1234567890')].cpu }}",
- "memory": "{{ provisioner.nodes[host_name.rstrip('1234567890')].memory }}",
- "disk": "{{ provisioner.nodes[host_name.rstrip('1234567890')].disks.disk1.size | regex_replace('(?P[0-9]+).*$', '\\g') }}",
+ "cpu": "{{ provisioner.topology.nodes[host_name.rstrip('1234567890')].cpu }}",
+ "memory": "{{ provisioner.topology.nodes[host_name.rstrip('1234567890')].memory }}",
+ "disk": "{{ provisioner.topology.nodes[host_name.rstrip('1234567890')].disks.disk1.size | regex_replace('(?P[0-9]+).*$', '\\g') }}",
"arch": "x86_64",
"pm_user": "{{ installer.user.name }}"
}{% if not loop.last %},{% endif %}
diff --git a/playbooks/provision.yaml b/playbooks/provision.yaml
index 13605fd47a..961e767383 100644
--- a/playbooks/provision.yaml
+++ b/playbooks/provision.yaml
@@ -33,21 +33,16 @@
state: link
src: "{{ lookup('env', 'PWD') }}/hosts-{{ lookup('env', 'USER') }}"
-- name: extend hosts file
- hosts: openstack_nodes
+- name: Check SSH connection
+ hosts: all:!localhost
gather_facts: no
- sudo: yes
tasks:
- - name: Template of nodes
- template:
- dest: "/tmp/ansible_hosts"
- src: provisioner/templates/hosts.j2
- register: templatehosts
- when: provisioner.network.domain is defined
-
- - name: Update hosts file
- shell: "cat /tmp/ansible_hosts >> /etc/hosts"
- when: templatehosts|changed
+ - name: wait for hosts to be reachable
+ wait_for:
+ host: "{{ ansible_ssh_host }}"
+ search_regex: OpenSSH
+ delay: 10
+ delegate_to: localhost
- name: Break point
hosts: localhost
diff --git a/playbooks/provisioner/templates/hosts.j2 b/playbooks/provisioner/templates/hosts.j2
deleted file mode 100644
index ba9dca649f..0000000000
--- a/playbooks/provisioner/templates/hosts.j2
+++ /dev/null
@@ -1,5 +0,0 @@
-{% for host in groups['all'] %}
-{% if hostvars[host].get('ansible_connection', '') != 'local' %}
-{{ hostvars[host]['ansible_ssh_host'] }} {{ host }} {{ host }}{{ provisioner.network.domain }}
-{% endif %}
-{% endfor %}
diff --git a/playbooks/provisioner/virsh/cleanup.yaml b/playbooks/provisioner/virsh/cleanup.yaml
index 749eab3269..b2fed29365 100644
--- a/playbooks/provisioner/virsh/cleanup.yaml
+++ b/playbooks/provisioner/virsh/cleanup.yaml
@@ -2,16 +2,23 @@
- name: Add host to host list
hosts: localhost
gather_facts: no
+ vars:
+ hypervisor: "{{ provisioner.host }}"
tasks:
+ - name: remove ansible with the new SSH settings
+ lineinfile:
+ state: absent
+ dest: "{{ inventory_dir }}/ansible.cfg"
+ line: "ssh_args = -o ForwardAgent=yes -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=30m -F {{ inventory_dir }}/ansible.ssh.config"
+
- name: add hosts to host list
add_host:
- name="{{ item.value.name }}"
- groups="{{ item.value.groups| join(',') }}"
- node_label="{{ item.key }}"
- ansible_ssh_user="{{ item.value.ssh_user }}"
- ansible_ssh_host="{{ item.value.ssh_host }}"
- ansible_ssh_private_key_file="{{ item.value.ssh_key_file }}"
- with_dict: provisioner.hosts
+ name: "{{ hypervisor.name }}"
+ groups: "{{ hypervisor.groups| join(',') }}"
+ node_label: "host1"
+ ansible_ssh_user: "{{ hypervisor.user }}"
+ ansible_ssh_host: "{{ hypervisor.address }}"
+ ansible_ssh_private_key_file: "{{ hypervisor.key }}"
- name: Remove all VMs and networks that were created
hosts: virthost
@@ -50,12 +57,12 @@
- name: remove the networks we created
virt_net:
- name: "{{ item.value.name }}"
+ name: "{{ item }}"
state: absent
- with_dict: provisioner.network.network_list
+ with_items: provisioner.topology.network.keys()
- name: remove any existing disks that we created
shell: "rm -f {{ item.key }}*disk*.qcow*"
args:
chdir: "/var/lib/libvirt/images"
- with_dict: provisioner.nodes
\ No newline at end of file
+ with_dict: provisioner.topology.nodes
diff --git a/playbooks/provisioner/virsh/main.yaml b/playbooks/provisioner/virsh/main.yaml
index 6c6c2e81ba..ef6e14c021 100644
--- a/playbooks/provisioner/virsh/main.yaml
+++ b/playbooks/provisioner/virsh/main.yaml
@@ -5,287 +5,23 @@
tasks:
- name: add hosts to host list
add_host:
- name="{{ item.value.name }}"
- groups="{{ item.value.groups| join(',') }}"
- node_label="{{ item.key }}"
- ansible_ssh_user="{{ item.value.ssh_user }}"
- ansible_ssh_host="{{ item.value.ssh_host }}"
- ansible_ssh_private_key_file="{{ item.value.ssh_key_file }}"
- with_dict: provisioner.hosts
-
-- name: Install dependencies
- hosts: virthost
- gather_facts: no
- sudo: yes
- tasks:
- - name: install packages
- yum:
- name: "{{ item }}"
- state: present
- with_items: "{{ provisioner.packages }}"
-
- - name: stop libvirtd
- service:
- name: libvirtd
- state: stopped
- enabled: no
-
- - name: start libvirtd
- service:
- name: libvirtd
- state: started
- enabled: yes
-
-- name: Check if virtualization is supported
- hosts: virthost
- gather_facts: no
- sudo: yes
- tasks:
- - name: check if CPU supports INTEL based KVM
- shell: egrep -c 'vmx' /proc/cpuinfo
- ignore_errors: True
- register: kvm_intel
-
- - name: set fact for Intel based KVM
- set_fact:
- kvm_base: "intel"
- when: kvm_intel == 0
-
- - name: check if CPU supports AMD based KVM
- shell: egrep -c 'svm' /proc/cpuinfo
- ignore_errors: True
- register: kvm_amd
-
- - name: set fact for AMD based KVM
- set_fact:
- kvm_base: "amd"
- when: kvm_amd == 0
-
-- name: Enable KVM for intel
- hosts: virthost
- gather_facts: no
- sudo: yes
- tasks:
- - name: enable nested KVM support for Intel
- lineinfile:
- dest: "/etc/modprobe.d/dist.conf"
- line: "options kvm_{{ kvm_base }} nested=1"
- state: present
- create: yes
- when: kvm_base is defined
-
- - name: enable nested KVM support for AMD
- lineinfile:
- dest: "/etc/modprobe.d/dist.conf"
- line: "options {{ kvm_base }} nested=1"
- state: present
- create: yes
- when: kvm_base is defined
-
- # A change in the modprove requires to reload the module
- - name: unload KVM module
- modprobe:
- name: "kvm_{{ kvm_base }}"
- state: absent
- ignore_errors: True
- when: kvm_base is defined
-
- - name: load KVM module
- modprobe:
- name: "kvm_{{ kvm_base }}"
- state: present
- ignore_errors: True
- when: kvm_base is defined
-
- - name: install required QEMU-KVM packages
- yum: name=qemu-kvm state=present
- when: kvm_base is defined
-
- # Make sure the net-virtio module is enabled
- - name: unload vhost-net module
- modprobe:
- name: "vhost-net"
- state: absent
- ignore_errors: True
- when: kvm_base is defined
-
- - name: load KVM module
- modprobe:
- name: "vhost-net"
- state: present
- ignore_errors: True
-
-- name: Validate virtualization supported on host
+ name: "{{ provisioner.host.name }}"
+ groups: "{{ provisioner.host.groups| join(',') }}"
+ node_label: "virthost"
+ ansible_ssh_user: "{{ provisioner.host.user }}"
+ ansible_ssh_host: "{{ provisioner.host.address }}"
+ ansible_ssh_private_key_file: "{{ provisioner.host.key }}"
+
+- name: Setup the virthost
hosts: virthost
- gather_facts: no
- sudo: yes
- tasks:
- - command: "virt-host-validate"
- register: result
-
- - debug:
- var: result.stdout_lines
-
- - fail:
- msg: "System does not support virtualization"
- when: "'FAIL' in result.stdout_lines"
-
-- name: Create the topology requested
- hosts: virthost
- gather_facts: no
- sudo: yes
- tasks:
- - name: generating RSA key for root
- user:
- name: root
- generate_ssh_key: yes
- ssh_key_file: ".ssh/virthost"
-
- - name: check for existing networks
- virt_net:
- command: list_nets
- register: network_list
-
- - name: create the networks for the topology
- virt_net:
- command: define
- name: "{{ item.value.name }}"
- xml: "{{ lookup('template', 'network/network.xml.j2') }}"
- when: "item.value.name not in network_list.list_nets"
- with_dict: provisioner.network.network_list
-
- - name: check if network is active
- virt_net:
- state: active
- name: "{{ item.value.name }}"
- with_dict: provisioner.network.network_list
-
- - name: make network persistent
- virt_net:
- autostart: "yes"
- name: "{{ item.value.name }}"
- with_dict: provisioner.network.network_list
-
- - name: remove pre-existing file
- file:
- state: absent
- dest: "/var/lib/libvirt/images/{{ provisioner.image.name }}-original"
-
- - name: download base guest image
- get_url:
- dest: "/var/lib/libvirt/images/{{ provisioner.image.name }}-original"
- url: "{{ provisioner.image.base_url }}/{{ provisioner.image.name }}"
- register: result
- until: result.msg.find("Request failed") == -1
- retries: 5
- delay: 5
-
- - name: create image templates for nodes
- template:
- dest: "~/create_images.sh"
- src: "templates/create_images.sh.j2"
- mode: 0755
-
- - name: the create images script
- shell: "cat ~/create_images.sh"
-
- - name: execute the create images script
- shell: "bash ~/create_images.sh"
-
- - name: create virt install templates for nodes
- template:
- dest: "~/create_vms.sh"
- src: "templates/create_vms.sh.j2"
- mode: 0755
- #TODO: move this logic to a module
- - name: the create vms script
- shell: "cat ~/create_vms.sh"
-
- - name: execute the create vms script
- shell: "bash ~/create_vms.sh"
-
- - name: get the list of VMs
- shell: "virsh list --all | grep -P '[\\w]+' | sed -n '2,$p' | awk '{print $2}'"
- register: vm_names
-
- - set_fact:
- vm_name_list: "{{ vm_names.stdout_lines }}"
-
- - name: get MAC list
- shell: "virsh domiflist {{ item[0] }} | awk '/{{ item[1] }}/ {print $5};'"
- with_nested:
- - vm_name_list
- - provisioner.network.network_list
- register: mac_list
-
- - set_fact:
- vm_mac_list: "{{ mac_list.results }}"
-
- - name: wait until one of the VMs gets an IP
- shell: "virsh net-dhcp-leases {{ provisioner.network.network_list['%s' % item.item[1]].name }} | awk /{{ item.stdout }}/'{print $5}' | cut -d'/' -f1"
- when: item.stdout != ""
- register: ip_list
- until: ip_list.stdout.find("{{ provisioner.network.network_list['%s' % item.item[1]].ip_address | truncate(7, True, '') }}") > -1
- retries: 40
- delay: 5
- with_items: vm_mac_list
-
- - set_fact:
- vm_ip_list: "{{ ip_list.results }}"
-
- - name: add hosts to host list
- add_host:
- name="{{ item.item.item[0] }}"
- groups="{{ provisioner.nodes['%s' % item.item.item[0].rstrip('1234567890')].groups | join(',') }}"
- ansible_ssh_user="root"
- ansible_ssh_password="redhat"
- ansible_ssh_host="{{ item.stdout }}"
- when: item.item is defined and item.item.item[1] == "management"
- with_items: vm_ip_list
-
- - name: make IPs persistent
- shell: "virsh net-update {{ item[0] }} add ip-dhcp-host \"\" --live --config"
- when: item[1].item is defined and item[1].item.item[1] == item[0]
- with_nested:
- - provisioner.network.network_list
- - vm_ip_list
-
- - name: copy created key from virthost for SSH proxy
- fetch:
- src: "~/.ssh/virthost"
- dest: "{{ inventory_dir }}/id_rsa"
- flat: yes
-
- - name: SSH copy ID to all VMs from virthost
- shell: "sshpass -p 'redhat' ssh-copy-id -i ~/.ssh/virthost.pub root@{{ item.stdout }} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
- register: shell_result
- until: shell_result.stderr.find("ERROR") == -1
- retries: 20
- delay: 1
- when: item.item is defined
- with_items: vm_ip_list
+ gather_facts: yes
+ roles:
+ - {role: linux/rhel/libguestfs, when: ansible_distribution == 'RedHat'}
+ - {role: provision/virthost}
- name: Update ansible with the new hosts
hosts: localhost
tasks:
- - name: update file permissions
- file:
- path: "{{ inventory_dir }}/id_rsa"
- mode: 0600
-
- - name: setup ssh config
- template:
- src: "templates/ssh.config.ansible.j2"
- dest: "{{ inventory_dir }}/ansible.ssh.config"
- mode: 0755
-
- # This step is necessary in order to allow the SSH forwarding
- - name: update the ssh host name of each machine
- add_host:
- name="{{ item }}"
- ansible_ssh_host="{{ item }}"
- with_items: groups['openstack_nodes']
-
- name: update ansible with the new SSH settings
ini_file:
dest: "{{ inventory_dir }}/ansible.cfg"
diff --git a/playbooks/provisioner/virsh/templates/create_images.sh.j2 b/playbooks/provisioner/virsh/templates/create_images.sh.j2
deleted file mode 100644
index 29120283dc..0000000000
--- a/playbooks/provisioner/virsh/templates/create_images.sh.j2
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-
-# Clean our guest image
-virt-sysprep /var/lib/libvirt/images/{{ provisioner.image.name }}-original --operations dhcp-client-state,dhcp-server-state,net-hostname,net-hwaddr,udev-persistent-net
-
-# reset the password to a default one
-virt-customize -a /var/lib/libvirt/images/{{ provisioner.image.name }}-original --root-password password:redhat
-
-# Removing cloud-init
-virt-customize -a /var/lib/libvirt/images/{{ provisioner.image.name }}-original --run-command 'yum remove cloud-init* -y'
-
-# TODO: configure interfaces based on config rather than hardcode
-# configure three network interfaces for the image
-virt-customize -a /var/lib/libvirt/images/{{ provisioner.image.name }}-original --run-command 'cp /etc/sysconfig/network-scripts/ifcfg-eth{0,1} && sed -i s/DEVICE=.*/DEVICE=eth1/g /etc/sysconfig/network-scripts/ifcfg-eth1'
-
-# TODO: configure interfaces based on config rather than hardcode
-# configure three network interfaces for the image
-virt-customize -a /var/lib/libvirt/images/{{ provisioner.image.name }}-original --run-command 'cp /etc/sysconfig/network-scripts/ifcfg-eth{1,2} && sed -i s/DEVICE=.*/DEVICE=eth2/g /etc/sysconfig/network-scripts/ifcfg-eth2'
-
-{% for node_name, node_values in provisioner.nodes.iteritems() %}
-{% for num in range(1, node_values.amount + 1, 1) %}
-{% for disk_name, disk_values in node_values.disks.iteritems() %}
-qemu-img create -f qcow2 {{ disk_values.path }}/{{ node_name }}{% if node_values.amount > 1 %}{{ num }}{% endif %}.{{ disk_name }}.qcow2 {{ disk_values.size }}
-{% endfor %}
-virt-resize --expand /dev/sda1 /var/lib/libvirt/images/{{ provisioner.image.name }}-original {{ node_values.disks.disk1.path }}/{{ node_name }}{% if node_values.amount > 1 %}{{ num }}{% endif %}.disk1.qcow2
-{% endfor %}
-{% endfor %}
diff --git a/playbooks/provisioner/virsh/templates/ssh.config.ansible.j2 b/playbooks/provisioner/virsh/templates/ssh.config.ansible.j2
deleted file mode 100644
index b33ff06b53..0000000000
--- a/playbooks/provisioner/virsh/templates/ssh.config.ansible.j2
+++ /dev/null
@@ -1,13 +0,0 @@
-{% for host in groups['all'] %}
-{% if hostvars[host].get('ansible_connection', '') != 'local' and host != 'virthost' %}
-Host {{ host }}
- ProxyCommand ssh -i {{ provisioner.hosts.host1.ssh_key_file }} {{ provisioner.hosts.host1.ssh_user }}@{{ provisioner.hosts.host1.ssh_host }} nc %h %p
- HostName {{ hostvars[host].ansible_ssh_host }}
- User root
- IdentityFile {{ inventory_dir }}/id_rsa
- StrictHostKeyChecking no
- UserKnownHostsFile=/dev/null
- ForwardAgent yes
-
-{% endif %}
-{% endfor %}
diff --git a/plugins/callbacks/human_log.py b/plugins/callbacks/human_log.py
index daad0f84b3..3072e9fe66 100644
--- a/plugins/callbacks/human_log.py
+++ b/plugins/callbacks/human_log.py
@@ -22,12 +22,18 @@
except ImportError:
import json
+try:
+ from ansible.plugins.callback import CallbackBase
+ ANSIBLE2 = True
+except ImportError:
+ ANSIBLE2 = False
+
# Fields to reformat output for
FIELDS = ['cmd', 'command', 'start', 'end', 'delta', 'msg', 'stdout',
'stderr', 'results']
-class CallbackModule(object):
+class CallbackModule(CallbackBase if ANSIBLE2 else object):
def human_log(self, data):
if type(data) == dict:
for field in FIELDS:
diff --git a/plugins/callbacks/timing.py b/plugins/callbacks/timing.py
index 70563759e1..f24925f284 100644
--- a/plugins/callbacks/timing.py
+++ b/plugins/callbacks/timing.py
@@ -1,7 +1,13 @@
from datetime import datetime
+try:
+ from ansible.plugins.callback import CallbackBase
+ ANSIBLE2 = True
+except ImportError:
+ ANSIBLE2 = False
-class CallbackModule(object):
+
+class CallbackModule(CallbackBase if ANSIBLE2 else object):
__color = '\033[01;30m'
__endcolor = '\033[00m'
diff --git a/provision.example.ini b/provision.example.ini
index 0bb3ad1c57..f6be18ad24 100644
--- a/provision.example.ini
+++ b/provision.example.ini
@@ -5,7 +5,7 @@
host=earth
image-file=image.file.qcow2
image-server=my.server.com
-extra-vars=[@/home/obaranov/local/redhat/git/khaleesi/khaleesi-settings/settings/product/rhos/private_settings/redhat_internal.yml]
+extra-vars=["@private.yml","key=value"]
output=provision.yaml
-topology=all-in-one.yml
+topology=1_controller,1_compute,1_undercloud
network=default.yml
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 02bbf3ee6c..8a823e99a7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-ansible<2.0.0
+ansible>=2.0.0
python-novaclient<3.0.0
python-neutronclient>=4.0.0
PyYAML>=3.11
diff --git a/roles/ospd/overcloud/images/build/tasks/main.yml b/roles/installer/ospd/images/build/tasks/main.yml
similarity index 100%
rename from roles/ospd/overcloud/images/build/tasks/main.yml
rename to roles/installer/ospd/images/build/tasks/main.yml
diff --git a/roles/installer/ospd/images/import/tasks/main.yml b/roles/installer/ospd/images/import/tasks/main.yml
new file mode 100644
index 0000000000..1a13b2625d
--- /dev/null
+++ b/roles/installer/ospd/images/import/tasks/main.yml
@@ -0,0 +1,10 @@
+---
+- name: download the pre-built overcloud images
+ get_url:
+ dest: "~/{{ item.value }}"
+ url: "{{ installer.images.base_url }}/{{ item.value }}"
+ with_dict: "{{ installer.images.overcloud.files }}"
+
+- name: untar the images
+ shell: "tar -xvf ~/{{ item.value }}"
+ with_dict: "{{ installer.images.overcloud.files }}"
diff --git a/roles/ospd/overcloud/network/isolation/single-nic-vlans/tasks/main.yml b/roles/installer/ospd/overcloud/network/isolation/single-nic-vlans/tasks/main.yml
similarity index 99%
rename from roles/ospd/overcloud/network/isolation/single-nic-vlans/tasks/main.yml
rename to roles/installer/ospd/overcloud/network/isolation/single-nic-vlans/tasks/main.yml
index 69781e7f1e..4c16980c6f 100644
--- a/roles/ospd/overcloud/network/isolation/single-nic-vlans/tasks/main.yml
+++ b/roles/installer/ospd/overcloud/network/isolation/single-nic-vlans/tasks/main.yml
@@ -9,12 +9,12 @@
- name: append the network isolation template line to the base overcloud deploy script
lineinfile:
dest: "~/overcloud_deploy.sh"
- line: ' -e {{ installer.overcloud.template_base }}/{{ installer.overcloud.network.template.file }} \\'
+ line: ' -e {{ installer.overcloud.template_base }}/{{ installer.overcloud.network.template.file }} \'
- name: append the net-single-nic-vlans template line base overcloud deploy script
lineinfile:
dest: "~/overcloud_deploy.sh"
- line: ' -e {{ installer.overcloud.template_base }}/{{ installer.overcloud.network.isolation.file }} \\'
+ line: ' -e {{ installer.overcloud.template_base }}/{{ installer.overcloud.network.isolation.file }} \'
- name: add port to br-ctlplane for ipv6 over vlan support
shell: "sudo ovs-vsctl add-port br-ctlplane vlan{{ installer.overcloud.network.template.content.parameter_defaults.ExternalNetworkVlanID }} tag={{ installer.overcloud.network.template.content.parameter_defaults.ExternalNetworkVlanID }} -- set interface vlan{{ installer.overcloud.network.template.content.parameter_defaults.ExternalNetworkVlanID }} type=internal"
diff --git a/roles/ospd/overcloud/network/isolation/single-nic-vlans/templates/isolation.yml.j2 b/roles/installer/ospd/overcloud/network/isolation/single-nic-vlans/templates/isolation.yml.j2
similarity index 100%
rename from roles/ospd/overcloud/network/isolation/single-nic-vlans/templates/isolation.yml.j2
rename to roles/installer/ospd/overcloud/network/isolation/single-nic-vlans/templates/isolation.yml.j2
diff --git a/roles/ospd/overcloud/network/isolation/three-nics-vlans/tasks/main.yml b/roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/tasks/main.yml
similarity index 94%
rename from roles/ospd/overcloud/network/isolation/three-nics-vlans/tasks/main.yml
rename to roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/tasks/main.yml
index 56b15cbd98..ba0589e6ff 100644
--- a/roles/ospd/overcloud/network/isolation/three-nics-vlans/tasks/main.yml
+++ b/roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/tasks/main.yml
@@ -32,17 +32,17 @@
- name: append the network isolation template line to the base overcloud deploy script
lineinfile:
dest: "~/overcloud_deploy.sh"
- line: ' -e /usr/share/openstack-tripleo-heat-templates/environments/{{ isolation_file }} \\'
+ line: ' -e /usr/share/openstack-tripleo-heat-templates/environments/{{ isolation_file }} \'
- name: append the network environment template line to the base overcloud deploy script
lineinfile:
dest: "~/overcloud_deploy.sh"
- line: ' -e {{ installer.overcloud.template_base }}/network-environment.yaml \\'
+ line: ' -e {{ installer.overcloud.template_base }}/network-environment.yaml \'
- name: append the net-three-nics-vlans template line base overcloud deploy script
lineinfile:
dest: "~/overcloud_deploy.sh"
- line: ' -e {{ installer.overcloud.template_base }}/{{ nics_file }} \\'
+ line: ' -e {{ installer.overcloud.template_base }}/{{ nics_file }} \'
- name: add the ipv6 address
shell: "sudo ip addr add {{ installer.overcloud.network.template.content.parameter_defaults.ExternalInterfaceDefaultRoute }}/64 dev eth2"
diff --git a/roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/ceph-storage.yaml b/roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/ceph-storage.yaml
similarity index 100%
rename from roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/ceph-storage.yaml
rename to roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/ceph-storage.yaml
diff --git a/roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/cinder-storage.yaml b/roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/cinder-storage.yaml
similarity index 100%
rename from roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/cinder-storage.yaml
rename to roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/cinder-storage.yaml
diff --git a/roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/compute.yaml b/roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/compute.yaml
similarity index 100%
rename from roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/compute.yaml
rename to roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/compute.yaml
diff --git a/roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/controller-v6.yaml b/roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/controller-v6.yaml
similarity index 100%
rename from roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/controller-v6.yaml
rename to roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/controller-v6.yaml
diff --git a/roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/controller.yaml b/roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/controller.yaml
similarity index 100%
rename from roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/controller.yaml
rename to roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/controller.yaml
diff --git a/roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/net-three-nics-with-vlans-v6.yaml b/roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/net-three-nics-with-vlans-v6.yaml
similarity index 100%
rename from roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/net-three-nics-with-vlans-v6.yaml
rename to roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/net-three-nics-with-vlans-v6.yaml
diff --git a/roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/net-three-nics-with-vlans.yaml b/roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/net-three-nics-with-vlans.yaml
similarity index 100%
rename from roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/net-three-nics-with-vlans.yaml
rename to roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/net-three-nics-with-vlans.yaml
diff --git a/roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/network-environment.yaml.j2 b/roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/network-environment.yaml.j2
similarity index 100%
rename from roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/network-environment.yaml.j2
rename to roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/network-environment.yaml.j2
diff --git a/roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/swift-storage.yaml b/roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/swift-storage.yaml
similarity index 100%
rename from roles/ospd/overcloud/network/isolation/three-nics-vlans/templates/swift-storage.yaml
rename to roles/installer/ospd/overcloud/network/isolation/three-nics-vlans/templates/swift-storage.yaml
diff --git a/roles/ospd/overcloud/ssl/tasks/main.yml b/roles/installer/ospd/overcloud/ssl/tasks/main.yml
similarity index 97%
rename from roles/ospd/overcloud/ssl/tasks/main.yml
rename to roles/installer/ospd/overcloud/ssl/tasks/main.yml
index 0176a80875..2608ff82f1 100644
--- a/roles/ospd/overcloud/ssl/tasks/main.yml
+++ b/roles/installer/ospd/overcloud/ssl/tasks/main.yml
@@ -46,9 +46,9 @@
- name: append the invocation line to the base overcloud deploy script
lineinfile:
dest: "~/overcloud_deploy.sh"
- line: ' -e {{ installer.overcloud.template_base }}/enable-tls.yaml \\'
+ line: ' -e {{ installer.overcloud.template_base }}/enable-tls.yaml \'
- name: append the invocation line to the base overcloud deploy script
lineinfile:
dest: "~/overcloud_deploy.sh"
- line: ' -e {{ installer.overcloud.template_base }}/inject-trust-anchor.yaml \\'
+ line: ' -e {{ installer.overcloud.template_base }}/inject-trust-anchor.yaml \'
diff --git a/roles/ospd/overcloud/storage/ceph/tasks/main.yml b/roles/installer/ospd/overcloud/storage/ceph/tasks/main.yml
similarity index 84%
rename from roles/ospd/overcloud/storage/ceph/tasks/main.yml
rename to roles/installer/ospd/overcloud/storage/ceph/tasks/main.yml
index d6752bde43..0e0ef09a17 100644
--- a/roles/ospd/overcloud/storage/ceph/tasks/main.yml
+++ b/roles/installer/ospd/overcloud/storage/ceph/tasks/main.yml
@@ -7,21 +7,21 @@
- name: append the ceph storage template line to the base overcloud deploy script
lineinfile:
dest: "~/overcloud_deploy.sh"
- line: ' --ceph-storage-scale {{ groups["ceph"]| length }} \\'
+ line: ' --ceph-storage-scale {{ groups["ceph"]| length }} \'
when: installer.overcloud.storage.external == "no"
- name: append the ceph storage template line to the base overcloud deploy script
lineinfile:
dest: "~/overcloud_deploy.sh"
- line: '{% for flavor in tagged_flavors %}{% if flavor.cmd is defined and "ceph" in flavor.cmd %} --ceph-storage-flavor {{ flavor.cmd.split() | last }} \\{% endif %}{% endfor %}'
+ line: '{% for flavor in tagged_flavors %}{% if flavor.cmd is defined and "ceph" in flavor.cmd %} --ceph-storage-flavor {{ flavor.cmd.split() | last }} \{% endif %}{% endfor %}'
when: installer.overcloud.storage.external == "no"
- name: append the storage template line to the base overcloud deploy script
lineinfile:
dest: "~/overcloud_deploy.sh"
- line: ' -e /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml \\'
+ line: ' -e /usr/share/openstack-tripleo-heat-templates/environments/storage-environment.yaml \'
- name: append the storage ceph custom template line to the base overcloud deploy script
lineinfile:
dest: "~/overcloud_deploy.sh"
- line: ' -e {{ installer.overcloud.template_base }}/custom-ceph.yaml \\'
\ No newline at end of file
+ line: ' -e {{ installer.overcloud.template_base }}/custom-ceph.yaml \'
diff --git a/roles/ospd/overcloud/storage/ceph/templates/ceph.yml.j2 b/roles/installer/ospd/overcloud/storage/ceph/templates/ceph.yml.j2
similarity index 88%
rename from roles/ospd/overcloud/storage/ceph/templates/ceph.yml.j2
rename to roles/installer/ospd/overcloud/storage/ceph/templates/ceph.yml.j2
index 456cc58737..d351d0022c 100644
--- a/roles/ospd/overcloud/storage/ceph/templates/ceph.yml.j2
+++ b/roles/installer/ospd/overcloud/storage/ceph/templates/ceph.yml.j2
@@ -12,7 +12,7 @@ resource_registry:
{% else %}
ExtraConfig:
ceph::profile::params::osds:
-{% for disk_name, disk_values in provisioner.nodes.ceph.disks.iteritems() %}
+{% for disk_name, disk_values in provisioner.topology.nodes.ceph.disks.iteritems() %}
{% if disk_name != 'disk1' %}
'{{ disk_values.dev }}':
journal: ''
diff --git a/roles/installer/ospd/overcloud/storage/netapp/tasks/main.yml b/roles/installer/ospd/overcloud/storage/netapp/tasks/main.yml
new file mode 100644
index 0000000000..80344eed97
--- /dev/null
+++ b/roles/installer/ospd/overcloud/storage/netapp/tasks/main.yml
@@ -0,0 +1,11 @@
+---
+- name: Prepare NetApp storage template
+ template:
+ src: "netapp.yml.j2"
+ dest: "{{ installer.overcloud.template_base }}/custom-netapp.yml"
+ mode: 0755
+
+- name: Append the storage NetApp custom template line to the base overcloud deploy script
+ lineinfile:
+ dest: "~/overcloud_deploy.sh"
+ line: ' -e {{ installer.overcloud.template_base }}/custom-netapp.yml \'
diff --git a/roles/installer/ospd/overcloud/storage/netapp/templates/netapp.yml.j2 b/roles/installer/ospd/overcloud/storage/netapp/templates/netapp.yml.j2
new file mode 100644
index 0000000000..68b1fe8a31
--- /dev/null
+++ b/roles/installer/ospd/overcloud/storage/netapp/templates/netapp.yml.j2
@@ -0,0 +1,26 @@
+resource_registry:
+ OS::TripleO::ControllerExtraConfigPre: /usr/share/openstack-tripleo-heat-templates/puppet/extraconfig/pre_deploy/controller/cinder-netapp.yaml
+
+parameter_defaults:
+ CinderEnableIscsiBackend: false
+ CinderEnableNetappBackend: true
+ CinderNetappServerPort: '80'
+ CinderNetappSizeMultiplier: '1.2'
+ CinderNetappStorageFamily: 'ontap_7mode'
+ CinderNetappTransportType: 'http'
+
+{% if installer.overcloud.storage.protocol == 'iscsi' %}
+ CinderNetappBackendName: 'tripleo_netapp_iscsi'
+ CinderNetappStorageProtocol: 'iscsi'
+{% elif installer.overcloud.storage.protocol == 'nfs' %}
+ CinderNetappBackendName: 'tripleo_netapp_nfs'
+ CinderNetappStorageProtocol: 'nfs'
+ CinderNetappVfiler: ''
+ CinderNetappVserver: ''
+ CinderNetappPartnerBackendName: ''
+ CinderNetappNfsSharesConfig: '/etc/cinder/shares.conf'
+{% endif %}
+
+{% for key, val in installer.overcloud.storage.template.content.parameter_defaults.iteritems() %}
+ {{ key }}: '{{ val }}'
+{% endfor %}
diff --git a/roles/ospd/undercloud/tasks/main.yml b/roles/installer/ospd/undercloud/tasks/main.yml
similarity index 100%
rename from roles/ospd/undercloud/tasks/main.yml
rename to roles/installer/ospd/undercloud/tasks/main.yml
diff --git a/roles/linux/rhel/libguestfs/tasks/main.yml b/roles/linux/rhel/libguestfs/tasks/main.yml
new file mode 100644
index 0000000000..3c3c47766a
--- /dev/null
+++ b/roles/linux/rhel/libguestfs/tasks/main.yml
@@ -0,0 +1,21 @@
+# TODO: remove this once libguestfs is updated to 1.30 on RHEL
+- name: remove existing libguestfs packages
+ become_user: root
+ yum:
+ state: absent
+ name: "libguestfs"
+
+- name: install from RPM
+ become_user: root
+ shell: "wget {{ private.rpms.base_url }}/{{ item }} --no-check-certificate && yum localinstall -y {{ item }}"
+ with_items:
+ - libguestfs-1.32.3-1.el7.x86_64.rpm
+ - libguestfs-tools-c-1.32.3-1.el7.x86_64.rpm
+ - libguestfs-xfs-1.32.3-1.el7.x86_64.rpm
+
+- name: start libvirtd
+ become_user: root
+ service:
+ name: libvirtd
+ state: started
+ enabled: yes
\ No newline at end of file
diff --git a/roles/ospd/overcloud/images/import/tasks/main.yml b/roles/ospd/overcloud/images/import/tasks/main.yml
deleted file mode 100644
index 44b095fda6..0000000000
--- a/roles/ospd/overcloud/images/import/tasks/main.yml
+++ /dev/null
@@ -1,21 +0,0 @@
----
-- name: download the pre-built overcloud images
- get_url:
- dest: "~/{{ item.value }}"
- url: "{{ installer.images.base_url }}/{{ item.value }}"
- with_dict: "{{ installer.images.overcloud.files }}"
-
-- name: untar the images
- shell: "tar -xvf ~/{{ item.value }}"
- with_dict: "{{ installer.images.overcloud.files }}"
-
-- name: install the libguestfs-tools to customize the image
- shell: "sudo yum install libguestfs-tools -y"
-
-- name: list the available images
- shell: "ls ~/*.qcow2"
- register: image_list
-
-- name: assign a default password for root
- shell: "virt-customize -a {{ item }} --root-password password:redhat"
- with_items: image_list.stdout_lines
\ No newline at end of file
diff --git a/roles/provision/virthost/tasks/images.yml b/roles/provision/virthost/tasks/images.yml
new file mode 100644
index 0000000000..f1d9fb5361
--- /dev/null
+++ b/roles/provision/virthost/tasks/images.yml
@@ -0,0 +1,42 @@
+- name: remove pre-existing file
+ file:
+ state: absent
+ dest: "/var/lib/libvirt/images/{{ provisioner.image.file }}-original"
+
+- name: download base guest image
+ get_url:
+ dest: "/var/lib/libvirt/images/{{ provisioner.image.file }}-original"
+ url: "{{ provisioner.image.server }}/{{ provisioner.image.file }}"
+ register: result
+ until: result.msg.find("Request failed") == -1
+ retries: 5
+ delay: 5
+
+- name: clean our guest image
+ shell: "virt-sysprep -a /var/lib/libvirt/images/{{ provisioner.image.file }}-original --operations dhcp-client-state,dhcp-server-state,net-hostname,net-hwaddr,udev-persistent-net"
+
+- name: reset the password to a default one and inject our SSH key
+ shell: "virt-customize -a /var/lib/libvirt/images/{{ provisioner.image.file }}-original --root-password password:redhat --ssh-inject root:file:/root/.ssh/id_rsa.pub --selinux-relabel"
+
+- name: removing cloud-init
+ shell: "virt-customize -a /var/lib/libvirt/images/{{ provisioner.image.file }}-original --run-command 'yum remove cloud-init* -y'"
+
+ # TODO: configure interfaces based on config rather than hardcode
+- name: configure three network interfaces for the image
+ shell: "virt-customize -a /var/lib/libvirt/images/{{ provisioner.image.file }}-original --run-command 'cp /etc/sysconfig/network-scripts/ifcfg-eth{0,1} && sed -i s/DEVICE=.*/DEVICE=eth1/g /etc/sysconfig/network-scripts/ifcfg-eth1'"
+
+ # TODO: configure interfaces based on config rather than hardcode
+- name: configure three network interfaces for the image
+ shell: "virt-customize -a /var/lib/libvirt/images/{{ provisioner.image.file }}-original --run-command 'cp /etc/sysconfig/network-scripts/ifcfg-eth{1,2} && sed -i s/DEVICE=.*/DEVICE=eth2/g /etc/sysconfig/network-scripts/ifcfg-eth2'"
+
+- name: create the image disks for our nodes
+ template:
+ dest: "~/create_disks.sh"
+ src: "create_disks.sh.j2"
+ mode: 0755
+
+- name: the create disks script
+ shell: "cat ~/create_disks.sh"
+
+- name: execute the create disks script
+ shell: "bash ~/create_disks.sh"
diff --git a/roles/provision/virthost/tasks/main.yml b/roles/provision/virthost/tasks/main.yml
new file mode 100644
index 0000000000..640706d9ff
--- /dev/null
+++ b/roles/provision/virthost/tasks/main.yml
@@ -0,0 +1,83 @@
+- name: install packages
+ yum:
+ name: "{{ item }}"
+ state: present
+ with_items: "{{ provisioner.packages }}"
+
+- name: stop libvirtd
+ service:
+ name: libvirtd
+ state: stopped
+ enabled: no
+
+- name: start libvirtd
+ service:
+ name: libvirtd
+ state: started
+ enabled: yes
+
+- name: check if host supports virtualization
+ include: validate.yml
+
+- name: setup the virthost
+ include: setup.yml
+
+- name: setup the networks
+ include: network.yml
+
+- name: check if private key exist on system
+ stat:
+ path: /root/.ssh/id_rsa
+ register: result
+
+- name: if private key doesn't exist, create it
+ shell: "ssh-keygen -f /root/.ssh/id_rsa -t rsa -N ''"
+ when: result.stat.exists != true
+
+- name: copy server private key from virthost for SSH proxy
+ fetch:
+ src: "{{ item.src }}"
+ dest: "{{ item.dest }}"
+ flat: yes
+ with_items:
+ - {src: "~/.ssh/id_rsa", dest: "{{ inventory_dir }}/id_rsa"}
+ - {src: "~/.ssh/id_rsa.pub", dest: "{{ inventory_dir }}/id_rsa.pub"}
+
+- name: update file permissions
+ file:
+ path: "{{ item }}"
+ mode: 0600
+ with_items:
+ - "{{ inventory_dir }}/id_rsa"
+ - "{{ inventory_dir }}/id_rsa.pub"
+ delegate_to: localhost
+
+- name: Set up authorized_keys for the deploy user
+ authorized_key:
+ user: root
+ key: "{{ item }}"
+ with_file:
+ - "{{ inventory_dir }}/id_rsa.pub"
+
+- name: create the base images for each VM
+ include: images.yml
+
+- name: create the VMs
+ include: vms.yml
+
+- name: add hosts to host list
+ add_host:
+ name="{{ item.item.item[0] }}"
+ groups="{{ provisioner.topology.nodes['%s' % item.item.item[0].rstrip('1234567890')].groups | join(',') }}"
+ ansible_ssh_user="root"
+ ansible_ssh_password="redhat"
+ ansible_ssh_host="{{ item.stdout }}"
+ ansible_ssh_private_key_file="{{ inventory_dir }}/id_rsa"
+ when: item.item is defined and item.item.item[1] == "external"
+ with_items: vm_ip_list
+
+- name: update ansible.ssh.config template
+ template:
+ src: ansible.ssh.config.j2
+ dest: "{{ inventory_dir }}/ansible.ssh.config"
+ delegate_to: localhost
\ No newline at end of file
diff --git a/roles/provision/virthost/tasks/network.yml b/roles/provision/virthost/tasks/network.yml
new file mode 100644
index 0000000000..a9440a4f49
--- /dev/null
+++ b/roles/provision/virthost/tasks/network.yml
@@ -0,0 +1,24 @@
+- name: check for existing networks
+ virt_net:
+ command: list_nets
+ register: network_list
+
+- name: create the networks for the topology
+ virt_net:
+ command: define
+ name: "{{ item.key }}"
+ xml: "{{ lookup('template', '../templates/network.xml.j2') }}"
+ when: "item.key not in network_list.list_nets"
+ with_dict: provisioner.topology.network
+
+- name: check if network is active
+ virt_net:
+ state: active
+ name: "{{ item }}"
+ with_items: provisioner.topology.network.keys()
+
+- name: make network persistent
+ virt_net:
+ autostart: "yes"
+ name: "{{ item }}"
+ with_items: provisioner.topology.network.keys()
diff --git a/roles/provision/virthost/tasks/setup.yml b/roles/provision/virthost/tasks/setup.yml
new file mode 100644
index 0000000000..baed8673a6
--- /dev/null
+++ b/roles/provision/virthost/tasks/setup.yml
@@ -0,0 +1,50 @@
+- name: enable nested KVM support for Intel
+ lineinfile:
+ dest: "/etc/modprobe.d/dist.conf"
+ line: "options kvm_{{ kvm_base }} nested=1"
+ state: present
+ create: yes
+ when: kvm_base is defined
+
+- name: enable nested KVM support for AMD
+ lineinfile:
+ dest: "/etc/modprobe.d/dist.conf"
+ line: "options {{ kvm_base }} nested=1"
+ state: present
+ create: yes
+ when: kvm_base is defined
+
+# A change in the modprove requires to reload the module
+- name: unload KVM module
+ modprobe:
+ name: "kvm_{{ kvm_base }}"
+ state: absent
+ ignore_errors: True
+ when: kvm_base is defined
+
+- name: load KVM module
+ modprobe:
+ name: "kvm_{{ kvm_base }}"
+ state: present
+ ignore_errors: True
+ when: kvm_base is defined
+
+- name: install required QEMU-KVM packages
+ yum:
+ name: "qemu-kvm"
+ state: present
+ when: kvm_base is defined
+
+# Make sure the net-virtio module is enabled
+- name: unload vhost-net module
+ modprobe:
+ name: "vhost-net"
+ state: absent
+ ignore_errors: True
+ when: kvm_base is defined
+
+- name: load KVM module
+ modprobe:
+ name: "vhost-net"
+ state: present
+ ignore_errors: True
diff --git a/roles/provision/virthost/tasks/validate.yml b/roles/provision/virthost/tasks/validate.yml
new file mode 100644
index 0000000000..019d291ef3
--- /dev/null
+++ b/roles/provision/virthost/tasks/validate.yml
@@ -0,0 +1,30 @@
+- name: check if host supports virtualization
+ command: "virt-host-validate"
+ register: result
+
+- debug:
+ var: result.stdout_lines
+
+- fail:
+ msg: "System does not support virtualization"
+ when: "'FAIL' in result.stdout_lines"
+
+- name: check if CPU supports INTEL based KVM
+ shell: egrep -c 'vmx' /proc/cpuinfo
+ ignore_errors: True
+ register: kvm_intel
+
+- name: set fact for Intel based KVM
+ set_fact:
+ kvm_base: "intel"
+ when: kvm_intel == 0
+
+- name: check if CPU supports AMD based KVM
+ shell: egrep -c 'svm' /proc/cpuinfo
+ ignore_errors: True
+ register: kvm_amd
+
+- name: set fact for AMD based KVM
+ set_fact:
+ kvm_base: "amd"
+ when: kvm_amd == 0
diff --git a/roles/provision/virthost/tasks/vms.yml b/roles/provision/virthost/tasks/vms.yml
new file mode 100644
index 0000000000..34efe318b3
--- /dev/null
+++ b/roles/provision/virthost/tasks/vms.yml
@@ -0,0 +1,53 @@
+- name: create virt install templates for nodes
+ template:
+ dest: "~/create_vms.sh"
+ src: "create_vms.sh.j2"
+ mode: 0755
+
+ #TODO: move this logic to a module
+- name: the create vms script
+ shell: "cat ~/create_vms.sh"
+
+- name: execute the create vms script
+ shell: "bash ~/create_vms.sh"
+
+- name: get the list of VMs
+ shell: "virsh list --all | grep -P '[\\w]+' | sed -n '2,$p' | awk '{print $2}'"
+ register: vm_names
+
+- set_fact:
+ vm_name_list: "{{ vm_names.stdout_lines }}"
+
+- name: get MAC list
+ shell: "virsh domiflist {{ item[0] }} | awk '/{{ item[1] }}/ {print $5};'"
+ with_nested:
+ - vm_name_list
+ - provisioner.topology.network
+ register: mac_list
+
+- set_fact:
+ vm_mac_list: "{{ mac_list.results }}"
+
+- name: wait until one of the VMs gets an IP
+ shell: "virsh net-dhcp-leases {{ item.item[1] }} | awk /{{ item.stdout }}/'{print $5}' | cut -d'/' -f1"
+ when: >
+ provisioner.topology.network[item.item[1]].dhcp is defined and
+ item.stdout is defined and item.stdout != ""
+ register: ip_list
+ until: ip_list.stdout.find("{{ provisioner.topology.network['%s' % item.item[1]].ip_address | truncate(7, True, '') }}") > -1
+ retries: 40
+ delay: 5
+ with_items: vm_mac_list
+
+- set_fact:
+ vm_ip_list: "{{ ip_list.results }}"
+
+- name: make IPs persistent
+ shell: "virsh net-update {{ item[0] }} add ip-dhcp-host \"\" --live --config"
+ when: >
+ provisioner.topology.network[item[1].item.item[1]].dhcp is defined and
+ item[1].item is defined and
+ item[1].item.item[1] == item[0]
+ with_nested:
+ - provisioner.topology.network
+ - vm_ip_list
diff --git a/roles/provision/virthost/templates/ansible.ssh.config.j2 b/roles/provision/virthost/templates/ansible.ssh.config.j2
new file mode 100644
index 0000000000..592704f7ef
--- /dev/null
+++ b/roles/provision/virthost/templates/ansible.ssh.config.j2
@@ -0,0 +1,20 @@
+{% for host in groups['all'] %}
+{% if host not in ['localhost'] %}
+Host {{ host }}
+ HostName {{ hostvars[host].ansible_ssh_host }}
+ User {{ hostvars[host].ansible_ssh_user }}
+ IdentityFile {{ hostvars[host].ansible_ssh_private_key_file }}
+ StrictHostKeyChecking no
+ UserKnownHostsFile=/dev/null
+ ForwardAgent yes
+{% if host != 'virthost' %}
+ ProxyCommand ssh -W %h:%p -i {{ hostvars[host].ansible_ssh_private_key_file }} {{ provisioner.host.user }}@{{ provisioner.host.address }}
+{% endif %}
+{% endif %}
+
+{% endfor %}
+Host {{ '%s' % '.'.join(provisioner.topology.network.external.ip_address.split('.')[:-2]) }}.*
+ ProxyCommand ssh -W %h:%p -i {{ provisioner.host.key }} {{ provisioner.host.user }}@{{ provisioner.host.address }}
+ ControlMaster auto
+ ControlPath {{ inventory_dir }}/%%r@%%h:%%p
+ ControlPersist 15m
\ No newline at end of file
diff --git a/roles/provision/virthost/templates/create_disks.sh.j2 b/roles/provision/virthost/templates/create_disks.sh.j2
new file mode 100644
index 0000000000..c3f082b7a2
--- /dev/null
+++ b/roles/provision/virthost/templates/create_disks.sh.j2
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+{% for node_name, node_values in provisioner.topology.nodes.iteritems() %}
+{% for num in range(1, node_values.amount + 1, 1) %}
+{% for disk_name, disk_values in node_values.disks.iteritems() %}
+qemu-img create -f qcow2 {{ disk_values.path }}/{{ node_name }}{% if node_values.amount > 1 %}{{ num }}{% endif %}.{{ disk_name }}.qcow2 {{ disk_values.size }}
+{% endfor %}
+virt-resize --expand /dev/sda1 /var/lib/libvirt/images/{{ provisioner.image.file }}-original {{ node_values.disks.disk1.path }}/{{ node_name }}{% if node_values.amount > 1 %}{{ num }}{% endif %}.disk1.qcow2
+{% endfor %}
+{% endfor %}
diff --git a/playbooks/provisioner/virsh/templates/create_vms.sh.j2 b/roles/provision/virthost/templates/create_vms.sh.j2
similarity index 55%
rename from playbooks/provisioner/virsh/templates/create_vms.sh.j2
rename to roles/provision/virthost/templates/create_vms.sh.j2
index a1d1e2be66..a17ed33805 100644
--- a/playbooks/provisioner/virsh/templates/create_vms.sh.j2
+++ b/roles/provision/virthost/templates/create_vms.sh.j2
@@ -1,13 +1,20 @@
#!/bin/bash
-{% for node_name, node_values in provisioner.nodes.iteritems() %}
+{% for node_name, node_values in provisioner.topology.nodes.iteritems() %}
{% for num in range(1, node_values.amount + 1, 1) %}
virt-install --name {{ node_name }}{% if node_values.amount > 1 %}{{ num }}{% endif %} \
{% for disk_name, disk_values in node_values.disks.iteritems() %}
--disk path={{ disk_values.path }}/{{ node_name }}{% if node_values.amount > 1 %}{{ num }}{% endif %}.{{ disk_name }}.qcow2,device=disk,bus=virtio,format=qcow2 \
{% endfor %}
- --network network:{{ provisioner.network.network_list.data.name }} \
- --network network:{{ provisioner.network.network_list.management.name }} \
- --network network:{{ provisioner.network.network_list.external.name }} \
+{#{% for net in provisioner.topology.network.keys() %}#}
+{# todo(yfried): make network order work#}
+{# --network network:{{ net }} \#}
+{#{% endfor %}#}
+{# Hard coding network names because undercloud needs "data" network to map
+ to the NIC defined in ospd.yml::installer.undercloud.config.local_interface
+ todo(yfried): remove hardcoded network names#}
+ --network network:data \
+ --network network:management \
+ --network network:external \
--virt-type kvm \
--cpu host-model \
--ram {{ node_values.memory }} \
diff --git a/playbooks/provisioner/virsh/network/network.xml.j2 b/roles/provision/virthost/templates/network.xml.j2
similarity index 80%
rename from playbooks/provisioner/virsh/network/network.xml.j2
rename to roles/provision/virthost/templates/network.xml.j2
index 254a7d436f..cabff7d7a0 100644
--- a/playbooks/provisioner/virsh/network/network.xml.j2
+++ b/roles/provision/virthost/templates/network.xml.j2
@@ -1,8 +1,8 @@
- {{ item.value.name }}
+ {{ item.key }}
{% if item.value.forward is defined %}
-
+
{% endif %}
{% if item.value.dhcp is defined %}
diff --git a/settings/installer/ospd.yml b/settings/installer/ospd.yml
index c9e05061ac..3f95819821 100644
--- a/settings/installer/ospd.yml
+++ b/settings/installer/ospd.yml
@@ -38,7 +38,6 @@ defaults:
build: latest
images: import
network: "no-isolation"
- storage: ceph
version: 7
ssl: "no"
diff --git a/settings/installer/ospd/storage/ceph.yml b/settings/installer/ospd/storage/ceph.yml
index f94d6aa559..b4024baa72 100644
--- a/settings/installer/ospd/storage/ceph.yml
+++ b/settings/installer/ospd/storage/ceph.yml
@@ -3,6 +3,7 @@
installer:
overcloud:
storage:
+ backend: "ceph"
external: "no"
template:
content:
diff --git a/settings/installer/ospd/storage/netapp.yml b/settings/installer/ospd/storage/netapp.yml
new file mode 100644
index 0000000000..dd0933da53
--- /dev/null
+++ b/settings/installer/ospd/storage/netapp.yml
@@ -0,0 +1,14 @@
+---
+installer:
+ overcloud:
+ storage:
+ backend: "netapp"
+ template:
+ content:
+ parameter_defaults:
+ CinderNetappPassword: "{{ !lookup private.storage.netapp.CinderNetappPassword }}"
+ CinderNetappServerHostname: "{{ !lookup private.storage.netapp.CinderNetappServerHostname }}"
+ CinderNetappLogin: "{{ !lookup private.storage.netapp.CinderNetappLogin }}"
+
+defaults:
+ protocol: iscsi
diff --git a/settings/installer/ospd/storage/netapp/protocol/iscsi.yml b/settings/installer/ospd/storage/netapp/protocol/iscsi.yml
new file mode 100644
index 0000000000..2d3631ca54
--- /dev/null
+++ b/settings/installer/ospd/storage/netapp/protocol/iscsi.yml
@@ -0,0 +1,9 @@
+---
+installer:
+ overcloud:
+ storage:
+ protocol: "iscsi"
+ template:
+ content:
+ parameter_defaults:
+ CinderNetappVolumeList: "{{ !lookup private.storage.netapp.CinderNetappVolumeList }}"
diff --git a/settings/installer/ospd/storage/netapp/protocol/nfs.yml b/settings/installer/ospd/storage/netapp/protocol/nfs.yml
new file mode 100644
index 0000000000..9b0c717326
--- /dev/null
+++ b/settings/installer/ospd/storage/netapp/protocol/nfs.yml
@@ -0,0 +1,10 @@
+---
+installer:
+ overcloud:
+ storage:
+ protocol: "nfs"
+ template:
+ content:
+ parameter_defaults:
+ CinderNetappNfsShares: "{{ !lookup private.storage.netapp.CinderNetappNfsShares }}"
+ CinderNetappNfsMountOptions: "{{ !lookup private.storage.netapp.CinderNetappNfsMountOptions }}"
diff --git a/settings/provisioner/topology/ceph.yaml b/settings/provisioner/topology/ceph.yaml
new file mode 100644
index 0000000000..4bae92bb37
--- /dev/null
+++ b/settings/provisioner/topology/ceph.yaml
@@ -0,0 +1,26 @@
+name: "ceph"
+amount: "1"
+cpu: "2"
+memory: "4096"
+os:
+ type: "linux"
+ variant: "rhel7"
+disks:
+ disk1: &disk1
+ path: "/var/lib/libvirt/images"
+ dev: "/dev/vda"
+ size: "20G"
+ disk2:
+ <<: *disk1
+ dev: /dev/vdb
+network:
+ interfaces:
+ management:
+ label: "eth0"
+ data:
+ label: "eth1"
+ external:
+ label: "eth2"
+groups:
+ - ceph
+ - openstack_nodes
diff --git a/settings/provisioner/topology/compute.yaml b/settings/provisioner/topology/compute.yaml
new file mode 100644
index 0000000000..923d45362e
--- /dev/null
+++ b/settings/provisioner/topology/compute.yaml
@@ -0,0 +1,23 @@
+name: "compute"
+amount: "1"
+cpu: "4"
+memory: "6144"
+os:
+ type: "linux"
+ variant: "rhel7"
+disks:
+ disk1:
+ path: "/var/lib/libvirt/images"
+ dev: "/dev/vda"
+ size: "30G"
+network:
+ interfaces:
+ management:
+ label: "eth0"
+ data:
+ label: "eth1"
+ external:
+ label: "eth2"
+groups:
+ - compute
+ - openstack_nodes
diff --git a/settings/provisioner/topology/controller.yaml b/settings/provisioner/topology/controller.yaml
new file mode 100644
index 0000000000..a3a5713a07
--- /dev/null
+++ b/settings/provisioner/topology/controller.yaml
@@ -0,0 +1,23 @@
+name: "controller"
+amount: "1"
+cpu: "4"
+memory: "8192"
+os:
+ type: "linux"
+ variant: "rhel7"
+disks:
+ disk1:
+ path: "/var/lib/libvirt/images"
+ dev: "/dev/vda"
+ size: "30G"
+network:
+ interfaces:
+ management:
+ label: "eth0"
+ data:
+ label: "eth1"
+ external:
+ label: "eth2"
+groups:
+ - controller
+ - openstack_nodes
diff --git a/settings/provisioner/topology/undercloud.yaml b/settings/provisioner/topology/undercloud.yaml
new file mode 100644
index 0000000000..c8f5b0f03e
--- /dev/null
+++ b/settings/provisioner/topology/undercloud.yaml
@@ -0,0 +1,24 @@
+name: "undercloud"
+amount: "1"
+cpu: "4"
+memory: "16384"
+os:
+ type: "linux"
+ variant: "rhel7"
+disks:
+ disk1:
+ path: "/var/lib/libvirt/images"
+ dev: "/dev/vda"
+ size: "20G"
+network:
+ interfaces:
+ management:
+ label: "eth0"
+ data:
+ label: "eth1"
+ external:
+ label: "eth2"
+groups:
+ - undercloud
+ - tester
+ - openstack_nodes
diff --git a/settings/provisioner/virsh/default.ini b/settings/provisioner/virsh/default.ini
deleted file mode 100644
index 6de6f17269..0000000000
--- a/settings/provisioner/virsh/default.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[virsh]
-topology=all-in-one.yml
-network=default.yml
-ssh-key=~/.ssh/id_rsa
-ssh-user=root
\ No newline at end of file
diff --git a/settings/provisioner/virsh/network/default.yml b/settings/provisioner/virsh/network/default.yml
deleted file mode 100644
index 6424e1a09c..0000000000
--- a/settings/provisioner/virsh/network/default.yml
+++ /dev/null
@@ -1,40 +0,0 @@
----
-
-provisioner:
- network:
- network_list:
- # todo: same as hyp
- data:
- name: "provisioning"
- ip_address: "172.16.0.254"
- netmask: "255.255.255.0"
- management:
- name: "management"
- ip_address: "10.0.0.1"
- netmask: "255.255.255.0"
- forward:
- type: "nat"
- dhcp:
- range:
- start: "10.0.0.2"
- end: "10.0.0.100"
- subnet_cidr: "10.0.0.0/24"
- subnet_gateway: "10.0.0.1"
- floating_ip:
- start: "10.0.0.101"
- end: "10.0.0.150"
- external:
- name: "external"
- ip_address: "192.168.1.1"
- netmask: "255.255.255.0"
- forward:
- type: "nat"
- dhcp:
- range:
- start: "192.168.1.2"
- end: "192.168.1.100"
- subnet_cidr: "192.168.1.0/24"
- subnet_gateway: "192.168.1.1"
- floating_ip:
- start: "192.168.1.101"
- end: "192.168.1.150"
diff --git a/settings/provisioner/virsh/topology/README.txt b/settings/provisioner/virsh/topology/README.txt
deleted file mode 100644
index 111742e1a6..0000000000
--- a/settings/provisioner/virsh/topology/README.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-This is a placeholder for the new provisioner to hold the topology files
-in the form of:
-
-provisioner:
- nodes:
- # Dict of nodes
- example:
- name: controller
- amount: 1
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 8192
- os: &os
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disk: &disk
- size: "{{ !lookup provisioner.image.disk.size }}"
- dev: /dev/vda
- path: /var/lib/libvirt/images
- network: &network_params
- interfaces: &interfaces
- management: &mgmt_interface
- label: eth0
- data: &data_interface
- label: eth1
- external: &external_interface
- label: eth2
- groups:
- - controller
- - openstack_nodes
-
-
diff --git a/settings/provisioner/virsh/topology/all-in-one.yml b/settings/provisioner/virsh/topology/all-in-one.yml
deleted file mode 100644
index 1ec28df880..0000000000
--- a/settings/provisioner/virsh/topology/all-in-one.yml
+++ /dev/null
@@ -1,39 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 1
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 16384
- os:
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks:
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- network: &network_params
- interfaces: &interfaces
- data: &data_interface
- label: eth1
- config_params: &data_interface_params
- bootproto: static
- ipaddr: 10.0.0.1
- netmask: 255.255.255.0
- nm_controlled: "no"
- type: ethernet
- onboot: yes
- device: "{{ !lookup provisioner.nodes.controller.network.interfaces.data.label }}"
- external: &external_interface
- label: eth2
- config_params:
- <<: *data_interface_params
- ipaddr: ""
- device: "{{ !lookup provisioner.nodes.controller.network.interfaces.external.label }}"
- groups:
- - controller
- - compute
- - network
- - openstack_nodes
diff --git a/settings/provisioner/virsh/topology/multi-node.yml b/settings/provisioner/virsh/topology/multi-node.yml
deleted file mode 100644
index cda9f38a02..0000000000
--- a/settings/provisioner/virsh/topology/multi-node.yml
+++ /dev/null
@@ -1,88 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 1
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 16384
- os:
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks:
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- network: &network_params
- interfaces: &interfaces
- data: &data_interface
- label: eth1
- config_params: &data_interface_params
- bootproto: static
- ipaddr: 10.0.0.1
- netmask: 255.255.255.0
- nm_controlled: "no"
- type: ethernet
- onboot: yes
- device: "{{ !lookup provisioner.nodes.controller.network.interfaces.data.label }}"
- external: &external_interface
- label: eth2
- config_params:
- <<: *data_interface_params
- ipaddr: ""
- device: "{{ !lookup provisioner.nodes.controller.network.interfaces.external.label }}"
- groups:
- - controller
- - openstack_nodes
-
- network:
- <<: *controller
- name: '{{ tmp.node_prefix }}network'
- network:
- <<: *network_params
- interfaces:
- <<: *interfaces
- data:
- <<: *data_interface
- config_params:
- <<: *data_interface_params
- ipaddr: 10.0.0.2
-
- groups:
- - openstack_nodes
- - network
-
- compute1:
- <<: *controller
- name: '{{ tmp.node_prefix }}compute1'
- network:
- <<: *network_params
- interfaces:
- <<: *interfaces
- data:
- <<: *data_interface
- config_params:
- <<: *data_interface_params
- ipaddr: 10.0.0.3
-
- groups:
- - openstack_nodes
- - compute
-
- compute2:
- <<: *controller
- name: '{{ tmp.node_prefix }}compute2'
- network:
- <<: *network_params
- interfaces:
- <<: *interfaces
- data:
- <<: *data_interface
- config_params:
- <<: *data_interface_params
- ipaddr: 10.0.0.4
-
- groups:
- - openstack_nodes
- - compute
diff --git a/settings/provisioner/virsh/topology/network/default.yml b/settings/provisioner/virsh/topology/network/default.yml
new file mode 100644
index 0000000000..2190ddb3a7
--- /dev/null
+++ b/settings/provisioner/virsh/topology/network/default.yml
@@ -0,0 +1,32 @@
+---
+data:
+ ip_address: "172.16.0.254"
+ netmask: "255.255.255.0"
+management:
+ ip_address: "10.0.0.1"
+ netmask: "255.255.255.0"
+ forward:
+ type: "nat"
+ dhcp:
+ range:
+ start: "10.0.0.2"
+ end: "10.0.0.100"
+ subnet_cidr: "10.0.0.0/24"
+ subnet_gateway: "10.0.0.1"
+ floating_ip:
+ start: "10.0.0.101"
+ end: "10.0.0.150"
+external:
+ ip_address: "192.168.1.1"
+ netmask: "255.255.255.0"
+ forward:
+ type: "nat"
+ dhcp:
+ range:
+ start: "192.168.1.2"
+ end: "192.168.1.100"
+ subnet_cidr: "192.168.1.0/24"
+ subnet_gateway: "192.168.1.1"
+ floating_ip:
+ start: "192.168.1.101"
+ end: "192.168.1.150"
diff --git a/settings/provisioner/virsh/topology/ospd_1cont_1comp.yml b/settings/provisioner/virsh/topology/ospd_1cont_1comp.yml
deleted file mode 100644
index 03b5067c9a..0000000000
--- a/settings/provisioner/virsh/topology/ospd_1cont_1comp.yml
+++ /dev/null
@@ -1,52 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 1
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 8192
- os: &os
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks: &disks
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 30G
- network: &network_params
- interfaces: &interfaces
- management: &mgmt_interface
- label: eth0
- data: &data_interface
- label: eth1
- external: &external_interface
- label: eth2
- groups:
- - controller
- - openstack_nodes
-
- compute:
- <<: *controller
- name: compute
- amount: 1
- cpu: 2
- memory: 6144
- disks:
- disk1:
- path: /var/lib/libvirt/images
- size: 20G
- groups:
- - compute
- - openstack_nodes
-
- undercloud:
- <<: *controller
- name: undercloud
- amount: 1
- memory: "{{ !lookup provisioner.image.memory }}"
- groups:
- - undercloud
- - tester
- - openstack_nodes
-
diff --git a/settings/provisioner/virsh/topology/ospd_1cont_1comp_1ceph.yml b/settings/provisioner/virsh/topology/ospd_1cont_1comp_1ceph.yml
deleted file mode 100644
index 10bd0e4199..0000000000
--- a/settings/provisioner/virsh/topology/ospd_1cont_1comp_1ceph.yml
+++ /dev/null
@@ -1,76 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 1
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 8192
- os: &os
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks: &disks
- disk1:
- path: /var/lib/libvirt/images
- dev: /dev/sda1
- size: 30G
- network: &network_params
- interfaces: &interfaces
- management: &mgmt_interface
- label: eth0
- data: &data_interface
- label: eth1
- external: &external_interface
- label: eth2
- groups:
- - controller
- - openstack_nodes
-
- compute:
- <<: *controller
- name: compute
- amount: 1
- cpu: 2
- memory: 6144
- disks:
- disk1:
- path: /var/lib/libvirt/images
- dev: /dev/sda1
- size: 20G
- groups:
- - compute
- - openstack_nodes
-
- ceph:
- <<: *controller
- name: ceph
- amount: 1
- cpu: 2
- memory: 4096
- disks:
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- disk2:
- <<: *disk1
- dev: /dev/vdb
- disk3:
- <<: *disk1
- dev: /dev/vdc
- disk4:
- <<: *disk1
- dev: /dev/vdd
- groups:
- - ceph
- - openstack_nodes
-
- undercloud:
- <<: *controller
- name: undercloud
- memory: "{{ !lookup provisioner.image.memory }}"
- groups:
- - undercloud
- - tester
- - openstack_nodes
-
diff --git a/settings/provisioner/virsh/topology/ospd_1cont_2comp.yml b/settings/provisioner/virsh/topology/ospd_1cont_2comp.yml
deleted file mode 100644
index 6cae026874..0000000000
--- a/settings/provisioner/virsh/topology/ospd_1cont_2comp.yml
+++ /dev/null
@@ -1,52 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 1
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 8192
- os: &os
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks: &disks
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 30G
- network: &network_params
- interfaces: &interfaces
- management: &mgmt_interface
- label: eth0
- data: &data_interface
- label: eth1
- external: &external_interface
- label: eth2
- groups:
- - controller
- - openstack_nodes
-
- compute:
- <<: *controller
- name: compute
- amount: 2
- cpu: 2
- memory: 6144
- disks:
- disk1:
- path: /var/lib/libvirt/images
- size: 20G
- groups:
- - compute
- - openstack_nodes
-
- undercloud:
- <<: *controller
- name: undercloud
- amount: 1
- memory: "{{ !lookup provisioner.image.memory }}"
- groups:
- - undercloud
- - tester
- - openstack_nodes
-
diff --git a/settings/provisioner/virsh/topology/ospd_1cont_2comp_1ceph.yml b/settings/provisioner/virsh/topology/ospd_1cont_2comp_1ceph.yml
deleted file mode 100644
index e66be42693..0000000000
--- a/settings/provisioner/virsh/topology/ospd_1cont_2comp_1ceph.yml
+++ /dev/null
@@ -1,76 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 1
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 8192
- os: &os
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks: &disks
- disk1:
- path: /var/lib/libvirt/images
- dev: /dev/sda1
- size: 30G
- network: &network_params
- interfaces: &interfaces
- management: &mgmt_interface
- label: eth0
- data: &data_interface
- label: eth1
- external: &external_interface
- label: eth2
- groups:
- - controller
- - openstack_nodes
-
- compute:
- <<: *controller
- name: compute
- amount: 2
- cpu: 2
- memory: 6144
- disks:
- disk1:
- path: /var/lib/libvirt/images
- dev: /dev/sda1
- size: 20G
- groups:
- - compute
- - openstack_nodes
-
- ceph:
- <<: *controller
- name: ceph
- amount: 1
- cpu: 2
- memory: 4096
- disks:
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- disk2:
- <<: *disk1
- dev: /dev/vdb
- disk3:
- <<: *disk1
- dev: /dev/vdc
- disk4:
- <<: *disk1
- dev: /dev/vdd
- groups:
- - ceph
- - openstack_nodes
-
- undercloud:
- <<: *controller
- name: undercloud
- memory: "{{ !lookup provisioner.image.memory }}"
- groups:
- - undercloud
- - tester
- - openstack_nodes
-
diff --git a/settings/provisioner/virsh/topology/ospd_1cont_2comp_3ceph.yml b/settings/provisioner/virsh/topology/ospd_1cont_2comp_3ceph.yml
deleted file mode 100644
index ac67b3b25b..0000000000
--- a/settings/provisioner/virsh/topology/ospd_1cont_2comp_3ceph.yml
+++ /dev/null
@@ -1,70 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 1
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 8192
- os: &os
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks: &disks
- disk1:
- path: /var/lib/libvirt/images
- dev: /dev/sda1
- size: 30G
- network: &network_params
- interfaces: &interfaces
- management: &mgmt_interface
- label: eth0
- data: &data_interface
- label: eth1
- external: &external_interface
- label: eth2
- groups:
- - controller
- - openstack_nodes
-
- compute:
- <<: *controller
- name: compute
- amount: 2
- cpu: 2
- memory: 6144
- disks:
- disk1:
- path: /var/lib/libvirt/images
- dev: /dev/sda1
- size: 20G
- groups:
- - compute
- - openstack_nodes
-
- ceph:
- <<: *controller
- name: ceph
- amount: 3
- cpu: 2
- memory: 4096
- disks:
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- disk2:
- <<: *disk1
- dev: /dev/vdb
- groups:
- - ceph
- - openstack_nodes
-
- undercloud:
- <<: *controller
- name: undercloud
- memory: "{{ !lookup provisioner.image.memory }}"
- groups:
- - undercloud
- - tester
- - openstack_nodes
-
diff --git a/settings/provisioner/virsh/topology/ospd_3cont_1comp.yml b/settings/provisioner/virsh/topology/ospd_3cont_1comp.yml
deleted file mode 100644
index 8b8f1e703b..0000000000
--- a/settings/provisioner/virsh/topology/ospd_3cont_1comp.yml
+++ /dev/null
@@ -1,57 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 3
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 8192
- os: &os
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks: &disks
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- network: &network_params
- interfaces: &interfaces
- management: &mgmt_interface
- label: eth0
- data: &data_interface
- label: eth1
- external: &external_interface
- label: eth2
- groups:
- - controller
- - openstack_nodes
-
- compute:
- <<: *controller
- name: compute1
- amount: 1
- cpu: 2
- memory: 6144
- disks:
- disk1:
- path: /var/lib/libvirt/images
- size: 20G
- groups:
- - compute
- - openstack_nodes
-
- undercloud:
- <<: *controller
- name: undercloud
- amount: 1
- memory: "{{ !lookup provisioner.image.memory }}"
- disks:
- <<: *disks
- disk1:
- <<: *disk1
- size: 20G
- groups:
- - undercloud
- - tester
- - openstack_nodes
-
diff --git a/settings/provisioner/virsh/topology/ospd_3cont_1comp_1ceph.yml b/settings/provisioner/virsh/topology/ospd_3cont_1comp_1ceph.yml
deleted file mode 100644
index 947c9fcd1f..0000000000
--- a/settings/provisioner/virsh/topology/ospd_3cont_1comp_1ceph.yml
+++ /dev/null
@@ -1,81 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 3
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 8192
- os: &os
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks: &disks
- disk1:
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- network: &network_params
- interfaces: &interfaces
- management: &mgmt_interface
- label: eth0
- data: &data_interface
- label: eth1
- external: &external_interface
- label: eth2
- groups:
- - controller
- - openstack_nodes
-
- compute:
- <<: *controller
- name: compute1
- amount: 1
- cpu: 2
- memory: 6144
- disks:
- disk1:
- path: /var/lib/libvirt/images
- size: 20G
- groups:
- - compute
- - openstack_nodes
-
- ceph:
- <<: *controller
- name: ceph
- amount: 1
- cpu: 2
- memory: 4096
- disks:
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- disk2:
- <<: *disk1
- dev: /dev/vdb
- disk3:
- <<: *disk1
- dev: /dev/vdc
- disk4:
- <<: *disk1
- dev: /dev/vdd
- groups:
- - ceph
- - openstack_nodes
-
- undercloud:
- <<: *controller
- name: undercloud
- amount: 1
- memory: "{{ !lookup provisioner.image.memory }}"
- disks:
- <<: *disks
- disk1:
- <<: *disk1
- size: 20G
- groups:
- - undercloud
- - tester
- - openstack_nodes
-
diff --git a/settings/provisioner/virsh/topology/ospd_3cont_1comp_3ceph.yml b/settings/provisioner/virsh/topology/ospd_3cont_1comp_3ceph.yml
deleted file mode 100644
index c979da233c..0000000000
--- a/settings/provisioner/virsh/topology/ospd_3cont_1comp_3ceph.yml
+++ /dev/null
@@ -1,75 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 3
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 8192
- os: &os
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks: &disks
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- network: &network_params
- interfaces: &interfaces
- management: &mgmt_interface
- label: eth0
- data: &data_interface
- label: eth1
- external: &external_interface
- label: eth2
- groups:
- - controller
- - openstack_nodes
-
- compute:
- <<: *controller
- name: compute1
- amount: 1
- cpu: 2
- memory: 6144
- disks:
- disk1:
- path: /var/lib/libvirt/images
- size: 20G
- groups:
- - compute
- - openstack_nodes
-
- ceph:
- <<: *controller
- name: ceph
- amount: 3
- cpu: 2
- memory: 4096
- disks:
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- disk2:
- <<: *disk1
- dev: /dev/vdb
- groups:
- - ceph
- - openstack_nodes
-
- undercloud:
- <<: *controller
- name: undercloud
- amount: 1
- memory: "{{ !lookup provisioner.image.memory }}"
- disks:
- <<: *disks
- disk1:
- <<: *disk1
- size: 20G
- groups:
- - undercloud
- - tester
- - openstack_nodes
-
diff --git a/settings/provisioner/virsh/topology/ospd_3cont_2comp.yml b/settings/provisioner/virsh/topology/ospd_3cont_2comp.yml
deleted file mode 100644
index 2a27bfd09d..0000000000
--- a/settings/provisioner/virsh/topology/ospd_3cont_2comp.yml
+++ /dev/null
@@ -1,57 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 3
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 8192
- os: &os
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks: &disks
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- network: &network_params
- interfaces: &interfaces
- management: &mgmt_interface
- label: eth0
- data: &data_interface
- label: eth1
- external: &external_interface
- label: eth2
- groups:
- - controller
- - openstack_nodes
-
- compute:
- <<: *controller
- name: compute1
- amount: 2
- cpu: 2
- memory: 6144
- disks:
- disk1:
- path: /var/lib/libvirt/images
- size: 20G
- groups:
- - compute
- - openstack_nodes
-
- undercloud:
- <<: *controller
- name: undercloud
- amount: 1
- memory: "{{ !lookup provisioner.image.memory }}"
- disks:
- <<: *disks
- disk1:
- <<: *disk1
- size: 20G
- groups:
- - undercloud
- - tester
- - openstack_nodes
-
diff --git a/settings/provisioner/virsh/topology/ospd_3cont_2comp_1ceph.yml b/settings/provisioner/virsh/topology/ospd_3cont_2comp_1ceph.yml
deleted file mode 100644
index 09859a9f4d..0000000000
--- a/settings/provisioner/virsh/topology/ospd_3cont_2comp_1ceph.yml
+++ /dev/null
@@ -1,81 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 3
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 8192
- os: &os
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks: &disks
- disk1:
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- network: &network_params
- interfaces: &interfaces
- management: &mgmt_interface
- label: eth0
- data: &data_interface
- label: eth1
- external: &external_interface
- label: eth2
- groups:
- - controller
- - openstack_nodes
-
- compute:
- <<: *controller
- name: compute1
- amount: 2
- cpu: 2
- memory: 6144
- disks:
- disk1:
- path: /var/lib/libvirt/images
- size: 20G
- groups:
- - compute
- - openstack_nodes
-
- ceph:
- <<: *controller
- name: ceph
- amount: 1
- cpu: 2
- memory: 4096
- disks:
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- disk2:
- <<: *disk1
- dev: /dev/vdb
- disk3:
- <<: *disk1
- dev: /dev/vdc
- disk4:
- <<: *disk1
- dev: /dev/vdd
- groups:
- - ceph
- - openstack_nodes
-
- undercloud:
- <<: *controller
- name: undercloud
- amount: 1
- memory: "{{ !lookup provisioner.image.memory }}"
- disks:
- <<: *disks
- disk1:
- <<: *disk1
- size: 20G
- groups:
- - undercloud
- - tester
- - openstack_nodes
-
diff --git a/settings/provisioner/virsh/topology/ospd_3cont_2comp_3ceph.yml b/settings/provisioner/virsh/topology/ospd_3cont_2comp_3ceph.yml
deleted file mode 100644
index 595616a549..0000000000
--- a/settings/provisioner/virsh/topology/ospd_3cont_2comp_3ceph.yml
+++ /dev/null
@@ -1,76 +0,0 @@
----
-provisioner:
- nodes:
- controller: &controller
- name: controller
- amount: 3
- cpu: "{{ !lookup provisioner.image.cpu }}"
- memory: 8192
- os: &os
- type: linux
- variant: "{{ !lookup provisioner.image.os.variant }}"
- disks: &disks
- disk1: &disk1
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- network: &network_params
- interfaces: &interfaces
- management: &mgmt_interface
- label: eth0
- data: &data_interface
- label: eth1
- external: &external_interface
- label: eth2
- groups:
- - controller
- - openstack_nodes
-
- compute:
- <<: *controller
- name: compute1
- amount: 2
- cpu: 2
- memory: 6144
- disks:
- disk1:
- path: /var/lib/libvirt/images
- size: 20G
- groups:
- - compute
- - openstack_nodes
-
- ceph:
- <<: *controller
- name: ceph
- amount: 3
- cpu: 2
- memory: 4096
- disks:
- disk1:
- path: /var/lib/libvirt/images
- dev: /dev/vda
- size: 20G
- disk2:
- path: /var/lib/libvirt/images
- dev: /dev/vdb
- size: 20G
- groups:
- - ceph
- - openstack_nodes
-
- undercloud:
- <<: *controller
- name: undercloud
- amount: 1
- memory: "{{ !lookup provisioner.image.memory }}"
- disks:
- <<: *disks
- disk1:
- <<: *disk1
- size: 20G
- groups:
- - undercloud
- - tester
- - openstack_nodes
-
diff --git a/settings/provisioner/virsh/virsh.spec b/settings/provisioner/virsh/virsh.spec
index e9cf4a4ec2..7e1931a0d9 100644
--- a/settings/provisioner/virsh/virsh.spec
+++ b/settings/provisioner/virsh/virsh.spec
@@ -3,33 +3,40 @@ subparsers:
virsh:
help: Provision systems using virsh
groups:
- - title: host
+ - title: Hypervisor
options:
- host:
- type: str
+ host-address:
+ type: Value
help: Address/FQDN of the BM hypervisor
- ssh-user:
- type: str
+ required: yes
+ host-user:
+ type: Value
help: User to SSH to the host with
- ssh-key:
- type: str
+ default: root
+ host-key:
+ type: Value
help: "User's SSH key"
+ default: ~/.ssh/id_rsa
- title: image
options:
image-file:
- type: str
+ type: Value
help: An image to provision the host with
+ required: yes
image-server:
- type: str
+ type: Value
help: Base URL of the image file server
+ required: yes
- title: topology
options:
- network:
- type: str
+ topology-network:
+ type: YamlFile
help: Network
- topology:
- type: str
- help: 'Provision topology (default: __DEFAULT__)'
+ default: default.yml
+ topology-nodes:
+ type: Topology
+ help: Provision topology.
+ default: "1_controller,1_compute,1_undercloud"
- title: common
options:
dry-run:
@@ -55,3 +62,6 @@ subparsers:
from-file:
type: IniFile
help: the ini file with the list of arguments
+ generate-conf-file:
+ type: str
+ help: generate configuration file (ini) containing default values and exits. This file is than can be used with the from-file argument
diff --git a/settings/provisioner/virsh/virsh.yml b/settings/provisioner/virsh/virsh.yml
index 0fa08052ba..5d27c41da3 100644
--- a/settings/provisioner/virsh/virsh.yml
+++ b/settings/provisioner/virsh/virsh.yml
@@ -3,31 +3,26 @@
provisioner:
type: virsh
- hosts:
- host1:
- name: virthost
- groups:
- - virthost
+ host:
+ name: virthost
+ groups:
+ - virthost
+ image:
+ memory: "16384"
+ cpu: "4"
+ os:
+ variant: rhel7
+ disk:
+ size: "40G"
packages:
- libvirt
- qemu-kvm
- virt-manager
- virt-install
- - libguestfs-tools
- xorg-x11-apps
- xauth
- virt-viewer
- - libguestfs-xfs
- - sshpass
-
- image:
- memory: "16384"
- cpu: "4"
- os:
- variant: rhel7
- disk:
- size: "40G"
distro:
name: rhel
diff --git a/tests/test_conf.py b/tests/test_conf.py
index 8658c73f20..4dca465904 100644
--- a/tests/test_conf.py
+++ b/tests/test_conf.py
@@ -1,13 +1,34 @@
-import os
-
from tests.test_cwd import utils
our_cwd_setup = utils.our_cwd_setup
-def test_get_config_dir(our_cwd_setup):
+def test_get_config_dir_project_defaults(our_cwd_setup):
from cli import conf
+ from cli import utils
+
+ utils.IR_CONF_FILE = 'non_existing_conf.ini'
+
conf_file = conf.load_config_file()
- assert os.path.abspath(
- conf_file.get("defaults", "settings")) == os.path.join(os.getcwd(),
- "fake_settings")
+
+ assert 'defaults' in conf_file.sections(),\
+ "'defaults' section not in project's default conf file"
+
+ assert len(conf_file.sections()) == 1, \
+ "'defaults' section isn't the only section in the " \
+ "project's default conf file"
+
+ file_default_options = conf_file.options('defaults')
+ file_default_options.sort()
+
+ module_default_options = conf.DEFAULT_CONF_DIRS.keys()
+ module_default_options.sort()
+
+ assert file_default_options == module_default_options, \
+ "Options in conf module and conf file aren't the same"
+
+ from os.path import dirname, join
+ for option in conf_file.options('defaults'):
+ assert conf_file.get('defaults', option) == join(dirname(dirname(
+ __file__)), conf.DEFAULT_CONF_DIRS[option]),\
+ "Incorrect Option's (%s) value" % option
diff --git a/tests/test_cwd/infrared.cfg b/tests/test_cwd/infrared.cfg
deleted file mode 100644
index 2d2d578ada..0000000000
--- a/tests/test_cwd/infrared.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-[defaults]
-settings = fake_settings
diff --git a/tests/test_cwd/lookup_new_style_circular_reference.yml b/tests/test_cwd/lookup_new_style_circular_reference.yml
new file mode 100644
index 0000000000..c5d0271379
--- /dev/null
+++ b/tests/test_cwd/lookup_new_style_circular_reference.yml
@@ -0,0 +1,6 @@
+foo:
+ test1: "{{ !lookup bar.test1 }}"
+
+bar:
+ test1: "{{ !lookup foo.test1 }}"
+
diff --git a/tests/test_provision.py b/tests/test_provision.py
new file mode 100644
index 0000000000..db3d8a866f
--- /dev/null
+++ b/tests/test_provision.py
@@ -0,0 +1,44 @@
+import os
+
+from cli import spec
+
+
+def test_dynamic_topology(tmpdir):
+ """
+ Verifiesthe topology is dynamically constructed.
+ """
+ root_dir = tmpdir.mkdir("topology")
+ controller_yml = root_dir.join("controller.yaml")
+ compute_yml = root_dir.join("compute.yaml")
+ ceph_yml = root_dir.join("ceph.yaml")
+ controller_yml.write("""---
+memory: 8192
+os: linux
+name: controller
+""")
+ compute_yml.write("""---
+memory: 1024
+os: rhel
+name: compute
+""")
+ ceph_yml.write("""---
+memory: 2048
+os: fedora
+name: ceph
+""")
+ # prepare config
+ app_path = os.path.join(root_dir.strpath, "..")
+
+ spec.TopologyArgument.settings_dir = app_path
+ topology_arg = spec.TopologyArgument("10_controller,2_compute")
+ # process topology
+ topology_arg.resolve_value("topology", {})
+
+ topology = topology_arg.value
+ assert 'controller' in topology
+ assert 'compute' in topology
+ assert 'ceph' not in topology
+ assert topology['controller']['amount'] == 10
+ assert topology['compute']['amount'] == 2
+ assert topology['controller']['memory'] == 8192
+ assert topology['compute']['memory'] == 1024
diff --git a/tests/test_spec.py b/tests/test_spec.py
new file mode 100644
index 0000000000..f075cdc8d0
--- /dev/null
+++ b/tests/test_spec.py
@@ -0,0 +1,88 @@
+import ConfigParser
+import os
+
+import pytest
+from cli import exceptions
+from cli import spec
+
+
+@pytest.mark.parametrize("res_args, options, req_args, nonreq_args", [
+ [{'host': spec.ValueArgument(),
+ 'command0': 'virsh',
+ 'ssh-user': spec.ValueArgument()},
+ {
+ 'host': {'help': 'help', 'required': True},
+ 'ssh-user': {'help': 'help2', 'required': True},
+ 'ssh-key': {'help': 'help3', 'default': 'id_rsa'}
+ }, ['host', 'ssh-user'], ['ssh-key']],
+])
+def test_required_option_exception(res_args,
+ options,
+ req_args,
+ nonreq_args):
+
+ with pytest.raises(exceptions.IRConfigurationException) as ex_info:
+ spec.override_default_values(res_args, options)
+
+ for arg in req_args:
+ assert arg in ex_info.value.message
+
+ for arg in nonreq_args:
+ assert arg not in ex_info.value.message
+
+
+@pytest.mark.parametrize("res_args, options, expected_args", [
+ # data set #1
+ [{'host': None,
+ 'command0': 'virsh',
+ 'from-file': {
+ 'virsh': {
+ 'host': 'earth',
+ }
+ },
+ 'ssh-user': 'root',
+ 'ssh-key': None},
+ {'virsh': {
+ 'host': {'help': 'help', 'required': True},
+ 'ssh-user': {'help': 'help2', 'required': True},
+ 'ssh-key': {'help': 'help3', 'required': True, 'default': 'id_rsa'}
+ }},
+ {'host': 'earth',
+ 'command0': 'virsh',
+ 'from-file': {
+ 'virsh': {
+ 'host': 'earth',
+ }
+ },
+ 'ssh-user': 'id_rsa'}],
+
+ # todo(yfried): enable this in the future
+ # [{'host': None,
+ # 'command0': 'virsh',
+ # 'from-file': {
+ # 'virsh': {
+ # 'host': 'earth',
+ # }
+ # },
+ # 'ssh-user': None,
+ # 'ssh-key': None},
+ # {'virsh': {
+ # 'opt1': {'requires_only': ['host']},
+ # 'host': {'help': 'help', 'required': True},
+ # 'ssh-user': {'help': 'help2', 'required': True},
+ # 'ssh-key': {'help': 'help3', 'required': True, 'default': 'id_rsa'}
+ # }},
+ # {'host': 'earth',
+ # 'command0': 'virsh',
+ # 'from-file': {
+ # 'virsh': {
+ # 'host': 'earth',
+ # }
+ # },
+ # 'ssh-user': 'id_rsa'}]
+])
+def test_required_options_are_set(res_args,
+ options,
+ expected_args):
+ actual_args = spec.override_default_values(res_args, options)
+ cmp(actual_args, expected_args)
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 5d35cda043..e578f964c8 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -13,3 +13,73 @@ def test_dict_insert(tested, val, key, expected):
from cli import utils
utils.dict_insert(tested, val, *key)
assert tested == expected
+
+
+def test_dict_merge():
+ from cli.utils import dict_merge
+
+ first_dict = {'a': 1, 'b': 2, 'c': {'d': 'foo1', 'e': 'bar', 'list1': [
+ 'a', 'b', 'c']}, 'list2': [1, 2, 3]}
+ second_dict = {'a': 2, 'c': {'d': 'foo2', 'f': 5, 'list1': [3, 4, 5]},
+ 'g': 'bla', 5: 'yy', 'list3': ['a', 2]}
+ expected_result = {'a': 2, 'b': 2, 'c': {'d': 'foo2', 'e': 'bar', 'f': 5,
+ 'list1': [3, 4, 5]}, 'g': 'bla',
+ 5: 'yy', 'list2': [1, 2, 3], 'list3': ['a', 2]}
+
+ dict_merge(first_dict, second_dict)
+
+ assert not cmp(first_dict, expected_result)
+
+
+@pytest.mark.parametrize("first, second, expected", [
+ [dict(a=None, b=1, c=[1, 2, 3]),
+ dict(a=2, b=3, c=[4, 5]),
+ dict(a=2, b=1, c=[1, 2, 3, 4, 5])],
+
+ [dict(a={'b': 1, 'c': 2}, e=[1, ]),
+ dict(a={'f': 3}, e=4, d=5),
+ dict(a={'b': 1, 'c': 2, 'f': 3}, e=[1, 4], d=5)]
+])
+def test_dict_merge_none_resolver(first, second, expected):
+ from cli.utils import dict_merge, ConflictResolver
+
+ dict_merge(first, second, conflict_resolver=ConflictResolver.none_resolver)
+ assert not cmp(first, expected)
+
+
+@pytest.mark.parametrize("haystack, output", [
+ ({}, []),
+ ({"needle": "val1",
+ "bad": "input"}, ["val1"]),
+ ({"needle": "val1",
+ "bad": "input",
+ "nested1": {"needle": "val2", "differnt": "values"}
+ }, ["val1", "val2"]),
+ ({"needle": "val1",
+ "bad": "input",
+ "nested2": {"netsted3": {"needle": "val3",
+ "differnt": "values"}},
+ "nested1": {"needle": "val2", "differnt": "values"}
+ }, ["val1", "val2", "val3"]),
+ ({"bad": "input",
+ "needle": {"netsted3": {"needle": "val3",
+ "differnt": "values"}},
+ "nested1": {"needle": "val2", "differnt": "values"}
+ }, ["val2", "val3",
+ {"netsted3": {"needle": "val3", "differnt": "values"}}]),
+ ({"bad": "input",
+ "list": [{"needle": {"netsted3": {"needle": "val3",
+ "differnt": "values"}},
+ "nested1": {"needle": "val2", "differnt": "values"}}]
+ }, ["val2", "val3",
+ {"netsted3": {"needle": "val3", "differnt": "values"}}])
+
+])
+def test_search_tree(haystack, output):
+ from cli.utils import search_tree
+
+ result = search_tree("needle", haystack)
+ # can't make set of dict, but we still don't care about order
+ assert len(result) == len(output)
+ for item in output:
+ assert item in result
diff --git a/tests/test_yaml.py b/tests/test_yaml.py
index fa4c29ff6c..b5b0444b94 100644
--- a/tests/test_yaml.py
+++ b/tests/test_yaml.py
@@ -3,6 +3,7 @@
import configure
import pytest
import yaml
+from cli import exceptions
from tests.test_cwd import utils
@@ -169,3 +170,24 @@ def test_recursive_lookup(our_cwd_setup, lookup_style):
assert settings['foo']['test1'] == "key was found"
assert settings['foo']['test2'] == "key was found"
+
+
+def test_circular_reference_lookup():
+ import cli.yamls
+
+ tester_file_name = 'lookup_new_style_circular_reference.yml'
+ tester_file = os.path.join(utils.TESTS_CWD, tester_file_name)
+
+ # load settings from yaml and set them into Lookup 'settings' class var
+ cli.yamls.Lookup.settings = configure.Configuration().from_file(
+ tester_file).configure()
+ # cli.yamls.Lookup.handling_nested_lookups = False
+ cli.yamls.Lookup.in_string_lookup()
+
+ # dump the settings in order to retrieve the key's value and load them
+ # again for value validation
+
+ with pytest.raises(exceptions.IRInfiniteLookupException):
+ yaml.safe_load(
+ yaml.safe_dump(cli.yamls.Lookup.settings,
+ default_flow_style=False))