python-heatclient/heatclient/osc/v1/resource.py

304 lines
9.8 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 Stack action implementations"""
import logging
from osc_lib.command import command
from osc_lib import exceptions as exc
from osc_lib.i18n import _
from osc_lib import utils
from oslo_serialization import jsonutils
from urllib import request
from heatclient.common import format_utils
from heatclient.common import utils as heat_utils
from heatclient import exc as heat_exc
class ResourceShow(command.ShowOne):
"""Display stack resource."""
log = logging.getLogger(__name__ + '.ResourceShowStack')
def get_parser(self, prog_name):
parser = super(ResourceShow, self).get_parser(prog_name)
parser.add_argument(
'stack',
metavar='<stack>',
help=_('Name or ID of stack to query')
)
parser.add_argument(
'resource',
metavar='<resource>',
help=_('Name of resource')
)
parser.add_argument(
'--with-attr',
metavar='<attribute>',
action='append',
help=_('Attribute to show, can be specified multiple times')
)
return parser
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
client = self.app.client_manager.orchestration
try:
resource = client.resources.get(parsed_args.stack,
parsed_args.resource,
with_attr=parsed_args.with_attr)
except heat_exc.HTTPNotFound:
msg = (_('Stack or resource not found: %(stack)s %(resource)s') %
{'stack': parsed_args.stack,
'resource': parsed_args.resource})
raise exc.CommandError(msg)
return self.dict2columns(resource.to_dict())
class ResourceList(command.Lister):
"""List stack resources."""
log = logging.getLogger(__name__ + '.ResourceListStack')
@property
def formatter_namespace(self):
return 'heatclient.resource.formatter.list'
def get_parser(self, prog_name):
parser = super(ResourceList, self).get_parser(prog_name)
parser.add_argument(
'stack',
metavar='<stack>',
help=_('Name or ID of stack to query')
)
parser.add_argument(
'--long',
action='store_true',
help=_('Enable detailed information presented for each resource '
'in resource list')
)
parser.add_argument(
'-n', '--nested-depth',
metavar='<nested-depth>',
type=int,
help=_('Depth of nested stacks from which to display resources')
)
parser.add_argument(
'--filter',
metavar='<key=value>',
action='append',
help=_('Filter parameters to apply on returned resources based on '
'their name, status, type, action, id and '
'physical_resource_id')
)
return parser
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
client = self.app.client_manager.orchestration
fields = {
'nested_depth': parsed_args.nested_depth,
'with_detail': parsed_args.long,
'filters': heat_utils.format_parameters(parsed_args.filter),
}
try:
resources = client.resources.list(parsed_args.stack, **fields)
except heat_exc.HTTPNotFound:
msg = _('Stack not found: %s') % parsed_args.stack
raise exc.CommandError(msg)
if parsed_args.formatter == 'dot':
return [], resources
columns = ['physical_resource_id', 'resource_type', 'resource_status',
'updated_time']
if len(resources) >= 1 and not hasattr(resources[0], 'resource_name'):
columns.insert(0, 'logical_resource_id')
else:
columns.insert(0, 'resource_name')
if parsed_args.nested_depth or parsed_args.long:
columns.append('stack_name')
return (
columns,
(utils.get_item_properties(r, columns) for r in resources)
)
class ResourceMetadata(format_utils.JsonFormat):
"""Show resource metadata"""
log = logging.getLogger(__name__ + ".ResourceMetadata")
def get_parser(self, prog_name):
parser = super(ResourceMetadata, self).get_parser(prog_name)
parser.add_argument(
'stack',
metavar='<stack>',
help=_('Stack to display (name or ID)'),
)
parser.add_argument(
'resource',
metavar='<resource>',
help=_('Name of the resource to show the metadata for'))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
heat_client = self.app.client_manager.orchestration
return _resource_metadata(heat_client, parsed_args)
def _resource_metadata(heat_client, args):
fields = {'stack_id': args.stack,
'resource_name': args.resource}
try:
metadata = heat_client.resources.metadata(**fields)
except heat_exc.HTTPNotFound:
raise exc.CommandError(_('Stack %(stack)s or resource %(resource)s '
'not found.') %
{'stack': args.stack,
'resource': args.resource})
data = list(metadata.values())
columns = list(metadata.keys())
return columns, data
class ResourceSignal(command.Command):
"""Signal a resource with optional data."""
log = logging.getLogger(__name__ + ".ResourceSignal")
def get_parser(self, prog_name):
parser = super(ResourceSignal, self).get_parser(prog_name)
parser.add_argument(
'stack',
metavar='<stack>',
help=_('Name or ID of stack the resource belongs to'),
)
parser.add_argument(
'resource',
metavar='<resource>',
help=_('Name of the resoure to signal'),
)
parser.add_argument(
'--data',
metavar='<data>',
help=_('JSON Data to send to the signal handler')
)
parser.add_argument(
'--data-file',
metavar='<data-file>',
help=_('File containing JSON data to send to the signal handler')
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
heat_client = self.app.client_manager.orchestration
return _resource_signal(heat_client, parsed_args)
def _resource_signal(heat_client, args):
fields = {'stack_id': args.stack,
'resource_name': args.resource}
data = args.data
data_file = args.data_file
if data and data_file:
raise exc.CommandError(_('Should only specify one of data or '
'data-file'))
if data_file:
data_url = heat_utils.normalise_file_path_to_url(data_file)
data = request.urlopen(data_url).read()
if data:
try:
data = jsonutils.loads(data)
except ValueError as ex:
raise exc.CommandError(_('Data should be in JSON format: %s') % ex)
if not isinstance(data, dict):
raise exc.CommandError(_('Data should be a JSON dict'))
fields['data'] = data
try:
heat_client.resources.signal(**fields)
except heat_exc.HTTPNotFound:
raise exc.CommandError(_('Stack %(stack)s or resource %(resource)s '
'not found.') %
{'stack': args.stack,
'resource': args.resource})
class ResourceMarkUnhealthy(command.Command):
"""Set resource's health."""
log = logging.getLogger(__name__ + ".ResourceMarkUnhealthy")
def get_parser(self, prog_name):
parser = super(ResourceMarkUnhealthy, self).get_parser(prog_name)
parser.add_argument(
'stack',
metavar='<stack>',
help=_('Name or ID of stack the resource belongs to')
)
parser.add_argument(
'resource',
metavar='<resource>',
help=_('Name of the resource')
)
parser.add_argument(
'reason',
default="",
nargs='?',
help=_('Reason for state change')
)
parser.add_argument(
'--reset',
default=False,
action="store_true",
help=_('Set the resource as healthy')
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
heat_client = self.app.client_manager.orchestration
fields = {'stack_id': parsed_args.stack,
'resource_name': parsed_args.resource,
'mark_unhealthy': not parsed_args.reset,
'resource_status_reason': parsed_args.reason}
try:
heat_client.resources.mark_unhealthy(**fields)
except heat_exc.HTTPNotFound:
raise exc.CommandError(_('Stack or resource not found: '
'%(id)s %(resource)s') %
{'id': parsed_args.stack,
'resource': parsed_args.resource})