Merge "Squash standalone deployed_ceph for pythonclient to Wallaby" into stable/wallaby
This commit is contained in:
commit
0240f5360c
14
releasenotes/notes/overcloud_ceph_spec-e1cfd358c4db2b22.yaml
Normal file
14
releasenotes/notes/overcloud_ceph_spec-e1cfd358c4db2b22.yaml
Normal file
@ -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.
|
@ -43,6 +43,7 @@ openstack.tripleoclient.v2 =
|
|||||||
overcloud_netenv_validate = tripleoclient.v1.overcloud_netenv_validate:ValidateOvercloudNetenv
|
overcloud_netenv_validate = tripleoclient.v1.overcloud_netenv_validate:ValidateOvercloudNetenv
|
||||||
overcloud_cell_export = tripleoclient.v1.overcloud_cell:ExportCell
|
overcloud_cell_export = tripleoclient.v1.overcloud_cell:ExportCell
|
||||||
overcloud_ceph_deploy = tripleoclient.v2.overcloud_ceph:OvercloudCephDeploy
|
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_disable = tripleoclient.v2.overcloud_ceph:OvercloudCephUserDisable
|
||||||
overcloud_ceph_user_enable = tripleoclient.v2.overcloud_ceph:OvercloudCephUserEnable
|
overcloud_ceph_user_enable = tripleoclient.v2.overcloud_ceph:OvercloudCephUserEnable
|
||||||
overcloud_container_image_upload = tripleoclient.v1.container_image:UploadImage
|
overcloud_container_image_upload = tripleoclient.v1.container_image:UploadImage
|
||||||
|
@ -45,6 +45,7 @@ class TestOvercloudCephDeploy(fakes.FakePlaybookExecution):
|
|||||||
'--stack', 'overcloud',
|
'--stack', 'overcloud',
|
||||||
'--skip-user-create',
|
'--skip-user-create',
|
||||||
'--skip-hosts-config',
|
'--skip-hosts-config',
|
||||||
|
'--mon-ip', '127.0.0.1',
|
||||||
'--cephadm-ssh-user', 'jimmy',
|
'--cephadm-ssh-user', 'jimmy',
|
||||||
'--output', 'deployed-ceph.yaml',
|
'--output', 'deployed-ceph.yaml',
|
||||||
'--container-namespace', 'quay.io/ceph',
|
'--container-namespace', 'quay.io/ceph',
|
||||||
@ -66,8 +67,10 @@ class TestOvercloudCephDeploy(fakes.FakePlaybookExecution):
|
|||||||
"deployed_ceph_tht_path": mock.ANY,
|
"deployed_ceph_tht_path": mock.ANY,
|
||||||
"working_dir": mock.ANY,
|
"working_dir": mock.ANY,
|
||||||
"stack_name": 'overcloud',
|
"stack_name": 'overcloud',
|
||||||
|
"tripleo_cephadm_standalone": False,
|
||||||
'tripleo_cephadm_ssh_user': 'jimmy',
|
'tripleo_cephadm_ssh_user': 'jimmy',
|
||||||
'tripleo_cephadm_cluster': 'ceph',
|
'tripleo_cephadm_cluster': 'ceph',
|
||||||
|
'tripleo_cephadm_first_mon_ip': '127.0.0.1',
|
||||||
'tripleo_roles_path': mock.ANY,
|
'tripleo_roles_path': mock.ANY,
|
||||||
'tripleo_cephadm_container_ns': 'quay.io/ceph',
|
'tripleo_cephadm_container_ns': 'quay.io/ceph',
|
||||||
'tripleo_cephadm_container_image': '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.abspath', autospect=True)
|
||||||
@mock.patch('os.path.exists', autospect=True)
|
@mock.patch('os.path.exists', autospect=True)
|
||||||
def test_overcloud_deploy_ceph_no_overwrite(self, mock_abspath,
|
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.assertRaises(osc_lib_exc.CommandError,
|
||||||
self.cmd.take_action, parsed_args)
|
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):
|
class TestOvercloudCephUserDisable(fakes.FakePlaybookExecution):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -265,3 +325,45 @@ class TestOvercloudCephUserEnable(fakes.FakePlaybookExecution):
|
|||||||
"tripleo_cephadm_action": 'enable'
|
"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,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -3226,6 +3226,29 @@ def get_host_groups_from_ceph_spec(ceph_spec_path, prefix='',
|
|||||||
return hosts
|
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):
|
def cleanup_host_entry(entry):
|
||||||
# remove any tab or space excess
|
# remove any tab or space excess
|
||||||
entry_stripped = re.sub('[ \t]+', ' ', str(entry).rstrip())
|
entry_stripped = re.sub('[ \t]+', ' ', str(entry).rstrip())
|
||||||
|
@ -79,11 +79,13 @@ class OvercloudCephDeploy(command.Command):
|
|||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
parser = super(OvercloudCephDeploy, self).get_parser(prog_name)
|
parser = super(OvercloudCephDeploy, self).get_parser(prog_name)
|
||||||
|
|
||||||
parser.add_argument('baremetal_env',
|
parser.add_argument('baremetal_env', nargs='?',
|
||||||
metavar='<deployed_baremetal.yaml>',
|
metavar='<deployed_baremetal.yaml>',
|
||||||
help=_('Path to the environment file '
|
help=_('Path to the environment file '
|
||||||
'output from "openstack '
|
'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,
|
parser.add_argument('-o', '--output', required=True,
|
||||||
metavar='<deployed_ceph.yaml>',
|
metavar='<deployed_ceph.yaml>',
|
||||||
help=_('The path to the output environment '
|
help=_('The path to the output environment '
|
||||||
@ -176,6 +178,14 @@ class OvercloudCephDeploy(command.Command):
|
|||||||
"'ceph --cluster foo health' unless export "
|
"'ceph --cluster foo health' unless export "
|
||||||
"CEPH_ARGS='--cluster foo' is used."),
|
"CEPH_ARGS='--cluster foo' is used."),
|
||||||
default='ceph')
|
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',
|
parser.add_argument('--config',
|
||||||
help=_(
|
help=_(
|
||||||
"Path to an existing ceph.conf with settings "
|
"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 = parser.add_mutually_exclusive_group()
|
||||||
spec_group.add_argument('--ceph-spec',
|
spec_group.add_argument('--ceph-spec',
|
||||||
help=_(
|
help=_(
|
||||||
"Path to an existing Ceph spec file. "
|
"Path to an existing Ceph spec file. If "
|
||||||
"If not provided a spec will be generated "
|
"not provided a spec will be generated "
|
||||||
"automatically based on --roles-data and "
|
"automatically based on --roles-data and "
|
||||||
"<deployed_baremetal.yaml>"),
|
"<deployed_baremetal.yaml>. The "
|
||||||
|
"<deployed_baremetal.yaml> parameter is "
|
||||||
|
"optional only if --ceph-spec is used."),
|
||||||
default=None)
|
default=None)
|
||||||
spec_group.add_argument('--osd-spec',
|
spec_group.add_argument('--osd-spec',
|
||||||
help=_(
|
help=_(
|
||||||
@ -270,6 +282,11 @@ class OvercloudCephDeploy(command.Command):
|
|||||||
"Path to an existing crush hierarchy spec "
|
"Path to an existing crush hierarchy spec "
|
||||||
"file. "),
|
"file. "),
|
||||||
default=None)
|
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',
|
parser.add_argument('--container-image-prepare',
|
||||||
help=_(
|
help=_(
|
||||||
"Path to an alternative "
|
"Path to an alternative "
|
||||||
@ -327,14 +344,7 @@ class OvercloudCephDeploy(command.Command):
|
|||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
self.log.debug("take_action(%s)" % 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)
|
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
|
overwrite = parsed_args.yes
|
||||||
if (os.path.exists(output_path) and not overwrite
|
if (os.path.exists(output_path) and not overwrite
|
||||||
and not oooutils.prompt_user_for_confirmation(
|
and not oooutils.prompt_user_for_confirmation(
|
||||||
@ -354,8 +364,11 @@ class OvercloudCephDeploy(command.Command):
|
|||||||
working_dir = os.path.abspath(parsed_args.working_dir)
|
working_dir = os.path.abspath(parsed_args.working_dir)
|
||||||
oooutils.makedirs(working_dir)
|
oooutils.makedirs(working_dir)
|
||||||
|
|
||||||
inventory = os.path.join(working_dir,
|
if parsed_args.standalone:
|
||||||
constants.TRIPLEO_STATIC_INVENTORY)
|
inventory = oooutils.standalone_ceph_inventory(working_dir)
|
||||||
|
else:
|
||||||
|
inventory = os.path.join(working_dir,
|
||||||
|
constants.TRIPLEO_STATIC_INVENTORY)
|
||||||
if not os.path.exists(inventory):
|
if not os.path.exists(inventory):
|
||||||
raise oscexc.CommandError(
|
raise oscexc.CommandError(
|
||||||
"Inventory file not found in working directory: "
|
"Inventory file not found in working directory: "
|
||||||
@ -365,13 +378,29 @@ class OvercloudCephDeploy(command.Command):
|
|||||||
|
|
||||||
# mandatory extra_vars are now set, add others conditionally
|
# mandatory extra_vars are now set, add others conditionally
|
||||||
extra_vars = {
|
extra_vars = {
|
||||||
"baremetal_deployed_path": baremetal_env_path,
|
|
||||||
"deployed_ceph_tht_path": output_path,
|
"deployed_ceph_tht_path": output_path,
|
||||||
"working_dir": working_dir,
|
"working_dir": working_dir,
|
||||||
"stack_name": parsed_args.stack,
|
"stack_name": parsed_args.stack,
|
||||||
|
"tripleo_cephadm_standalone": parsed_args.standalone
|
||||||
}
|
}
|
||||||
extra_vars_file = None
|
extra_vars_file = None
|
||||||
# optional paths to pass to playbook
|
# optional paths to pass to playbook
|
||||||
|
if parsed_args.ceph_spec is None and \
|
||||||
|
parsed_args.baremetal_env is None:
|
||||||
|
raise oscexc.CommandError(
|
||||||
|
"Either <deployed_baremetal.yaml> "
|
||||||
|
"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 parsed_args.roles_data:
|
||||||
if not os.path.exists(parsed_args.roles_data):
|
if not os.path.exists(parsed_args.roles_data):
|
||||||
raise oscexc.CommandError(
|
raise oscexc.CommandError(
|
||||||
@ -406,6 +435,15 @@ class OvercloudCephDeploy(command.Command):
|
|||||||
extra_vars['tripleo_cephadm_cluster'] = \
|
extra_vars['tripleo_cephadm_cluster'] = \
|
||||||
parsed_args.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 parsed_args.ceph_spec:
|
||||||
if not os.path.exists(parsed_args.ceph_spec):
|
if not os.path.exists(parsed_args.ceph_spec):
|
||||||
raise oscexc.CommandError(
|
raise oscexc.CommandError(
|
||||||
@ -658,6 +696,11 @@ class OvercloudCephUserDisable(command.Command):
|
|||||||
metavar='<FSID>', required=True,
|
metavar='<FSID>', required=True,
|
||||||
help=_("The FSID of the Ceph cluster to be "
|
help=_("The FSID of the Ceph cluster to be "
|
||||||
"disabled. Required for disable option."))
|
"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
|
return parser
|
||||||
|
|
||||||
@ -693,8 +736,11 @@ class OvercloudCephUserDisable(command.Command):
|
|||||||
working_dir = os.path.abspath(parsed_args.working_dir)
|
working_dir = os.path.abspath(parsed_args.working_dir)
|
||||||
oooutils.makedirs(working_dir)
|
oooutils.makedirs(working_dir)
|
||||||
|
|
||||||
inventory = os.path.join(working_dir,
|
if parsed_args.standalone:
|
||||||
constants.TRIPLEO_STATIC_INVENTORY)
|
inventory = oooutils.standalone_ceph_inventory(working_dir)
|
||||||
|
else:
|
||||||
|
inventory = os.path.join(working_dir,
|
||||||
|
constants.TRIPLEO_STATIC_INVENTORY)
|
||||||
if not os.path.exists(inventory):
|
if not os.path.exists(inventory):
|
||||||
raise oscexc.CommandError(
|
raise oscexc.CommandError(
|
||||||
"Inventory file not found in working directory: "
|
"Inventory file not found in working directory: "
|
||||||
@ -777,6 +823,11 @@ class OvercloudCephUserEnable(command.Command):
|
|||||||
"so that cephadm will be re-enabled "
|
"so that cephadm will be re-enabled "
|
||||||
"for the Ceph cluster idenified "
|
"for the Ceph cluster idenified "
|
||||||
"by the FSID."))
|
"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)
|
parser = arg_parse_common(parser)
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
@ -807,8 +858,11 @@ class OvercloudCephUserEnable(command.Command):
|
|||||||
working_dir = os.path.abspath(parsed_args.working_dir)
|
working_dir = os.path.abspath(parsed_args.working_dir)
|
||||||
oooutils.makedirs(working_dir)
|
oooutils.makedirs(working_dir)
|
||||||
|
|
||||||
inventory = os.path.join(working_dir,
|
if parsed_args.standalone:
|
||||||
constants.TRIPLEO_STATIC_INVENTORY)
|
inventory = oooutils.standalone_ceph_inventory(working_dir)
|
||||||
|
else:
|
||||||
|
inventory = os.path.join(working_dir,
|
||||||
|
constants.TRIPLEO_STATIC_INVENTORY)
|
||||||
if not os.path.exists(inventory):
|
if not os.path.exists(inventory):
|
||||||
raise oscexc.CommandError(
|
raise oscexc.CommandError(
|
||||||
"Inventory file not found in working directory: "
|
"Inventory file not found in working directory: "
|
||||||
@ -860,3 +914,196 @@ class OvercloudCephUserEnable(command.Command):
|
|||||||
limit_hosts=ceph_hosts['_admin'][0],
|
limit_hosts=ceph_hosts['_admin'][0],
|
||||||
reproduce_command=False,
|
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='<deployed_baremetal.yaml>',
|
||||||
|
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='<ceph_spec.yaml>',
|
||||||
|
help=_('The path to the output cephadm spec '
|
||||||
|
'file to pass to the "openstack '
|
||||||
|
'overcloud ceph deploy --ceph-spec '
|
||||||
|
'<ceph_spec.yaml>" command.'))
|
||||||
|
parser.add_argument('-y', '--yes', default=False, action='store_true',
|
||||||
|
help=_('Skip yes/no prompt before overwriting an '
|
||||||
|
'existing <ceph_spec.yaml> 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/<stack>"'))
|
||||||
|
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 "
|
||||||
|
"<deployed_baremetal.yaml>."),
|
||||||
|
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 <deployed_baremetal.yaml> "
|
||||||
|
"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,
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user