# 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 config action implementations""" import logging from osc_lib.command import command from osc_lib import exceptions as exc from osc_lib import utils from urllib import request import yaml from heatclient._i18n import _ from heatclient.common import format_utils from heatclient.common import template_format from heatclient.common import utils as heat_utils from heatclient import exc as heat_exc class DeleteConfig(command.Command): """Delete software configs""" log = logging.getLogger(__name__ + ".DeleteConfig") def get_parser(self, prog_name): parser = super(DeleteConfig, self).get_parser(prog_name) parser.add_argument( 'config', metavar='', nargs='+', help=_('IDs of the software configs to delete') ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) heat_client = self.app.client_manager.orchestration return _delete_config(heat_client, parsed_args) def _delete_config(heat_client, args): failure_count = 0 for config_id in args.config: try: heat_client.software_configs.delete( config_id=config_id) except Exception as e: if isinstance(e, heat_exc.HTTPNotFound): print(_('Software config with ID %s not found') % config_id) failure_count += 1 continue if failure_count: raise exc.CommandError(_('Unable to delete %(count)s of the ' '%(total)s software configs.') % {'count': failure_count, 'total': len(args.config)}) class ListConfig(command.Lister): """List software configs""" log = logging.getLogger(__name__ + ".ListConfig") def get_parser(self, prog_name): parser = super(ListConfig, self).get_parser(prog_name) parser.add_argument( '--limit', metavar='', type=int, help=_('Limit the number of configs returned') ) parser.add_argument( '--marker', metavar='', help=_('Return configs that appear after the given config ID') ) 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_config(heat_client, parsed_args) def _list_config(heat_client, args): kwargs = {} if args.limit: kwargs['limit'] = args.limit if args.marker: kwargs['marker'] = args.marker scs = heat_client.software_configs.list(**kwargs) columns = ['id', 'name', 'group', 'creation_time'] return (columns, (utils.get_item_properties(s, columns) for s in scs)) class CreateConfig(format_utils.JsonFormat): """Create software config""" log = logging.getLogger(__name__ + ".CreateConfig") def get_parser(self, prog_name): parser = super(CreateConfig, self).get_parser(prog_name) parser.add_argument( 'name', metavar='', help=_('Name of the software config to create') ) parser.add_argument( '--config-file', metavar='', help=_('Path to JSON/YAML containing map defining ' ', , and ') ) parser.add_argument( '--definition-file', metavar='', help=_('Path to software config script/data') ) parser.add_argument( '--group', metavar='', default='Heat::Ungrouped', help=_('Group name of tool expected by the software config') ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) heat_client = self.app.client_manager.orchestration return _create_config(heat_client, parsed_args) def _create_config(heat_client, args): config = { 'group': args.group, 'config': '' } defn = {} if args.definition_file: defn_url = heat_utils.normalise_file_path_to_url( args.definition_file) defn_raw = request.urlopen(defn_url).read() or '{}' defn = yaml.load(defn_raw, Loader=template_format.yaml_loader) config['inputs'] = defn.get('inputs', []) config['outputs'] = defn.get('outputs', []) config['options'] = defn.get('options', {}) if args.config_file: config_url = heat_utils.normalise_file_path_to_url( args.config_file) config['config'] = request.urlopen(config_url).read() # build a mini-template with a config resource and validate it validate_template = { 'heat_template_version': '2013-05-23', 'resources': { args.name: { 'type': 'OS::Heat::SoftwareConfig', 'properties': config } } } heat_client.stacks.validate(template=validate_template) config['name'] = args.name sc = heat_client.software_configs.create(**config).to_dict() rows = list(sc.values()) columns = list(sc.keys()) return columns, rows class ShowConfig(format_utils.YamlFormat): """Show software config details""" log = logging.getLogger(__name__ + ".ShowConfig") def get_parser(self, prog_name): parser = super(ShowConfig, self).get_parser(prog_name) parser.add_argument( 'config', metavar='', help=_('ID of the config') ) parser.add_argument( '--config-only', default=False, action="store_true", help=_('Only display the value of the property.') ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) heat_client = self.app.client_manager.orchestration return _show_config(heat_client, config_id=parsed_args.config, config_only=parsed_args.config_only) def _show_config(heat_client, config_id, config_only): try: sc = heat_client.software_configs.get(config_id=config_id) except heat_exc.HTTPNotFound: raise exc.CommandError(_('Configuration not found: %s') % config_id) columns = None rows = None if config_only: print(sc.config) else: columns = ( 'id', 'name', 'group', 'config', 'inputs', 'outputs', 'options', 'creation_time', ) rows = utils.get_dict_properties(sc.to_dict(), columns) return columns, rows