From 46919b64db6764fa74c7500422cce85728d04f4a Mon Sep 17 00:00:00 2001 From: John Fulton Date: Tue, 21 Dec 2021 17:04:50 +0000 Subject: [PATCH] Squash standalone deployed_ceph for pythonclient to Wallaby This squash backports the standalone deployed_ceph feature to Wallaby without introducing related bugs. It has 5 commits. Depends-On: https://review.opendev.org/c/openstack/tripleo-ansible/+/845595 1. Add --mon-ip option to deployed ceph Add --mon-ip option to 'openstack overcloud ceph deploy', to easily override the variable tripleo_cephadm_first_mon_ip from the CLI. This option makes deployed ceph easier when using standalone. Change-Id: Id9a61356e8fd4618ce68faa2677f4ed30981b7d6 (cherry picked from commit 2ac3953410337361777974cb2dccde3231ee3116) 2. Make deployed ceph baremetal file optional If the --ceph-spec option is passed to 'openstack overcloud ceph deploy', then it is not necessary for the path to the environment file output from 'openstack overcloud node provision' to be passed. Add conditional to ensure that either --ceph-spec or are used and add two unit tests to cover all three scenarios: a. metal is passed, b. spec is passed and c. neither are passed so a failure is asserted. Change-Id: Ibd350956f401fa48aaeae89fdd5e0bd9c28d0685 (cherry picked from commit 84055814f9303a3fea6a252a24c2d9d123535bea) 3. Introduce "openstack overcloud ceph spec" This command may be used to create a cephadm spec file as a function of the output of metalsmith and a TripleO roles file. Calls cli-deployed-ceph.yaml with ceph_spec tag so users may create the Ceph spec in a separate step from the Ceph Deployment. The Ceph spec file may be then be passed to "openstack overcloud ceph deploy". Also, supports a --standalone feature which directly calls the ceph_spec library in tripleo-common in the depends-on chain. As Ansible is used less the tripleo-pythonclient can use the ceph_spec library directly for more than just the --standalone feature. Change-Id: Icd18de08ebc818441e45ff7ce9f348fbc5813253 (cherry picked from commit 3701f2ac318373250210c85f3483cd4f1be17dc7) 4. Add --standalone to overcloud ceph user and deploy Both 'openstack overcloud ceph user enable' and 'openstack overcloud ceph deploy' currently use Ansible and require an inventory with hosts matching those in the Ceph spec. The inventory is created in the working directory when 'openstack overcloud node provision' runs but for a standalone deployment the user would need to create it manually. Abstract this detail away by providing a --standalone option for single node deploys which do not use metalsmith. Change-Id: Icb785f015ee69d45dd513aef09fac625de421250 (cherry picked from commit 88ec87caf625f3a28382c484ec93ebdae1fe33b6) 5. Build standalone ceph_spec with Ansible Instead of calling the ceph_spec library directly from the python-tripleoclient call an ansible wrapper to it instead so that the library does not need to live in tripleo-common and bug 1961325 can be avoided. Closes-Bug: 1961325 Change-Id: I7514c95b5c3c24a82e8703556d4205b272270511 (cherry picked from commit ec411b93297f2c841b03f7afb55a51bf2a0f0d0e) --- .../overcloud_ceph_spec-e1cfd358c4db2b22.yaml | 14 + setup.cfg | 1 + .../v2/overcloud_ceph/test_overcloud_ceph.py | 102 +++++++ tripleoclient/utils.py | 23 ++ tripleoclient/v2/overcloud_ceph.py | 285 ++++++++++++++++-- 5 files changed, 406 insertions(+), 19 deletions(-) create mode 100644 releasenotes/notes/overcloud_ceph_spec-e1cfd358c4db2b22.yaml diff --git a/releasenotes/notes/overcloud_ceph_spec-e1cfd358c4db2b22.yaml b/releasenotes/notes/overcloud_ceph_spec-e1cfd358c4db2b22.yaml new file mode 100644 index 000000000..e2182bedb --- /dev/null +++ b/releasenotes/notes/overcloud_ceph_spec-e1cfd358c4db2b22.yaml @@ -0,0 +1,14 @@ +--- +features: + - | + New command "openstack overcloud ceph spec" has been added. This command + may be used to create a cephadm spec file as a function of the output of + metalsmith and a TripleO roles file. For example, if metalsmith output a + file with multiple hosts of differing roles and each role contained various + Ceph services, then a cephadm spec file could parse these files and return + input compatible with cephadm. The ceph spec file may be then be passed to + "openstack overcloud ceph deploy" so that cephadm deploys only those Ceph + services on those hosts. This feature should save users from the need to + create two different files containing much of the same data and make it + easier and less error prone to include Ceph in a deployment without the + need to manually create the Ceph spec file. diff --git a/setup.cfg b/setup.cfg index 3abcbfd21..e4b1b88e6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -43,6 +43,7 @@ openstack.tripleoclient.v2 = overcloud_netenv_validate = tripleoclient.v1.overcloud_netenv_validate:ValidateOvercloudNetenv overcloud_cell_export = tripleoclient.v1.overcloud_cell:ExportCell overcloud_ceph_deploy = tripleoclient.v2.overcloud_ceph:OvercloudCephDeploy + overcloud_ceph_spec = tripleoclient.v2.overcloud_ceph:OvercloudCephSpec overcloud_ceph_user_disable = tripleoclient.v2.overcloud_ceph:OvercloudCephUserDisable overcloud_ceph_user_enable = tripleoclient.v2.overcloud_ceph:OvercloudCephUserEnable overcloud_config_download = tripleoclient.v1.overcloud_config:DownloadConfig diff --git a/tripleoclient/tests/v2/overcloud_ceph/test_overcloud_ceph.py b/tripleoclient/tests/v2/overcloud_ceph/test_overcloud_ceph.py index f76eda46a..1552485db 100644 --- a/tripleoclient/tests/v2/overcloud_ceph/test_overcloud_ceph.py +++ b/tripleoclient/tests/v2/overcloud_ceph/test_overcloud_ceph.py @@ -45,6 +45,7 @@ class TestOvercloudCephDeploy(fakes.FakePlaybookExecution): '--stack', 'overcloud', '--skip-user-create', '--skip-hosts-config', + '--mon-ip', '127.0.0.1', '--cephadm-ssh-user', 'jimmy', '--output', 'deployed-ceph.yaml', '--container-namespace', 'quay.io/ceph', @@ -66,8 +67,10 @@ class TestOvercloudCephDeploy(fakes.FakePlaybookExecution): "deployed_ceph_tht_path": mock.ANY, "working_dir": mock.ANY, "stack_name": 'overcloud', + "tripleo_cephadm_standalone": False, 'tripleo_cephadm_ssh_user': 'jimmy', 'tripleo_cephadm_cluster': 'ceph', + 'tripleo_cephadm_first_mon_ip': '127.0.0.1', 'tripleo_roles_path': mock.ANY, 'tripleo_cephadm_container_ns': 'quay.io/ceph', 'tripleo_cephadm_container_image': 'ceph', @@ -75,6 +78,53 @@ class TestOvercloudCephDeploy(fakes.FakePlaybookExecution): } ) + @mock.patch('tripleoclient.utils.get_ceph_networks', autospect=True) + @mock.patch('tripleoclient.utils.TempDirs', autospect=True) + @mock.patch('os.path.abspath', autospect=True) + @mock.patch('os.path.exists', autospect=True) + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) + def test_deploy_ceph_spec(self, mock_playbook, mock_abspath, + mock_path_exists, mock_tempdirs, + mock_get_ceph_networks): + arglist = ['--yes', + '--stack', 'overcloud', + '--skip-user-create', + '--skip-hosts-config', + '--mon-ip', '127.0.0.1', + '--ceph-spec', 'ceph_spec.yaml', + '--cephadm-ssh-user', 'jimmy', + '--output', 'deployed-ceph.yaml', + '--container-namespace', 'quay.io/ceph', + '--container-image', 'ceph', + '--container-tag', 'latest'] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.cmd.take_action(parsed_args) + mock_playbook.assert_called_once_with( + playbook='cli-deployed-ceph.yaml', + inventory=mock.ANY, + workdir=mock.ANY, + playbook_dir=mock.ANY, + verbosity=3, + skip_tags='cephadm_ssh_user', + reproduce_command=False, + extra_vars_file=mock.ANY, + extra_vars={ + "deployed_ceph_tht_path": mock.ANY, + "working_dir": mock.ANY, + "stack_name": 'overcloud', + "tripleo_cephadm_standalone": False, + 'tripleo_roles_path': mock.ANY, + 'tripleo_cephadm_first_mon_ip': '127.0.0.1', + 'tripleo_cephadm_cluster': 'ceph', + 'dynamic_ceph_spec': False, + 'ceph_spec_path': mock.ANY, + 'tripleo_cephadm_container_ns': 'quay.io/ceph', + 'tripleo_cephadm_container_image': 'ceph', + 'tripleo_cephadm_container_tag': 'latest', + 'tripleo_cephadm_ssh_user': 'jimmy', + } + ) + @mock.patch('os.path.abspath', autospect=True) @mock.patch('os.path.exists', autospect=True) def test_overcloud_deploy_ceph_no_overwrite(self, mock_abspath, @@ -104,6 +154,16 @@ class TestOvercloudCephDeploy(fakes.FakePlaybookExecution): self.assertRaises(osc_lib_exc.CommandError, self.cmd.take_action, parsed_args) + @mock.patch('os.path.abspath', autospect=True) + @mock.patch('os.path.exists', autospect=True) + def test_overcloud_deploy_ceph_no_metal(self, mock_abspath, + mock_path_exists): + arglist = ['--stack', 'overcloud', + '--output', 'deployed-ceph.yaml'] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.assertRaises(osc_lib_exc.CommandError, + self.cmd.take_action, parsed_args) + class TestOvercloudCephUserDisable(fakes.FakePlaybookExecution): def setUp(self): @@ -265,3 +325,45 @@ class TestOvercloudCephUserEnable(fakes.FakePlaybookExecution): "tripleo_cephadm_action": 'enable' } ) + + +class TestOvercloudCephSpec(fakes.FakePlaybookExecution): + + def setUp(self): + super(TestOvercloudCephSpec, self).setUp() + + # Get the command object to test + app_args = mock.Mock() + app_args.verbose_level = 1 + self.app.options = fakes.FakeOptions() + self.cmd = overcloud_ceph.OvercloudCephSpec(self.app, + app_args) + + @mock.patch('tripleoclient.utils.TempDirs', autospect=True) + @mock.patch('os.path.abspath', autospect=True) + @mock.patch('os.path.exists', autospect=True) + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) + def test_overcloud_ceph_spec(self, mock_playbook, mock_abspath, + mock_path_exists, mock_tempdirs): + arglist = ['deployed-metal.yaml', '--yes', + '--stack', 'overcloud', + '--roles-data', 'roles_data.yaml', + '--osd-spec', 'osd_spec.yaml', + '--output', 'ceph_spec.yaml'] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.cmd.take_action(parsed_args) + mock_playbook.assert_called_once_with( + playbook='cli-deployed-ceph.yaml', + inventory=mock.ANY, + workdir=mock.ANY, + playbook_dir=mock.ANY, + verbosity=3, + tags='ceph_spec', + reproduce_command=False, + extra_vars={ + "baremetal_deployed_path": mock.ANY, + 'tripleo_roles_path': mock.ANY, + 'osd_spec_path': mock.ANY, + 'ceph_spec_path': mock.ANY, + } + ) diff --git a/tripleoclient/utils.py b/tripleoclient/utils.py index 469ba42ec..67ba11610 100644 --- a/tripleoclient/utils.py +++ b/tripleoclient/utils.py @@ -3222,6 +3222,29 @@ def get_host_groups_from_ceph_spec(ceph_spec_path, prefix='', return hosts +def standalone_ceph_inventory(working_dir): + """return an ansible inventory for deployed ceph standalone + :param working_dir: directory where inventory should be written + :return string: the path to the inventory + """ + host = get_hostname() + inv = \ + {'Standalone': + {'hosts': {host: {}, + 'undercloud': {}}, + 'vars': {'ansible_connection': 'local', + 'ansible_host': host, + 'ansible_python_interpreter': sys.executable}}, + 'allovercloud': + {'children': {'Standalone': {}}}} + + path = os.path.join(working_dir, + constants.TRIPLEO_STATIC_INVENTORY) + with open(path, 'w') as f: + f.write(yaml.safe_dump(inv)) + return path + + def cleanup_host_entry(entry): # remove any tab or space excess entry_stripped = re.sub('[ \t]+', ' ', str(entry).rstrip()) diff --git a/tripleoclient/v2/overcloud_ceph.py b/tripleoclient/v2/overcloud_ceph.py index 68afd4ee6..2d9989fb6 100644 --- a/tripleoclient/v2/overcloud_ceph.py +++ b/tripleoclient/v2/overcloud_ceph.py @@ -79,11 +79,13 @@ class OvercloudCephDeploy(command.Command): def get_parser(self, prog_name): parser = super(OvercloudCephDeploy, self).get_parser(prog_name) - parser.add_argument('baremetal_env', + parser.add_argument('baremetal_env', nargs='?', metavar='', help=_('Path to the environment file ' 'output from "openstack ' - 'overcloud node provision".')) + 'overcloud node provision". ' + 'This argument may be excluded ' + 'only if --ceph-spec is used.')) parser.add_argument('-o', '--output', required=True, metavar='', help=_('The path to the output environment ' @@ -176,6 +178,14 @@ class OvercloudCephDeploy(command.Command): "'ceph --cluster foo health' unless export " "CEPH_ARGS='--cluster foo' is used."), default='ceph') + parser.add_argument('--mon-ip', + help=_( + "IP address of the first Ceph monitor. " + "If not set, an IP from the Ceph " + "public_network of a server with the " + "mon label from the Ceph spec is used. " + "IP must already be active on server."), + default='') parser.add_argument('--config', help=_( "Path to an existing ceph.conf with settings " @@ -247,10 +257,12 @@ class OvercloudCephDeploy(command.Command): spec_group = parser.add_mutually_exclusive_group() spec_group.add_argument('--ceph-spec', help=_( - "Path to an existing Ceph spec file. " - "If not provided a spec will be generated " + "Path to an existing Ceph spec file. If " + "not provided a spec will be generated " "automatically based on --roles-data and " - ""), + ". The " + " parameter is " + "optional only if --ceph-spec is used."), default=None) spec_group.add_argument('--osd-spec', help=_( @@ -270,6 +282,11 @@ class OvercloudCephDeploy(command.Command): "Path to an existing crush hierarchy spec " "file. "), default=None) + parser.add_argument('--standalone', default=False, + action='store_true', + help=_("Use single host Ansible inventory. " + "Used only for development or testing " + "environments.")) parser.add_argument('--container-image-prepare', help=_( "Path to an alternative " @@ -327,14 +344,7 @@ class OvercloudCephDeploy(command.Command): def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) - baremetal_env_path = os.path.abspath(parsed_args.baremetal_env) output_path = os.path.abspath(parsed_args.output) - - if not os.path.exists(baremetal_env_path): - raise oscexc.CommandError( - "Baremetal environment file does not exist:" - " %s" % parsed_args.baremetal_env) - overwrite = parsed_args.yes if (os.path.exists(output_path) and not overwrite and not oooutils.prompt_user_for_confirmation( @@ -354,8 +364,11 @@ class OvercloudCephDeploy(command.Command): working_dir = os.path.abspath(parsed_args.working_dir) oooutils.makedirs(working_dir) - inventory = os.path.join(working_dir, - constants.TRIPLEO_STATIC_INVENTORY) + if parsed_args.standalone: + inventory = oooutils.standalone_ceph_inventory(working_dir) + else: + inventory = os.path.join(working_dir, + constants.TRIPLEO_STATIC_INVENTORY) if not os.path.exists(inventory): raise oscexc.CommandError( "Inventory file not found in working directory: " @@ -365,13 +378,29 @@ class OvercloudCephDeploy(command.Command): # mandatory extra_vars are now set, add others conditionally extra_vars = { - "baremetal_deployed_path": baremetal_env_path, "deployed_ceph_tht_path": output_path, "working_dir": working_dir, "stack_name": parsed_args.stack, + "tripleo_cephadm_standalone": parsed_args.standalone } extra_vars_file = None # optional paths to pass to playbook + if parsed_args.ceph_spec is None and \ + parsed_args.baremetal_env is None: + raise oscexc.CommandError( + "Either " + "or --ceph-spec must be used.") + + if parsed_args.baremetal_env: + baremetal_env_path = os.path.abspath(parsed_args.baremetal_env) + if not os.path.exists(baremetal_env_path): + raise oscexc.CommandError( + "Baremetal environment file does not exist:" + " %s" % parsed_args.baremetal_env) + else: + extra_vars['baremetal_deployed_path'] = \ + os.path.abspath(parsed_args.baremetal_env) + if parsed_args.roles_data: if not os.path.exists(parsed_args.roles_data): raise oscexc.CommandError( @@ -406,6 +435,15 @@ class OvercloudCephDeploy(command.Command): extra_vars['tripleo_cephadm_cluster'] = \ parsed_args.cluster + if parsed_args.mon_ip: + if not oooutils.is_valid_ip(parsed_args.mon_ip): + raise oscexc.CommandError( + "Invalid IP address '%s' passed to --mon-ip." + % parsed_args.mon_ip) + else: + extra_vars['tripleo_cephadm_first_mon_ip'] = \ + parsed_args.mon_ip + if parsed_args.ceph_spec: if not os.path.exists(parsed_args.ceph_spec): raise oscexc.CommandError( @@ -658,6 +696,11 @@ class OvercloudCephUserDisable(command.Command): metavar='', required=True, help=_("The FSID of the Ceph cluster to be " "disabled. Required for disable option.")) + parser.add_argument('--standalone', default=False, + action='store_true', + help=_("Use single host Ansible inventory. " + "Used only for development or testing " + "environments.")) return parser @@ -693,8 +736,11 @@ class OvercloudCephUserDisable(command.Command): working_dir = os.path.abspath(parsed_args.working_dir) oooutils.makedirs(working_dir) - inventory = os.path.join(working_dir, - constants.TRIPLEO_STATIC_INVENTORY) + if parsed_args.standalone: + inventory = oooutils.standalone_ceph_inventory(working_dir) + else: + inventory = os.path.join(working_dir, + constants.TRIPLEO_STATIC_INVENTORY) if not os.path.exists(inventory): raise oscexc.CommandError( "Inventory file not found in working directory: " @@ -777,6 +823,11 @@ class OvercloudCephUserEnable(command.Command): "so that cephadm will be re-enabled " "for the Ceph cluster idenified " "by the FSID.")) + parser.add_argument('--standalone', default=False, + action='store_true', + help=_("Use single host Ansible inventory. " + "Used only for development or testing " + "environments.")) parser = arg_parse_common(parser) return parser @@ -807,8 +858,11 @@ class OvercloudCephUserEnable(command.Command): working_dir = os.path.abspath(parsed_args.working_dir) oooutils.makedirs(working_dir) - inventory = os.path.join(working_dir, - constants.TRIPLEO_STATIC_INVENTORY) + if parsed_args.standalone: + inventory = oooutils.standalone_ceph_inventory(working_dir) + else: + inventory = os.path.join(working_dir, + constants.TRIPLEO_STATIC_INVENTORY) if not os.path.exists(inventory): raise oscexc.CommandError( "Inventory file not found in working directory: " @@ -860,3 +914,196 @@ class OvercloudCephUserEnable(command.Command): limit_hosts=ceph_hosts['_admin'][0], reproduce_command=False, ) + + +class OvercloudCephSpec(command.Command): + + log = logging.getLogger(__name__ + ".OvercloudCephSpec") + auth_required = False + + def get_parser(self, prog_name): + parser = super(OvercloudCephSpec, self).get_parser(prog_name) + + parser.add_argument('baremetal_env', nargs='?', + metavar='', + help=_('Path to the environment file ' + 'output from "openstack ' + 'overcloud node provision". ' + 'This argument may be excluded ' + 'only if --standalone is used.')) + parser.add_argument('-o', '--output', required=True, + metavar='', + help=_('The path to the output cephadm spec ' + 'file to pass to the "openstack ' + 'overcloud ceph deploy --ceph-spec ' + '" command.')) + parser.add_argument('-y', '--yes', default=False, action='store_true', + help=_('Skip yes/no prompt before overwriting an ' + 'existing output file ' + '(assume yes).')) + parser.add_argument('--stack', dest='stack', + help=_('Name or ID of heat stack ' + '(default=Env: OVERCLOUD_STACK_NAME)'), + default=utils.env('OVERCLOUD_STACK_NAME', + default='overcloud')) + parser.add_argument( + '--working-dir', action='store', + help=_('The working directory for the deployment where all ' + 'input, output, and generated files will be stored.\n' + 'Defaults to "$HOME/overcloud-deploy/"')) + parser.add_argument('--roles-data', + help=_( + "Path to an alternative roles_data.yaml. " + "Used to decide which node gets which " + "Ceph mon, mgr, or osd service " + "based on the node's role in " + "."), + default=os.path.join( + constants.TRIPLEO_HEAT_TEMPLATES, + constants.OVERCLOUD_ROLES_FILE)) + parser.add_argument('--mon-ip', + help=_( + "IP address of the first Ceph monitor. " + "Only available with --standalone."), + default='') + parser.add_argument('--standalone', default=False, + action='store_true', + help=_("Create a spec file for a standalone " + "deployment. Used for single server " + "development or testing environments.")) + spec_group = parser.add_mutually_exclusive_group() + spec_group.add_argument('--osd-spec', + help=_( + "Path to an existing OSD spec file. " + "When the Ceph spec file is generated " + "its OSD spec defaults to " + "{data_devices: {all: true}} " + "for all service_type osd. " + "Use --osd-spec to override the " + "data_devices value inside the " + "Ceph spec file."), + default=None) + spec_group.add_argument('--crush-hierarchy', + help=_( + "Path to an existing crush hierarchy spec " + "file. "), + default=None) + + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + output_path = os.path.abspath(parsed_args.output) + overwrite = parsed_args.yes + if (os.path.exists(output_path) and not overwrite + and not oooutils.prompt_user_for_confirmation( + 'Overwrite existing file %s [y/N]?' % parsed_args.output, + self.log)): + raise oscexc.CommandError("Will not overwrite existing file:" + " %s. See the --yes parameter to " + "override this behavior. " % + parsed_args.output) + else: + overwrite = True + + if not parsed_args.working_dir: + working_dir = oooutils.get_default_working_dir( + parsed_args.stack) + else: + working_dir = os.path.abspath(parsed_args.working_dir) + oooutils.makedirs(working_dir) + + if parsed_args.standalone: + inventory = oooutils.standalone_ceph_inventory(working_dir) + else: + inventory = os.path.join(working_dir, + constants.TRIPLEO_STATIC_INVENTORY) + if not os.path.exists(inventory): + raise oscexc.CommandError( + "Inventory file not found in working directory: " + "%s. It should have been created by " + "'openstack overcloud node provision'." + % inventory) + + # mandatory extra_vars are now set, add others conditionally + extra_vars = { + 'ceph_spec_path': output_path, + } + + # optional paths to pass to playbook + if parsed_args.standalone is None and \ + parsed_args.baremetal_env is None: + raise oscexc.CommandError( + "Either " + "or --standalone must be used.") + + if parsed_args.baremetal_env: + baremetal_env_path = os.path.abspath(parsed_args.baremetal_env) + if not os.path.exists(baremetal_env_path): + raise oscexc.CommandError( + "Baremetal environment file does not exist:" + " %s" % parsed_args.baremetal_env) + else: + extra_vars['baremetal_deployed_path'] = \ + os.path.abspath(parsed_args.baremetal_env) + + if parsed_args.roles_data: + if not os.path.exists(parsed_args.roles_data): + raise oscexc.CommandError( + "Roles Data file not found --roles-data %s." + % os.path.abspath(parsed_args.roles_data)) + else: + extra_vars['tripleo_roles_path'] = \ + os.path.abspath(parsed_args.roles_data) + + if parsed_args.mon_ip: + if not oooutils.is_valid_ip(parsed_args.mon_ip): + raise oscexc.CommandError( + "Invalid IP address '%s' passed to --mon-ip." + % parsed_args.mon_ip) + else: + if parsed_args.standalone: + extra_vars['tripleo_cephadm_first_mon_ip'] = \ + parsed_args.mon_ip + else: + raise oscexc.CommandError( + "Option --mon-ip may only be " + "used with --standalone") + + if parsed_args.osd_spec: + if not os.path.exists(parsed_args.osd_spec): + raise oscexc.CommandError( + "OSD Spec file not found --osd-spec %s." + % os.path.abspath(parsed_args.osd_spec)) + else: + extra_vars['osd_spec_path'] = \ + os.path.abspath(parsed_args.osd_spec) + + if parsed_args.crush_hierarchy: + if not os.path.exists(parsed_args.crush_hierarchy): + raise oscexc.CommandError( + "Crush Hierarchy Spec file not found --crush-hierarchy %s." + % os.path.abspath(parsed_args.crush_hierarchy)) + else: + extra_vars['crush_hierarchy_path'] = \ + os.path.abspath(parsed_args.crush_hierarchy) + + if parsed_args.standalone: + spec_playbook = 'cli-standalone-ceph-spec.yaml' + tags = '' + else: + spec_playbook = 'cli-deployed-ceph.yaml' + tags = 'ceph_spec' + + with oooutils.TempDirs() as tmp: + oooutils.run_ansible_playbook( + playbook=spec_playbook, + inventory=inventory, + workdir=tmp, + playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS, + verbosity=oooutils.playbook_verbosity(self=self), + extra_vars=extra_vars, + reproduce_command=False, + tags=tags, + )