Alex Schultz f321bb4688 Removed overcloud container commands
The following commands have been removed:

  overcloud container image upload
  overcloud container image build
  overcloud container image prepare
  overcloud container image tag discover

They were replaced by the tripleo container based commands and no longer
function.

Change-Id: Ic1f4d76c9e7bf3579b7e47d87b0ee2c86251c0d6
2021-11-17 14:58:03 -07:00

598 lines
23 KiB
Python

# Copyright 2015 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 copy
import datetime
import errno
from io import StringIO
import logging
import os
import shutil
from osc_lib import exceptions as oscexc
from osc_lib.i18n import _
from urllib import parse
import yaml
from tripleo_common.image import image_uploader
from tripleo_common.image import kolla_builder
from tripleo_common.utils.locks import processlock
from tripleoclient import utils as oooutils
from tripleoclient import command
from tripleoclient import constants
from tripleoclient import exceptions
from tripleoclient import utils
def build_env_file(params, command_options):
f = StringIO()
f.write('# Generated with the following on %s\n#\n' %
datetime.datetime.now().isoformat())
f.write('# openstack %s\n#\n\n' %
' '.join(command_options))
yaml.safe_dump({'parameter_defaults': params}, f,
default_flow_style=False)
return f.getvalue()
class TripleOContainerImagePush(command.Command):
"""Push specified image to registry."""
auth_required = False
log = logging.getLogger(__name__ + ".TripleoContainerImagePush")
def get_parser(self, prog_name):
parser = super(TripleOContainerImagePush, self).get_parser(prog_name)
parser.add_argument(
"--local",
dest="local",
default=False,
action="store_true",
help=_("Use this flag if the container image is already on the "
"current system and does not need to be pulled from a "
"remote registry.")
)
parser.add_argument(
"--registry-url",
dest="registry_url",
metavar='<registry url>',
default=None,
help=_("URL of the destination registry in the form "
"<fqdn>:<port>.")
)
parser.add_argument(
"--append-tag",
dest="append_tag",
default='',
help=_("Tag to append to the existing tag when pushing the "
"container. ")
)
parser.add_argument(
"--username",
dest="username",
metavar='<username>',
help=_("Username for the destination image registry.")
)
parser.add_argument(
"--password",
dest="password",
metavar='<password>',
help=_("Password for the destination image registry.")
)
parser.add_argument(
"--source-username",
dest="source_username",
metavar='<source_username>',
help=_("Username for the source image registry.")
)
parser.add_argument(
"--source-password",
dest="source_password",
metavar='<source_password>',
help=_("Password for the source image registry.")
)
parser.add_argument(
"--dry-run",
dest="dry_run",
action="store_true",
help=_("Perform a dry run upload. The upload action is not "
"performed, but the authentication process is attempted.")
)
parser.add_argument(
"--multi-arch",
dest="multi_arch",
action="store_true",
help=_("Enable multi arch support for the upload.")
)
parser.add_argument(
"--cleanup",
dest="cleanup",
action="store_true",
default=False,
help=_("Remove local copy of the image after uploading")
)
parser.add_argument(
dest="image_to_push",
metavar='<image to push>',
help=_("Container image to upload. Should be in the form of "
"<registry>/<namespace>/<name>:<tag>. If tag is "
"not provided, then latest will be used.")
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
lock = processlock.ProcessLock()
manager = image_uploader.ImageUploadManager(lock=lock)
uploader = manager.uploader('python')
source_image = parsed_args.image_to_push
if parsed_args.local or source_image.startswith('containers-storage:'):
storage = 'containers-storage:'
if not source_image.startswith(storage):
source_image = storage + source_image.replace('docker://', '')
elif not parsed_args.local:
self.log.warning('Assuming local container based on provided '
'container path. (e.g. starts with '
'containers-storage:)')
source_url = parse.urlparse(source_image)
image_name = source_url.geturl()
image_source = None
if parsed_args.source_username or parsed_args.source_password:
self.log.warning('Source credentials ignored for local images')
else:
storage = 'docker://'
if not source_image.startswith(storage):
source_image = storage + source_image
source_url = parse.urlparse(source_image)
image_source = source_url.netloc
image_name = source_url.path[1:]
if len(image_name.split('/')) != 2:
raise exceptions.DownloadError('Invalid container. Provided '
'container image should be '
'<registry>/<namespace>/<name>:'
'<tag>')
if parsed_args.source_username or parsed_args.source_password:
if not parsed_args.source_username:
self.log.warning('Skipping authentication - missing source'
' username')
elif not parsed_args.source_password:
self.log.warning('Skipping authentication - missing source'
' password')
else:
uploader.authenticate(source_url,
parsed_args.source_username,
parsed_args.source_password)
registry_url_arg = parsed_args.registry_url
if registry_url_arg is None:
registry_url_arg = image_uploader.get_undercloud_registry()
if not registry_url_arg.startswith('docker://'):
registry_url = 'docker://%s' % registry_url_arg
else:
registry_url = registry_url_arg
reg_url = parse.urlparse(registry_url)
session = uploader.authenticate(reg_url,
parsed_args.username,
parsed_args.password)
try:
if not parsed_args.dry_run:
task = image_uploader.UploadTask(
image_name=image_name,
pull_source=image_source,
push_destination=registry_url_arg,
append_tag=parsed_args.append_tag,
modify_role=None,
modify_vars=None,
cleanup=parsed_args.cleanup,
multi_arch=parsed_args.multi_arch)
uploader.add_upload_task(task)
uploader.run_tasks()
except OSError as e:
if e.errno == errno.EACCES:
self.log.error("Unable to upload due to permissions. "
"Please prefix command with sudo.")
raise oscexc.CommandError(e)
finally:
session.close()
class TripleOContainerImageDelete(command.Command):
"""Delete specified image from registry."""
auth_required = False
log = logging.getLogger(__name__ + ".TripleoContainerImageDelete")
def get_parser(self, prog_name):
parser = super(TripleOContainerImageDelete, self).get_parser(prog_name)
parser.add_argument(
"--registry-url",
dest="registry_url",
metavar='<registry url>',
default=None,
help=_("URL of registry images are to be listed from in the "
"form <fqdn>:<port>.")
)
parser.add_argument(
dest="image_to_delete",
metavar='<image to delete>',
help=_("Full URL of image to be deleted in the "
"form <fqdn>:<port>/path/to/image")
)
parser.add_argument(
"--username",
dest="username",
metavar='<username>',
help=_("Username for image registry.")
)
parser.add_argument(
"--password",
dest="password",
metavar='<password>',
help=_("Password for image registry.")
)
parser.add_argument(
'-y', '--yes',
help=_('Skip yes/no prompt (assume yes).'),
default=False,
action="store_true")
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
if not parsed_args.yes:
confirm = utils.prompt_user_for_confirmation(
message=_("Are you sure you want to delete this image "
"[y/N]? "),
logger=self.log)
if not confirm:
raise oscexc.CommandError("Action not confirmed, exiting.")
lock = processlock.ProcessLock()
manager = image_uploader.ImageUploadManager(lock=lock)
uploader = manager.uploader('python')
registry_url_arg = parsed_args.registry_url
if registry_url_arg is None:
registry_url_arg = image_uploader.get_undercloud_registry()
url = uploader._image_to_url(registry_url_arg)
session = uploader.authenticate(url, parsed_args.username,
parsed_args.password)
try:
uploader.delete(parsed_args.image_to_delete, session=session)
except OSError as e:
if e.errno == errno.EACCES:
self.log.error("Unable to remove due to permissions. "
"Please prefix command with sudo.")
raise oscexc.CommandError(e)
finally:
session.close()
class TripleOContainerImageList(command.Lister):
"""List images discovered in registry."""
auth_required = False
log = logging.getLogger(__name__ + ".TripleoContainerImageList")
def get_parser(self, prog_name):
parser = super(TripleOContainerImageList, self).get_parser(prog_name)
parser.add_argument(
"--registry-url",
dest="registry_url",
metavar='<registry url>',
default=None,
help=_("URL of registry images are to be listed from in the "
"form <fqdn>:<port>.")
)
parser.add_argument(
"--username",
dest="username",
metavar='<username>',
help=_("Username for image registry.")
)
parser.add_argument(
"--password",
dest="password",
metavar='<password>',
help=_("Password for image registry.")
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
lock = processlock.ProcessLock()
manager = image_uploader.ImageUploadManager(lock=lock)
uploader = manager.uploader('python')
registry_url_arg = parsed_args.registry_url
if registry_url_arg is None:
registry_url_arg = image_uploader.get_undercloud_registry()
url = uploader._image_to_url(registry_url_arg)
session = uploader.authenticate(url, parsed_args.username,
parsed_args.password)
try:
results = uploader.list(url.geturl(), session=session)
finally:
session.close()
cliff_results = []
for r in results:
cliff_results.append((r,))
return (("Image Name",), cliff_results)
class TripleOContainerImageShow(command.ShowOne):
"""Show image selected from the registry."""
auth_required = False
log = logging.getLogger(__name__ + ".TripleoContainerImageShow")
@property
def formatter_default(self):
return 'json'
def get_parser(self, prog_name):
parser = super(TripleOContainerImageShow, self).get_parser(prog_name)
parser.add_argument(
"--username",
dest="username",
metavar='<username>',
help=_("Username for image registry.")
)
parser.add_argument(
"--password",
dest="password",
metavar='<password>',
help=_("Password for image registry.")
)
parser.add_argument(
dest="image_to_inspect",
metavar='<image to inspect>',
help=_(
"Image to be inspected, for example: "
"docker.io/library/centos:7 or "
"docker://docker.io/library/centos:7")
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
lock = processlock.ProcessLock()
manager = image_uploader.ImageUploadManager(lock=lock)
uploader = manager.uploader('python')
url = uploader._image_to_url(parsed_args.image_to_inspect)
session = uploader.authenticate(url, parsed_args.username,
parsed_args.password)
try:
image_inspect_result = uploader.inspect(
parsed_args.image_to_inspect,
session=session)
finally:
session.close()
return self.format_image_inspect(image_inspect_result)
def format_image_inspect(self, image_inspect_result):
column_names = ['Name']
data = [image_inspect_result.pop('Name')]
result_fields = list(image_inspect_result.keys())
result_fields.sort()
for field in result_fields:
column_names.append(field)
data.append(image_inspect_result[field])
return column_names, data
class TripleOImagePrepareDefault(command.Command):
"""Generate a default ContainerImagePrepare parameter."""
auth_required = False
log = logging.getLogger(__name__ + ".TripleoImagePrepare")
def get_parser(self, prog_name):
parser = super(TripleOImagePrepareDefault, self).get_parser(prog_name)
parser.add_argument(
"--output-env-file",
dest="output_env_file",
metavar='<file path>',
help=_("File to write environment file containing default "
"ContainerImagePrepare value."),
)
parser.add_argument(
'--local-push-destination',
dest='push_destination',
action='store_true',
default=False,
help=_('Include a push_destination to trigger upload to a local '
'registry.')
)
parser.add_argument(
'--enable-registry-login',
dest='registry_login',
action='store_true',
default=False,
help=_('Use this flag to enable the flag to have systems attempt '
'to login to a remote registry prior to pulling their '
'containers. This flag should be used when '
'--local-push-destination is *NOT* used and the target '
'systems will have network connectivity to the remote '
'registries. Do not use this for an overcloud that '
'may not have network connectivity to a remote registry.')
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
cip = copy.deepcopy(kolla_builder.CONTAINER_IMAGE_PREPARE_PARAM)
if parsed_args.push_destination:
for entry in cip:
entry['push_destination'] = True
params = {
'ContainerImagePrepare': cip
}
if parsed_args.registry_login:
if parsed_args.push_destination:
self.log.warning('[WARNING] --local-push-destination was used '
'with --enable-registry-login. Please make '
'sure you understand the use of these '
'parameters together as they can cause '
'deployment failures.')
self.log.warning('[NOTE] Make sure to update the paramter_defaults'
' with ContainerImageRegistryCredentials for the '
'registries requiring authentication.')
params['ContainerImageRegistryLogin'] = True
env_data = build_env_file(params, self.app.command_options)
self.app.stdout.write(env_data)
if parsed_args.output_env_file:
if os.path.exists(parsed_args.output_env_file):
self.log.warning("Output env file exists, "
"moving it to backup.")
shutil.move(parsed_args.output_env_file,
parsed_args.output_env_file + ".backup")
utils.safe_write(parsed_args.output_env_file, env_data)
class TripleOImagePrepare(command.Command):
"""Prepare and upload containers from a single command."""
auth_required = False
log = logging.getLogger(__name__ + ".TripleoImagePrepare")
def get_parser(self, prog_name):
parser = super(TripleOImagePrepare, self).get_parser(prog_name)
try:
roles_file = utils.rel_or_abs_path(
constants.OVERCLOUD_ROLES_FILE,
constants.TRIPLEO_HEAT_TEMPLATES)
except exceptions.DeploymentError:
roles_file = None
parser.add_argument(
'--environment-file', '-e', metavar='<file path>',
action='append', dest='environment_files',
help=_('Environment file containing the ContainerImagePrepare '
'parameter which specifies all prepare actions. '
'Also, environment files specifying which services are '
'containerized. Entries will be filtered to only contain '
'images used by containerized services. (Can be specified '
'more than once.)')
)
parser.add_argument(
'--environment-directory', metavar='<HEAT ENVIRONMENT DIRECTORY>',
action='append', dest='environment_directories',
default=[os.path.expanduser(constants.DEFAULT_ENV_DIRECTORY)],
help=_('Environment file directories that are automatically '
'added to the environment. '
'Can be specified more than once. Files in directories are '
'loaded in ascending sort order.')
)
parser.add_argument(
'--roles-file', '-r', dest='roles_file',
default=roles_file,
help=_(
'Roles file, overrides the default %s in the t-h-t templates '
'directory used for deployment. May be an '
'absolute path or the path relative to the templates dir.'
) % constants.OVERCLOUD_ROLES_FILE
)
parser.add_argument(
"--output-env-file",
dest="output_env_file",
metavar='<file path>',
help=_("File to write heat environment file which specifies all "
"image parameters. Any existing file will be overwritten."),
)
parser.add_argument(
'--dry-run',
dest='dry_run',
action='store_true',
default=False,
help=_('Do not perform any pull, modify, or push operations. '
'The environment file will still be populated as if these '
'operations were performed.')
)
parser.add_argument(
"--cleanup",
dest="cleanup",
metavar='<full, partial, none>',
default=image_uploader.CLEANUP_FULL,
help=_("Cleanup behavior for local images left after upload. "
"The default 'full' will attempt to delete all local "
"images. 'partial' will leave images required for "
"deployment on this host. 'none' will do no cleanup.")
)
parser.add_argument(
"--log-file",
dest="log_file",
default=constants.CONTAINER_IMAGE_PREPARE_LOG_FILE,
help=_("Log file to be used for python logging. "
"By default it would be logged to "
"$HOME/container_image_prepare.log.")
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
if parsed_args.cleanup not in image_uploader.CLEANUP:
raise oscexc.CommandError('--cleanup must be one of: %s' %
', '.join(image_uploader.CLEANUP))
role_file = None
if parsed_args.roles_file:
role_file = utils.rel_or_abs_path(parsed_args.roles_file,
constants.TRIPLEO_HEAT_TEMPLATES)
env_dirs = [os.path.abspath(x)
for x in parsed_args.environment_directories]
env_files = [os.path.abspath(x)
for x in parsed_args.environment_files]
extra_vars = {
"roles_file": role_file,
"environment_directories": env_dirs,
"environment_files": env_files,
"cleanup": parsed_args.cleanup,
"dry_run": parsed_args.dry_run,
"log_file": parsed_args.log_file}
if self.app_args.verbose_level >= 3:
extra_vars["debug"] = True
if parsed_args.output_env_file:
extra_vars["output_env_file"] = os.path.abspath(
parsed_args.output_env_file)
with oooutils.TempDirs() as tmp:
oooutils.run_ansible_playbook(
playbook='cli-container-image-prepare.yaml',
inventory='localhost,',
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
verbosity=oooutils.playbook_verbosity(self=self),
extra_vars=extra_vars)