571 lines
28 KiB
Python
571 lines
28 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))
|
|
parser.add_argument('--network-data',
|
|
help=_(
|
|
"Path to an alternative network_data.yaml. "
|
|
"Used to define Ceph public_network and "
|
|
"cluster_network. This file is searched "
|
|
"for networks with name_lower values of "
|
|
"storage and storage_mgmt. If none found, "
|
|
"then search repeats but with "
|
|
"service_net_map_replace in place of "
|
|
"name_lower. Use --public-network-name or "
|
|
"--cluster-network-name options to override "
|
|
"name of the searched for network from "
|
|
"storage or storage_mgmt to a customized "
|
|
"name. If network_data has no storage "
|
|
"networks, both default to ctlplane. "
|
|
"If found network has >1 subnet, they are "
|
|
"all combined (for routed traffic). "
|
|
"If a network has ipv6 true, then "
|
|
"the ipv6_subnet is retrieved instead "
|
|
"of the ip_subnet, and the Ceph global "
|
|
"ms_bind_ipv4 is set false and the "
|
|
"ms_bind_ipv6 is set true. Use --config "
|
|
"to override these defaults if desired."),
|
|
default=os.path.join(
|
|
constants.TRIPLEO_HEAT_TEMPLATES,
|
|
constants.OVERCLOUD_NETWORKS_FILE))
|
|
parser.add_argument('--public-network-name',
|
|
help=_(
|
|
"Name of the network defined in "
|
|
"network_data.yaml which should be "
|
|
"used for the Ceph public_network. "
|
|
"Defaults to 'storage'."),
|
|
default='storage')
|
|
parser.add_argument('--cluster-network-name',
|
|
help=_(
|
|
"Name of the network defined in "
|
|
"network_data.yaml which should be "
|
|
"used for the Ceph cluster_network. "
|
|
"Defaults to 'storage_mgmt'."),
|
|
default='storage_mgmt')
|
|
parser.add_argument('--config',
|
|
help=_(
|
|
"Path to an existing ceph.conf with settings "
|
|
"to be assimilated by the new cluster via "
|
|
"'cephadm bootstrap --config' ")),
|
|
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)
|
|
spec_group.add_argument('--crush-hierarchy',
|
|
help=_(
|
|
"Path to an existing crush hierarchy 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.config:
|
|
if not os.path.exists(parsed_args.config):
|
|
raise oscexc.CommandError(
|
|
"Config file not found --config %s."
|
|
% os.path.abspath(parsed_args.config))
|
|
else:
|
|
extra_vars['tripleo_cephadm_bootstrap_conf'] = \
|
|
os.path.abspath(parsed_args.config)
|
|
|
|
if parsed_args.network_data:
|
|
if not os.path.exists(parsed_args.network_data):
|
|
raise oscexc.CommandError(
|
|
"Network Data file not found --network-data %s."
|
|
% os.path.abspath(parsed_args.network_data))
|
|
|
|
ceph_networks_map = \
|
|
oooutils.get_ceph_networks(parsed_args.network_data,
|
|
parsed_args.public_network_name,
|
|
parsed_args.cluster_network_name)
|
|
extra_vars = {**extra_vars, **ceph_networks_map}
|
|
|
|
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)
|
|
|
|
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)
|
|
# 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,
|
|
)
|
|
|
|
|
|
class OvercloudCephUserDisable(command.Command):
|
|
|
|
log = logging.getLogger(__name__ + ".OvercloudCephUserDisable")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(OvercloudCephUserDisable, self).get_parser(prog_name)
|
|
parser.add_argument('ceph_spec',
|
|
metavar='<ceph_spec.yaml>',
|
|
help=_(
|
|
"Path to an existing Ceph spec file "
|
|
"which describes the Ceph cluster "
|
|
"where the cephadm SSH user will have "
|
|
"their public and private keys removed "
|
|
"and cephadm will be disabled. "
|
|
"Spec file is necessary to determine "
|
|
"which nodes to modify. "
|
|
"WARNING: Ceph cluster administration or "
|
|
"modification will no longer function."))
|
|
parser.add_argument('-y', '--yes', default=False, action='store_true',
|
|
help=_('Skip yes/no prompt before disabling '
|
|
'cephadm and its SSH user. '
|
|
'(assume yes).'))
|
|
parser.add_argument('--ssh-user', dest='ssh_user',
|
|
help=_('Name of the SSH user used by cephadm. '
|
|
'Defaults to "ceph-admin". '
|
|
'(default=Env: CEPHADM_SSH_USER)'),
|
|
default=utils.env('CEPHADM_SSH_USER',
|
|
default='ceph-admin'))
|
|
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 from the deployment where all '
|
|
'input, output, and generated files are stored.\n'
|
|
'Defaults to "$HOME/overcloud-deploy/<stack>"'))
|
|
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
self.log.debug("take_action(%s)" % parsed_args)
|
|
|
|
ceph_spec = os.path.abspath(parsed_args.ceph_spec)
|
|
|
|
if not os.path.exists(ceph_spec):
|
|
raise oscexc.CommandError(
|
|
"Ceph spec file does not exist:"
|
|
" %s" % parsed_args.ceph_spec)
|
|
|
|
overwrite = parsed_args.yes
|
|
if (not overwrite
|
|
and not oooutils.prompt_user_for_confirmation(
|
|
'Are you sure you want to disable Ceph '
|
|
'cluster management [y/N]?',
|
|
self.log)):
|
|
raise oscexc.CommandError("Will not disable cephadm and delete "
|
|
"the cephadm SSH user :"
|
|
" %s. See the --yes parameter to "
|
|
"override this behavior. " %
|
|
parsed_args.ssh_user)
|
|
else:
|
|
overwrite = True
|
|
|
|
# use stack and working_dir to find inventory
|
|
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)
|
|
|
|
# call the playbook to toggle cephadm w/ disable
|
|
extra_vars = {
|
|
"backend": '',
|
|
"action": 'disable'
|
|
}
|
|
with oooutils.TempDirs() as tmp:
|
|
oooutils.run_ansible_playbook(
|
|
playbook='disable_cephadm.yml',
|
|
inventory=inventory,
|
|
workdir=tmp,
|
|
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
|
verbosity=oooutils.playbook_verbosity(self=self),
|
|
extra_vars=extra_vars,
|
|
)
|
|
# call the playbook to remove ssh_user_keys
|
|
extra_vars = {
|
|
"tripleo_cephadm_ssh_user": parsed_args.ssh_user
|
|
}
|
|
hosts = oooutils.get_hosts_from_ceph_spec(ceph_spec)
|
|
if len(hosts['admin']) > 0 and len(hosts['non_admin']) > 0:
|
|
with oooutils.TempDirs() as tmp:
|
|
oooutils.run_ansible_playbook(
|
|
playbook='ceph-admin-user-disable.yml',
|
|
inventory=inventory,
|
|
workdir=tmp,
|
|
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
|
verbosity=oooutils.playbook_verbosity(self=self),
|
|
extra_vars=extra_vars,
|
|
limit_hosts=",".join(hosts['admin'] + hosts['non_admin'])
|
|
)
|
|
|
|
|
|
class OvercloudCephUserReEnable(command.Command):
|
|
|
|
log = logging.getLogger(__name__ + ".OvercloudCephUserReEnable")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(OvercloudCephUserReEnable, self).get_parser(prog_name)
|
|
parser.add_argument('ceph_spec',
|
|
metavar='<ceph_spec.yaml>',
|
|
help=_(
|
|
"Path to an existing Ceph spec file "
|
|
"which describes the Ceph cluster "
|
|
"where the cephadm SSH user will have "
|
|
"their public or private keys restored "
|
|
"and cephadm will be re-enabled for "
|
|
"cluster administration. "
|
|
"Spec file is necessary to determine "
|
|
"which nodes to modify and if "
|
|
"a public or private key is required."))
|
|
parser.add_argument('--ssh-user', dest='ssh_user',
|
|
help=_('Name of the SSH user used by cephadm. '
|
|
'Defaults to "ceph-admin". '
|
|
'(default=Env: CEPHADM_SSH_USER)'),
|
|
default=utils.env('CEPHADM_SSH_USER',
|
|
default='ceph-admin'))
|
|
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 from the deployment where all '
|
|
'input, output, and generated files are stored.\n'
|
|
'Defaults to "$HOME/overcloud-deploy/<stack>"'))
|
|
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
self.log.debug("take_action(%s)" % parsed_args)
|
|
|
|
ceph_spec = os.path.abspath(parsed_args.ceph_spec)
|
|
|
|
if not os.path.exists(ceph_spec):
|
|
raise oscexc.CommandError(
|
|
"Ceph spec file does not exist:"
|
|
" %s" % parsed_args.ceph_spec)
|
|
|
|
# use stack and working_dir to find inventory
|
|
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)
|
|
|
|
# Create the ssh_user on admin and then non-admin hosts
|
|
hosts = oooutils.get_hosts_from_ceph_spec(ceph_spec)
|
|
for limit_list in [hosts['admin'], hosts['non_admin']]:
|
|
if len(limit_list) > 0:
|
|
extra_vars = {
|
|
"tripleo_admin_user": parsed_args.ssh_user,
|
|
"distribute_private_key": True
|
|
}
|
|
# need to include undercloud where RSA keys are generated
|
|
limit_list.append('undercloud')
|
|
with oooutils.TempDirs() as tmp:
|
|
oooutils.run_ansible_playbook(
|
|
playbook='ceph-admin-user-playbook.yml',
|
|
inventory=inventory,
|
|
workdir=tmp,
|
|
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
|
verbosity=oooutils.playbook_verbosity(self=self),
|
|
extra_vars=extra_vars,
|
|
limit_hosts=",".join(limit_list)
|
|
)
|
|
# admin_hosts are done now so don't distribute private key
|
|
extra_vars["distribute_private_key"] = False
|
|
|
|
# Call the playbook to toggle cephadm w/ enable
|
|
extra_vars = {
|
|
"backend": 'cephadm',
|
|
"action": 'enable'
|
|
}
|
|
# todo(fultonj): I can limit it to any mon
|
|
# but "hosts: ceph_mon[0]" in this playbook won't work
|
|
with oooutils.TempDirs() as tmp:
|
|
oooutils.run_ansible_playbook(
|
|
playbook='disable_cephadm.yml',
|
|
inventory=inventory,
|
|
workdir=tmp,
|
|
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
|
verbosity=oooutils.playbook_verbosity(self=self),
|
|
extra_vars=extra_vars,
|
|
)
|