python-heatclient-2.1.0/0000775000175000017500000000000013644132101015166 5ustar zuulzuul00000000000000python-heatclient-2.1.0/babel.cfg0000664000175000017500000000002013644132026016712 0ustar zuulzuul00000000000000[python: **.py] python-heatclient-2.1.0/playbooks/0000775000175000017500000000000013644132101017171 5ustar zuulzuul00000000000000python-heatclient-2.1.0/playbooks/devstack/0000775000175000017500000000000013644132101020775 5ustar zuulzuul00000000000000python-heatclient-2.1.0/playbooks/devstack/functional-post.yaml0000664000175000017500000000455113644132026025021 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*nose_results.html - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testr_results.html.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.testrepository/tmp* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testrepository.subunit.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}/tox' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.tox/*/log/* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs python-heatclient-2.1.0/playbooks/devstack/functional-run.yaml0000664000175000017500000000343113644132026024634 0ustar zuulzuul00000000000000- hosts: all name: Job for heatclient-functional tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x cat << 'EOF' >>"/tmp/dg-local.conf" [[local|localrc]] enable_plugin heat https://opendev.org/openstack/heat EOF executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_PROJECT_FROM_GIT=python-heatclient if [ "{{ branch_override }}" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi function post_test_hook { # Configure and run functional tests $BASE/new/python-heatclient/heatclient/tests/functional/hooks/post_test_hook.sh } export -f post_test_hook cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' python-heatclient-2.1.0/heatclient/0000775000175000017500000000000013644132101017306 5ustar zuulzuul00000000000000python-heatclient-2.1.0/heatclient/osc/0000775000175000017500000000000013644132101020072 5ustar zuulzuul00000000000000python-heatclient-2.1.0/heatclient/osc/plugin.py0000664000175000017500000000470513644132026021756 0ustar zuulzuul00000000000000# 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. # """OpenStackClient plugin for Orchestration service.""" import logging from osc_lib import utils LOG = logging.getLogger(__name__) DEFAULT_ORCHESTRATION_API_VERSION = '1' API_VERSION_OPTION = 'os_orchestration_api_version' API_NAME = 'orchestration' API_VERSIONS = { '1': 'heatclient.v1.client.Client', } def make_client(instance): """Returns an orchestration service client""" heat_client = utils.get_client_class( API_NAME, instance._api_version[API_NAME], API_VERSIONS) LOG.debug('Instantiating orchestration client: %s', heat_client) kwargs = {'region_name': instance.region_name, 'interface': instance.interface} if instance.session: kwargs.update({'session': instance.session, 'service_type': API_NAME}) elif instance.auth_plugin_name == 'token_endpoint': kwargs.update({'endpoint': instance.auth.url, 'token': instance.auth.token}) else: endpoint = instance.get_endpoint_for_service_type( API_NAME, region_name=instance.region_name, interface=instance.interface, ) kwargs.update({'endpoint': endpoint, 'auth_url': instance.auth.auth_url, 'username': instance.auth_ref.username, 'token': instance.auth_ref.auth_token}) client = heat_client(**kwargs) return client def build_option_parser(parser): """Hook to add global options""" parser.add_argument( '--os-orchestration-api-version', metavar='', default=utils.env( 'OS_ORCHESTRATION_API_VERSION', default=DEFAULT_ORCHESTRATION_API_VERSION), help='Orchestration API version, default=' + DEFAULT_ORCHESTRATION_API_VERSION + ' (Env: OS_ORCHESTRATION_API_VERSION)') return parser python-heatclient-2.1.0/heatclient/osc/v1/0000775000175000017500000000000013644132101020420 5ustar zuulzuul00000000000000python-heatclient-2.1.0/heatclient/osc/v1/software_deployment.py0000664000175000017500000003131613644132026025076 0ustar zuulzuul00000000000000# 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='', 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='', action='append', help=_('Input value to set on the deployment. This can be ' 'specified multiple times.') ) parser.add_argument( '--action', metavar='', 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='', help=_('ID of the configuration to deploy') ) parser.add_argument( '--signal-transport', metavar='', 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='', 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='', type=int, default=60, help=_('Deployment timeout in minutes') ) parser.add_argument( '--server', metavar='', 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='', 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='', 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='', 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='', 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='', help=_('ID of deployment to show the output for') ) parser.add_argument( 'output', metavar='', 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': '', '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) python-heatclient-2.1.0/heatclient/osc/v1/snapshot.py0000664000175000017500000001756113644132026022651 0ustar zuulzuul00000000000000# 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 Snapshot implementations.""" import logging import sys from osc_lib.command import command from osc_lib import exceptions as exc from osc_lib.i18n import _ from osc_lib import utils import six from heatclient.common import format_utils from heatclient import exc as heat_exc class ListSnapshot(command.Lister): """List stack snapshots.""" log = logging.getLogger(__name__ + ".ListSnapshot") def get_parser(self, prog_name): parser = super(ListSnapshot, self).get_parser(prog_name) parser.add_argument( 'stack', metavar='', help=_('Name or ID of stack containing the snapshots') ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) heat_client = self.app.client_manager.orchestration return self._list_snapshot(heat_client, parsed_args) def _list_snapshot(self, heat_client, parsed_args): fields = {'stack_id': parsed_args.stack} try: snapshots = heat_client.stacks.snapshot_list(**fields) except heat_exc.HTTPNotFound: raise exc.CommandError(_('Stack not found: %s') % parsed_args.stack) columns = ['id', 'name', 'status', 'status_reason', 'creation_time'] return ( columns, (utils.get_dict_properties(s, columns) for s in snapshots['snapshots']) ) class ShowSnapshot(format_utils.YamlFormat): """Show stack snapshot.""" log = logging.getLogger(__name__ + ".ShowSnapshot") def get_parser(self, prog_name): parser = super(ShowSnapshot, self).get_parser(prog_name) parser.add_argument( 'stack', metavar='', help=_('Name or ID of stack containing the snapshot') ) parser.add_argument( 'snapshot', metavar='', help=_('ID of the snapshot to show') ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) heat_client = self.app.client_manager.orchestration return self._show_snapshot(heat_client, parsed_args.stack, parsed_args.snapshot) def _show_snapshot(self, heat_client, stack_id, snapshot_id): try: data = heat_client.stacks.snapshot_show(stack_id, snapshot_id) except heat_exc.HTTPNotFound: raise exc.CommandError(_('Snapshot ID <%(snapshot_id)s> not found ' 'for stack <%(stack_id)s>') % {'snapshot_id': snapshot_id, 'stack_id': stack_id}) rows = list(six.itervalues(data)) columns = list(six.iterkeys(data)) return columns, rows class RestoreSnapshot(command.Command): """Restore stack snapshot""" log = logging.getLogger(__name__ + ".RestoreSnapshot") def get_parser(self, prog_name): parser = super(RestoreSnapshot, self).get_parser(prog_name) parser.add_argument( 'stack', metavar='', help=_('Name or ID of stack containing the snapshot') ) parser.add_argument( 'snapshot', metavar='', help=_('ID of the snapshot to restore') ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) heat_client = self.app.client_manager.orchestration return self._restore_snapshot(heat_client, parsed_args) def _restore_snapshot(self, heat_client, parsed_args): fields = {'stack_id': parsed_args.stack, 'snapshot_id': parsed_args.snapshot} try: heat_client.stacks.restore(**fields) except heat_exc.HTTPNotFound: raise exc.CommandError(_('Stack %(stack)s or ' 'snapshot %(snapshot)s not found.') % {'stack': parsed_args.stack, 'snapshot': parsed_args.snapshot}) class CreateSnapshot(command.ShowOne): """Create stack snapshot.""" log = logging.getLogger(__name__ + ".CreateSnapshot") def get_parser(self, prog_name): parser = super(CreateSnapshot, self).get_parser(prog_name) parser.add_argument( 'stack', metavar='', help=_('Name or ID of stack') ) parser.add_argument( '--name', metavar='', help=_('Name of snapshot') ) 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.stacks.snapshot(parsed_args.stack, parsed_args.name) except heat_exc.HTTPNotFound: raise exc.CommandError(_('Stack not found: %s') % parsed_args.stack) columns = [ 'ID', 'name', 'status', 'status_reason', 'data', 'creation_time' ] return (columns, utils.get_dict_properties(data, columns)) class DeleteSnapshot(command.Command): """Delete stack snapshot.""" log = logging.getLogger(__name__ + ".DeleteSnapshot") def get_parser(self, prog_name): parser = super(DeleteSnapshot, self).get_parser(prog_name) parser.add_argument( 'stack', metavar='', help=_('Name or ID of stack') ) parser.add_argument( 'snapshot', metavar='', help=_('ID of stack snapshot') ) parser.add_argument( '-y', '--yes', action='store_true', help=_('Skip yes/no prompt (assume yes)') ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) heat_client = self.app.client_manager.orchestration msg = ('User did not confirm snapshot delete ' '%sso taking no action.') try: if not parsed_args.yes and sys.stdin.isatty(): sys.stdout.write( _('Are you sure you want to delete the snapshot of this ' 'stack [Y/N]?')) prompt_response = sys.stdin.readline().lower() if not prompt_response.startswith('y'): self.log.info(msg, '') return except KeyboardInterrupt: # ctrl-c self.log.info(msg, '(ctrl-c) ') return except EOFError: # ctrl-d self.log.info(msg, '(ctrl-d) ') return try: heat_client.stacks.snapshot_delete(parsed_args.stack, parsed_args.snapshot) except heat_exc.HTTPNotFound: raise exc.CommandError(_('Snapshot ID <%(snapshot_id)s> not found ' 'for stack <%(stack_id)s>') % {'snapshot_id': parsed_args.snapshot, 'stack_id': parsed_args.stack}) python-heatclient-2.1.0/heatclient/osc/v1/event.py0000664000175000017500000002042513644132026022124 0ustar zuulzuul00000000000000# 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. # # Copyright 2015 IBM Corp. import logging import time from cliff.formatters import base from osc_lib.command import command from osc_lib import utils from heatclient._i18n import _ from heatclient.common import event_utils from heatclient.common import utils as heat_utils from heatclient import exc class ShowEvent(command.ShowOne): """Show event details.""" log = logging.getLogger(__name__ + '.ShowEvent') def get_parser(self, prog_name): parser = super(ShowEvent, self).get_parser(prog_name) parser.add_argument( 'stack', metavar='', help=_('Name or ID of stack to show events for') ) parser.add_argument( 'resource', metavar='', help=_('Name of the resource event belongs to') ) parser.add_argument( 'event', metavar='', help=_('ID of event to display details for') ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) client = self.app.client_manager.orchestration fields = { 'stack_id': parsed_args.stack, 'resource_name': parsed_args.resource, 'event_id': parsed_args.event } try: client.stacks.get(parsed_args.stack) client.resources.get(parsed_args.stack, parsed_args.resource) event = client.events.get(**fields) except exc.HTTPNotFound as ex: raise exc.CommandError(str(ex)) formatters = { 'links': heat_utils.link_formatter, 'resource_properties': heat_utils.json_formatter } columns = [] for key in event.to_dict(): columns.append(key) return columns, utils.get_item_properties(event, columns, formatters=formatters) class ListEvent(command.Lister): """List events.""" log = logging.getLogger(__name__ + '.ListEvent') @property def formatter_default(self): return 'log' @property def formatter_namespace(self): return 'heatclient.event.formatter.list' def get_parser(self, prog_name): parser = super(ListEvent, self).get_parser(prog_name) parser.add_argument( 'stack', metavar='', help=_('Name or ID of stack to show events for') ) parser.add_argument( '--resource', metavar='', help=_('Name of resource to show events for. Note: this cannot ' 'be specified with --nested-depth') ) parser.add_argument( '--filter', metavar='', action='append', help=_('Filter parameters to apply on returned events') ) parser.add_argument( '--limit', metavar='', type=int, help=_('Limit the number of events returned') ) parser.add_argument( '--marker', metavar='', help=_('Only return events that appear after the given ID') ) parser.add_argument( '--nested-depth', metavar='', type=int, help=_('Depth of nested stacks from which to display events. ' 'Note: this cannot be specified with --resource') ) parser.add_argument( '--sort', metavar='[:]', action='append', help=_('Sort output by selected keys and directions (asc or desc) ' '(default: asc). Specify multiple times to sort on ' 'multiple keys. Sort key can be: ' '"event_time" (default), "resource_name", "links", ' '"logical_resource_id", "resource_status", ' '"resource_status_reason", "physical_resource_id", or ' '"id". You can leave the key empty and specify ":desc" ' 'for sorting by reverse time.') ) parser.add_argument( '--follow', action='store_true', help=_('Print events until process is halted') ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) client = self.app.client_manager.orchestration columns = ['id', 'resource_status', 'resource_status_reason', 'event_time', 'physical_resource_id'] kwargs = { 'resource_name': parsed_args.resource, 'filters': heat_utils.format_parameters(parsed_args.filter), 'sort_dir': 'asc' } if parsed_args.resource and parsed_args.nested_depth: msg = _('--nested-depth cannot be specified with --resource') raise exc.CommandError(msg) if parsed_args.nested_depth: columns.append('stack_name') nested_depth = parsed_args.nested_depth else: nested_depth = 0 if parsed_args.sort: sorts = [] sort_keys = [] for sort in parsed_args.sort: if sort.startswith(":"): sorts.append(":".join(["event_time", sort.lstrip(":")])) else: sorts.append(sort) sort_keys.append(sort.split(":")[0]) kwargs['sort_keys'] = sort_keys if ":" in parsed_args.sort[0]: kwargs['sort_dir'] = parsed_args.sort[0].split(":")[1] if parsed_args.follow: if parsed_args.formatter != 'log': msg = _('--follow can only be specified with --format log') raise exc.CommandError(msg) marker = parsed_args.marker try: event_log_context = heat_utils.EventLogContext() while True: events = event_utils.get_events( client, stack_id=parsed_args.stack, event_args=kwargs, nested_depth=nested_depth, marker=marker) if events: marker = getattr(events[-1], 'id', None) events_log = heat_utils.event_log_formatter( events, event_log_context) self.app.stdout.write(events_log) self.app.stdout.write('\n') time.sleep(5) # this loop never exits except (KeyboardInterrupt, EOFError): # ctrl-c, ctrl-d return [], [] events = event_utils.get_events( client, stack_id=parsed_args.stack, event_args=kwargs, nested_depth=nested_depth, marker=parsed_args.marker, limit=parsed_args.limit) if parsed_args.sort: events = utils.sort_items(events, ','.join(sorts)) if parsed_args.formatter == 'log': return [], events if len(events): if hasattr(events[0], 'resource_name'): columns.insert(0, 'resource_name') columns.append('logical_resource_id') else: columns.insert(0, 'logical_resource_id') return ( columns, (utils.get_item_properties(s, columns) for s in events) ) class LogFormatter(base.ListFormatter): """A formatter which prints event objects in a log style""" def add_argument_group(self, parser): pass def emit_list(self, column_names, data, stdout, parsed_args): stdout.write(heat_utils.event_log_formatter(data)) stdout.write('\n') python-heatclient-2.1.0/heatclient/osc/v1/resource_type.py0000664000175000017500000001114713644132026023674 0ustar zuulzuul00000000000000# 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 resource type implementations""" import logging from osc_lib.command import command from osc_lib import exceptions as exc from osc_lib.i18n import _ import six from heatclient.common import format_utils from heatclient.common import utils as heat_utils from heatclient import exc as heat_exc class ResourceTypeShow(format_utils.YamlFormat): """Show details and optionally generate a template for a resource type.""" log = logging.getLogger(__name__ + ".ResourceTypeShow") def get_parser(self, prog_name): parser = super(ResourceTypeShow, self).get_parser(prog_name) parser.add_argument( 'resource_type', metavar='', help=_('Resource type to show details for'), ) parser.add_argument( '--template-type', metavar='', help=_('Optional template type to generate, hot or cfn') ) parser.add_argument( '--long', default=False, action='store_true', help=_('Show resource type with corresponding description.') ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) if parsed_args.template_type is not None and parsed_args.long: msg = _('Cannot use --template-type and --long in one time.') raise exc.CommandError(msg) heat_client = self.app.client_manager.orchestration return _show_resourcetype(heat_client, parsed_args) def _show_resourcetype(heat_client, parsed_args): try: if parsed_args.template_type: template_type = parsed_args.template_type.lower() if template_type not in ('hot', 'cfn'): raise exc.CommandError( _('Template type invalid: %s') % parsed_args.template_type) fields = {'resource_type': parsed_args.resource_type, 'template_type': template_type} data = heat_client.resource_types.generate_template(**fields) else: data = heat_client.resource_types.get(parsed_args.resource_type, parsed_args.long) except heat_exc.HTTPNotFound: raise exc.CommandError( _('Resource type not found: %s') % parsed_args.resource_type) rows = list(six.itervalues(data)) columns = list(six.iterkeys(data)) return columns, rows class ResourceTypeList(command.Lister): """List resource types.""" log = logging.getLogger(__name__ + '.ResourceTypeList') def get_parser(self, prog_name): parser = super(ResourceTypeList, self).get_parser(prog_name) parser.add_argument( '--filter', dest='filter', metavar='', help=_('Filter parameters to apply on returned resource types. ' 'This can be specified multiple times. It can be any of ' 'name, version or support_status'), action='append' ) parser.add_argument( '--long', default=False, action='store_true', help=_('Show resource types with corresponding description of ' 'each resource type.') ) 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_resourcetypes(heat_client, parsed_args) def _list_resourcetypes(heat_client, parsed_args): resource_types = heat_client.resource_types.list( filters=heat_utils.format_parameters(parsed_args.filter), with_description=parsed_args.long ) if parsed_args.long: columns = ['Resource Type', 'Description'] rows = sorted([r.resource_type, r.description] for r in resource_types) else: columns = ['Resource Type'] rows = sorted([r.resource_type] for r in resource_types) return columns, rows python-heatclient-2.1.0/heatclient/osc/v1/service.py0000664000175000017500000000255113644132026022443 0ustar zuulzuul00000000000000# 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 Service action implementations""" import logging from osc_lib.command import command from osc_lib import utils class ListService(command.Lister): """List the Heat engines.""" log = logging.getLogger(__name__ + ".ListService") def get_parser(self, prog_name): parser = super(ListService, self).get_parser(prog_name) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) heat_client = self.app.client_manager.orchestration columns = ['Hostname', 'Binary', 'Engine ID', 'Host', 'Topic', 'Updated At', 'Status'] services = heat_client.services.list() return ( columns, (utils.get_item_properties(s, columns) for s in services) ) python-heatclient-2.1.0/heatclient/osc/v1/resource.py0000664000175000017500000002357713644132026022645 0ustar zuulzuul00000000000000# 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 import six from six.moves.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='', help=_('Name or ID of stack to query') ) parser.add_argument( 'resource', metavar='', help=_('Name of resource') ) parser.add_argument( '--with-attr', metavar='', 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='', 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='', type=int, help=_('Depth of nested stacks from which to display resources') ) parser.add_argument( '--filter', metavar='', 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='', help=_('Stack to display (name or ID)'), ) parser.add_argument( 'resource', metavar='', 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(six.itervalues(metadata)) columns = list(six.iterkeys(metadata)) 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='', help=_('Name or ID of stack the resource belongs to'), ) parser.add_argument( 'resource', metavar='', help=_('Name of the resoure to signal'), ) parser.add_argument( '--data', metavar='', help=_('JSON Data to send to the signal handler') ) parser.add_argument( '--data-file', metavar='', 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='', help=_('Name or ID of stack the resource belongs to') ) parser.add_argument( 'resource', metavar='', 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}) python-heatclient-2.1.0/heatclient/osc/v1/template.py0000664000175000017500000001370513644132026022621 0ustar zuulzuul00000000000000# 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. # # Copyright 2015 IBM Corp. import logging from osc_lib.command import command from osc_lib import utils import six from heatclient._i18n import _ from heatclient.common import format_utils from heatclient.common import http from heatclient.common import template_utils from heatclient.common import utils as heat_utils from heatclient import exc class VersionList(command.Lister): """List the available template versions.""" log = logging.getLogger(__name__ + '.VersionList') def take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) client = self.app.client_manager.orchestration versions = client.template_versions.list() try: versions[1].aliases def format_alias(aliases): return ','.join(aliases) fields = ['Version', 'Type', 'Aliases'] formatters = {'Aliases': format_alias} except AttributeError: fields = ['Version', 'Type'] formatters = None items = (utils.get_item_properties(s, fields, formatters=formatters) for s in versions) return (fields, items) class FunctionList(command.Lister): """List the available functions.""" log = logging.getLogger(__name__ + '.FunctionList') def get_parser(self, prog_name): parser = super(FunctionList, self).get_parser(prog_name) parser.add_argument( 'template_version', metavar='', help=_('Template version to get the functions for') ) parser.add_argument( '--with_conditions', default=False, action='store_true', help=_('Show condition functions for template.') ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)', parsed_args) client = self.app.client_manager.orchestration version = parsed_args.template_version try: functions = client.template_versions.get( version, with_condition_func=parsed_args.with_conditions) except exc.HTTPNotFound: msg = _('Template version not found: %s') % version raise exc.CommandError(msg) fields = ['Functions', 'Description'] return ( fields, (utils.get_item_properties(s, fields) for s in functions) ) class Validate(format_utils.YamlFormat): """Validate a template""" log = logging.getLogger(__name__ + ".Validate") def get_parser(self, prog_name): parser = super(Validate, self).get_parser(prog_name) parser.add_argument( '-e', '--environment', metavar='', action='append', help=_('Path to the environment. Can be specified multiple times') ) parser.add_argument( '--show-nested', action='store_true', help=_('Resolve parameters from nested templates as well') ) parser.add_argument( '--parameter', metavar='', action='append', help=_('Parameter values used to create the stack. This can be ' 'specified multiple times') ) parser.add_argument( '-s', '--files-container', metavar='', help=_('Swift files container name. Local files other than ' 'root template would be ignored. If other files are not ' 'found in swift, heat engine would raise an error.') ) parser.add_argument( '--ignore-errors', metavar='', help=_('List of heat errors to ignore') ) parser.add_argument( '-t', '--template', metavar='