357 lines
13 KiB
Python
357 lines
13 KiB
Python
# 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.
|
|
#
|
|
|
|
"""Orchestration v1 Software Deployment action implementations"""
|
|
|
|
import logging
|
|
|
|
from osc_lib.command import command
|
|
from osc_lib import exceptions as exc
|
|
from osc_lib import utils
|
|
from oslo_serialization import jsonutils
|
|
|
|
from heatclient._i18n import _
|
|
from heatclient.common import deployment_utils
|
|
from heatclient.common import format_utils
|
|
from heatclient.common import utils as heat_utils
|
|
from heatclient import exc as heat_exc
|
|
|
|
|
|
class CreateDeployment(format_utils.YamlFormat):
|
|
"""Create a software deployment."""
|
|
|
|
log = logging.getLogger(__name__ + '.CreateDeployment')
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(CreateDeployment, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'name',
|
|
metavar='<deployment-name>',
|
|
help=_('Name of the derived config associated with this '
|
|
'deployment. This is used to apply a sort order to the '
|
|
'list of configurations currently deployed to the server.')
|
|
)
|
|
parser.add_argument(
|
|
'--input-value',
|
|
metavar='<key=value>',
|
|
action='append',
|
|
help=_('Input value to set on the deployment. This can be '
|
|
'specified multiple times.')
|
|
)
|
|
parser.add_argument(
|
|
'--action',
|
|
metavar='<action>',
|
|
default='UPDATE',
|
|
help=_('Name of an action for this deployment. This can be a '
|
|
'custom action, or one of CREATE, UPDATE, DELETE, SUSPEND, '
|
|
'RESUME. Default is UPDATE')
|
|
)
|
|
parser.add_argument(
|
|
'--config',
|
|
metavar='<config>',
|
|
help=_('ID of the configuration to deploy')
|
|
)
|
|
parser.add_argument(
|
|
'--signal-transport',
|
|
metavar='<signal-transport>',
|
|
default='TEMP_URL_SIGNAL',
|
|
help=_('How the server should signal to heat with the deployment '
|
|
'output values. TEMP_URL_SIGNAL will create a Swift '
|
|
'TempURL to be signaled via HTTP PUT. ZAQAR_SIGNAL will '
|
|
'create a dedicated zaqar queue to be signaled using the '
|
|
'provided keystone credentials.NO_SIGNAL will result in '
|
|
'the resource going to the COMPLETE state without waiting '
|
|
'for any signal')
|
|
)
|
|
parser.add_argument(
|
|
'--container',
|
|
metavar='<container>',
|
|
help=_('Optional name of container to store TEMP_URL_SIGNAL '
|
|
'objects in. If not specified a container will be created '
|
|
'with a name derived from the DEPLOY_NAME')
|
|
)
|
|
parser.add_argument(
|
|
'--timeout',
|
|
metavar='<timeout>',
|
|
type=int,
|
|
default=60,
|
|
help=_('Deployment timeout in minutes')
|
|
)
|
|
parser.add_argument(
|
|
'--server',
|
|
metavar='<server>',
|
|
required=True,
|
|
help=_('ID of the server being deployed to')
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
self.log.debug('take_action(%s)', parsed_args)
|
|
|
|
client = self.app.client_manager.orchestration
|
|
|
|
config = {}
|
|
if parsed_args.config:
|
|
try:
|
|
config = client.software_configs.get(parsed_args.config)
|
|
except heat_exc.HTTPNotFound:
|
|
msg = (_('Software configuration not found: %s') %
|
|
parsed_args.config)
|
|
raise exc.CommandError(msg)
|
|
|
|
derived_params = deployment_utils.build_derived_config_params(
|
|
parsed_args.action,
|
|
config,
|
|
parsed_args.name,
|
|
heat_utils.format_parameters(parsed_args.input_value, False),
|
|
parsed_args.server,
|
|
parsed_args.signal_transport,
|
|
signal_id=deployment_utils.build_signal_id(client, parsed_args)
|
|
)
|
|
derived_config = client.software_configs.create(**derived_params)
|
|
|
|
sd = client.software_deployments.create(
|
|
config_id=derived_config.id,
|
|
server_id=parsed_args.server,
|
|
action=parsed_args.action,
|
|
status='IN_PROGRESS'
|
|
)
|
|
|
|
return zip(*sorted(sd.to_dict().items()))
|
|
|
|
|
|
class DeleteDeployment(command.Command):
|
|
"""Delete software deployment(s) and correlative config(s)."""
|
|
|
|
log = logging.getLogger(__name__ + '.DeleteDeployment')
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(DeleteDeployment, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'deployment',
|
|
metavar='<deployment>',
|
|
nargs='+',
|
|
help=_('ID of the deployment(s) to delete.')
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
self.log.debug("take_action(%s)", parsed_args)
|
|
hc = self.app.client_manager.orchestration
|
|
failure_count = 0
|
|
|
|
for deploy_id in parsed_args.deployment:
|
|
try:
|
|
sd = hc.software_deployments.get(deployment_id=deploy_id)
|
|
hc.software_deployments.delete(
|
|
deployment_id=deploy_id)
|
|
except Exception as e:
|
|
if isinstance(e, heat_exc.HTTPNotFound):
|
|
print(_('Deployment with ID %s not found') % deploy_id)
|
|
else:
|
|
print(_('Deployment with ID %s failed to delete')
|
|
% deploy_id)
|
|
failure_count += 1
|
|
continue
|
|
# just try best to delete the corresponding config
|
|
try:
|
|
config_id = getattr(sd, 'config_id')
|
|
hc.software_configs.delete(config_id=config_id)
|
|
except Exception:
|
|
print(_('Failed to delete the correlative config'
|
|
' %(config_id)s of deployment %(deploy_id)s') %
|
|
{'config_id': config_id, 'deploy_id': deploy_id})
|
|
|
|
if failure_count:
|
|
raise exc.CommandError(_('Unable to delete %(count)s of the '
|
|
'%(total)s deployments.') %
|
|
{'count': failure_count,
|
|
'total': len(parsed_args.deployment)})
|
|
|
|
|
|
class ListDeployment(command.Lister):
|
|
"""List software deployments."""
|
|
|
|
log = logging.getLogger(__name__ + '.ListDeployment')
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(ListDeployment, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'--server',
|
|
metavar='<server>',
|
|
help=_('ID of the server to fetch deployments for')
|
|
)
|
|
parser.add_argument(
|
|
'--long',
|
|
action='store_true',
|
|
help=_('List more fields in output')
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
self.log.debug("take_action(%s)", parsed_args)
|
|
|
|
heat_client = self.app.client_manager.orchestration
|
|
return _list_deployment(heat_client, args=parsed_args)
|
|
|
|
|
|
def _list_deployment(heat_client, args=None):
|
|
kwargs = {'server_id': args.server} if args.server else {}
|
|
columns = ['id', 'config_id', 'server_id', 'action', 'status']
|
|
if args.long:
|
|
columns.append('creation_time')
|
|
columns.append('status_reason')
|
|
|
|
deployments = heat_client.software_deployments.list(**kwargs)
|
|
return (
|
|
columns,
|
|
(utils.get_item_properties(s, columns) for s in deployments)
|
|
)
|
|
|
|
|
|
class ShowDeployment(command.ShowOne):
|
|
"""Show SoftwareDeployment Details."""
|
|
|
|
log = logging.getLogger(__name__ + ".ShowSoftwareDeployment")
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(ShowDeployment, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'deployment',
|
|
metavar='<deployment>',
|
|
help=_('ID of the deployment')
|
|
)
|
|
parser.add_argument(
|
|
'--long',
|
|
action='store_true',
|
|
help=_('Show more fields in output')
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
self.log.debug("take_action(%s)", parsed_args)
|
|
|
|
heat_client = self.app.client_manager.orchestration
|
|
try:
|
|
data = heat_client.software_deployments.get(
|
|
deployment_id=parsed_args.deployment)
|
|
except heat_exc.HTTPNotFound:
|
|
raise exc.CommandError(
|
|
_('Software Deployment not found: %s')
|
|
% parsed_args.deployment)
|
|
else:
|
|
columns = [
|
|
'id',
|
|
'server_id',
|
|
'config_id',
|
|
'creation_time',
|
|
'updated_time',
|
|
'status',
|
|
'status_reason',
|
|
'input_values',
|
|
'action',
|
|
]
|
|
if parsed_args.long:
|
|
columns.append('output_values')
|
|
return columns, utils.get_item_properties(data, columns)
|
|
|
|
|
|
class ShowMetadataDeployment(command.Command):
|
|
"""Get deployment configuration metadata for the specified server."""
|
|
|
|
log = logging.getLogger(__name__ + '.ShowMetadataDeployment')
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(ShowMetadataDeployment, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'server',
|
|
metavar='<server>',
|
|
help=_('ID of the server to fetch deployments for')
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
self.log.debug("take_action(%s)", parsed_args)
|
|
heat_client = self.app.client_manager.orchestration
|
|
md = heat_client.software_deployments.metadata(
|
|
server_id=parsed_args.server)
|
|
print(jsonutils.dumps(md, indent=2))
|
|
|
|
|
|
class ShowOutputDeployment(command.Command):
|
|
"""Show a specific deployment output."""
|
|
|
|
log = logging.getLogger(__name__ + '.ShowOutputDeployment')
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(ShowOutputDeployment, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'deployment',
|
|
metavar='<deployment>',
|
|
help=_('ID of deployment to show the output for')
|
|
)
|
|
parser.add_argument(
|
|
'output',
|
|
metavar='<output-name>',
|
|
nargs='?',
|
|
default=None,
|
|
help=_('Name of an output to display')
|
|
)
|
|
parser.add_argument(
|
|
'--all',
|
|
default=False,
|
|
action='store_true',
|
|
help=_('Display all deployment outputs')
|
|
)
|
|
parser.add_argument(
|
|
'--long',
|
|
action='store_true',
|
|
default=False,
|
|
help='Show full deployment logs in output',
|
|
)
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
self.log.debug("take_action(%s)", parsed_args)
|
|
|
|
heat_client = self.app.client_manager.orchestration
|
|
if (not parsed_args.all and parsed_args.output is None or
|
|
parsed_args.all and parsed_args.output is not None):
|
|
raise exc.CommandError(
|
|
_('Error: either %(output)s or %(all)s argument is needed.')
|
|
% {'output': '<output-name>', 'all': '--all'})
|
|
try:
|
|
sd = heat_client.software_deployments.get(
|
|
deployment_id=parsed_args.deployment)
|
|
except heat_exc.HTTPNotFound:
|
|
raise exc.CommandError(_('Deployment not found: %s')
|
|
% parsed_args.deployment)
|
|
outputs = sd.output_values
|
|
if outputs:
|
|
if parsed_args.all:
|
|
print('output_values:\n')
|
|
for k in outputs:
|
|
format_utils.print_software_deployment_output(
|
|
data=outputs, name=k, long=parsed_args.long)
|
|
else:
|
|
if parsed_args.output not in outputs:
|
|
msg = (_('Output %(output)s does not exist in deployment'
|
|
' %(deployment)s')
|
|
% {'output': parsed_args.output,
|
|
'deployment': parsed_args.deployment})
|
|
raise exc.CommandError(msg)
|
|
else:
|
|
print('output_value:\n')
|
|
format_utils.print_software_deployment_output(
|
|
data=outputs, name=parsed_args.output)
|