Files
python-tripleoclient/tripleoclient/v2/overcloud_ceph.py
John Fulton a5d144ff3c Introduce "openstack overcloud ceph deploy"
The new "openstack overcloud ceph deploy" is used to deploy
Ceph after the hardware has been provisioned with networking
and before the overcloud is deployed. The command takes the
output of "openstack overcloud node provision" as input and
returns a Heat enviornment file, e.g. deployed_ceph.yaml,
as output. The deployed_ceph.yaml file may then be passed
to the "openstack overcloud deploy" command as input.

Change-Id: Ie0032190f0c07fd47a36a1915c02f0ba1a9ae2a4
(cherry picked from commit 61e4f4c788)
2021-08-25 13:26:02 -04:00

273 lines
13 KiB
Python

# Copyright 2021 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import logging
import os
from osc_lib import exceptions as oscexc
from osc_lib.i18n import _
from osc_lib import utils
from tripleoclient import command
from tripleoclient import constants
from tripleoclient import utils as oooutils
class OvercloudCephDeploy(command.Command):
log = logging.getLogger(__name__ + ".OvercloudCephDeploy")
def get_parser(self, prog_name):
parser = super(OvercloudCephDeploy, self).get_parser(prog_name)
parser.add_argument('baremetal_env',
metavar='<deployed_baremetal.yaml>',
help=_('Path to the environment file '
'output from "openstack '
'overcloud node provision".'))
parser.add_argument('-o', '--output', required=True,
metavar='<deployed_ceph.yaml>',
help=_('The path to the output environment '
'file describing the Ceph deployment '
' to pass to the overcloud deployment.'))
parser.add_argument('-y', '--yes', default=False, action='store_true',
help=_('Skip yes/no prompt before overwriting an '
'existing <deployed_ceph.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))
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 "
"automatically based on --roles-data and "
"<deployed_baremetal.yaml>"),
default=None)
spec_group.add_argument('--osd-spec',
help=_(
"Path to an existing OSD spec file. "
"Mutually exclusive with --ceph-spec. "
"If the Ceph spec file is generated "
"automatically, then the OSD spec "
"in the Ceph spec file 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)
parser.add_argument('--container-image-prepare',
help=_(
"Path to an alternative "
"container_image_prepare_defaults.yaml. "
"Used to control which Ceph container is "
"pulled by cephadm via the ceph_namespace, "
"ceph_image, and ceph_tag variables in "
"addition to registry authentication via "
"ContainerImageRegistryCredentials."
),
default=None)
container_group = parser.add_argument_group("container-image-prepare "
"overrides",
"The following options "
"may be used to override "
"individual values "
"set via "
"--container-image-prepare"
". If the example "
"variables below were "
"set the image would be "
"concatenated into "
"quay.io/ceph/ceph:latest "
"and a custom registry "
"login would be used."
)
container_group.add_argument('--container-namespace',
required=False,
help='e.g. quay.io/ceph')
container_group.add_argument('--container-image',
required=False,
help='e.g. ceph')
container_group.add_argument('--container-tag',
required=False,
help='e.g. latest')
container_group.add_argument('--registry-url',
required=False,
help='')
container_group.add_argument('--registry-username',
required=False,
help='')
container_group.add_argument('--registry-password',
required=False,
help='')
return parser
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(
'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)
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 = {
"baremetal_deployed_path": baremetal_env_path,
"deployed_ceph_tht_path": output_path,
"working_dir": working_dir,
"stack_name": parsed_args.stack,
}
# optional paths to pass to playbook
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.ceph_spec:
if not os.path.exists(parsed_args.ceph_spec):
raise oscexc.CommandError(
"Ceph Spec file not found --ceph-spec %s."
% os.path.abspath(parsed_args.ceph_spec))
else:
extra_vars['dynamic_ceph_spec'] = False
extra_vars['ceph_spec_path'] = \
os.path.abspath(parsed_args.ceph_spec)
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)
# optional container vars to pass to playbook
keys = ['ceph_namespace', 'ceph_image', 'ceph_tag']
key = 'ContainerImagePrepare'
container_dict = \
oooutils.parse_container_image_prepare(key, keys,
parsed_args.
container_image_prepare)
extra_vars['tripleo_cephadm_container_ns'] = \
parsed_args.container_namespace or \
container_dict['ceph_namespace']
extra_vars['tripleo_cephadm_container_image'] = \
parsed_args.container_image or \
container_dict['ceph_image']
extra_vars['tripleo_cephadm_container_tag'] = \
parsed_args.container_tag or \
container_dict['ceph_tag']
# optional container registry vars to pass to playbook
if 'tripleo_cephadm_container_ns' in extra_vars:
keys = [extra_vars['tripleo_cephadm_container_ns']]
key = 'ContainerImageRegistryCredentials'
registry_dict = \
oooutils.parse_container_image_prepare(key, keys,
parsed_args.
container_image_prepare)
# It's valid for the registry_dict to be empty so
# we cannot default to it with an 'or' like we can
# for ceph_{namespace,image,tag} as above.
if 'registry_url' in registry_dict:
extra_vars['tripleo_cephadm_registry_url'] = \
registry_dict['registry_url']
if 'registry_password' in registry_dict:
extra_vars['tripleo_cephadm_registry_password'] = \
registry_dict['registry_password']
if 'registry_username' in registry_dict:
extra_vars['tripleo_cephadm_registry_username'] = \
registry_dict['registry_username']
# Whether registry vars came out of --container-image-prepare
# or not, we need either to set them (as above) or override
# them if they were passed via the CLI (as follows)
if parsed_args.registry_url:
extra_vars['tripleo_cephadm_registry_url'] = \
parsed_args.registry_url
if parsed_args.registry_password:
extra_vars['tripleo_cephadm_registry_password'] = \
parsed_args.registry_password
if parsed_args.registry_username:
extra_vars['tripleo_cephadm_registry_username'] = \
parsed_args.registry_username
# call the playbook
with oooutils.TempDirs() as tmp:
oooutils.run_ansible_playbook(
playbook='cli-deployed-ceph.yaml',
inventory=inventory,
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
verbosity=oooutils.playbook_verbosity(self=self),
extra_vars=extra_vars,
)