# -*- coding: utf-8 -*- # Copyright 2016 Mirantis, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import abc from cliff import command import six from packetary.cli.commands.utils import make_display_attr_getter from packetary.cli.commands.utils import read_from_file from packetary import api @six.add_metaclass(abc.ABCMeta) class BaseRepoCommand(command.Command): """Super class for packetary commands.""" @property def stdout(self): """Shortcut for self.app.stdout.""" return self.app.stdout def get_parser(self, prog_name): """Specifies common options.""" parser = super(BaseRepoCommand, self).get_parser(prog_name) parser.add_argument( '-t', '--type', type=str, choices=['deb', 'rpm'], metavar='TYPE', default='deb', help='The type of repository.') parser.add_argument( '-a', '--arch', type=str, choices=["x86_64", "i386"], metavar='ARCHITECTURE', default="x86_64", help='The target architecture.') return parser def take_action(self, parsed_args): """See the Command.take_action. :param parsed_args: the command-line arguments :return: the result of take_repo_action :rtype: object """ return self.take_repo_action( api.RepositoryApi.create( self.app_args, parsed_args.type, parsed_args.arch ), parsed_args ) @abc.abstractmethod def take_repo_action(self, repo_api, parsed_args): """Takes action on repository. :param repo_api: the RepositoryApi instance :param parsed_args: the command-line arguments :return: the action result """ class RepositoriesMixin(object): def get_parser(self, prog_name): """Specifies common options.""" parser = super(RepositoriesMixin, self).get_parser(prog_name) parser.add_argument( '-r', '--repositories', dest='repositories', type=read_from_file, metavar='FILENAME', required=True, help="The path to file with list of repositories." "See documentation about format." ) return parser class PackagesMixin(object): """Added arguments to declare list of packages.""" def get_parser(self, prog_name): parser = super(PackagesMixin, self).get_parser(prog_name) parser.add_argument( "-R", "--requirements", dest='requirements', type=read_from_file, metavar='FILENAME', help="The path to file with list of packages." "See documentation about format." ) return parser class BaseProduceOutputCommand(BaseRepoCommand): columns = None def get_parser(self, prog_name): parser = super(BaseProduceOutputCommand, self).get_parser(prog_name) group = parser.add_argument_group( title='output formatter', description='output formatter options', ) group.add_argument( '-c', '--column', nargs='+', choices=self.columns, dest='columns', metavar='COLUMN', default=[], help='Space separated list of columns to include.', ) group.add_argument( '-s', '--sort-columns', type=str, nargs='+', choices=self.columns, metavar='SORT_COLUMN', default=[self.columns[0]], help='Space separated list of keys for sorting ' 'the data.' ) group.add_argument( '--sep', type=six.text_type, metavar='ROW SEPARATOR', default=six.text_type('; '), help='The row separator.' ) return parser def produce_output(self, parsed_args, data): indexes = dict( (c, i) for i, c in enumerate(self.columns) ) sort_index = [indexes[c] for c in parsed_args.sort_columns] if isinstance(data, list): data.sort(key=lambda x: [x[i] for i in sort_index]) else: data = sorted(data, key=lambda x: [x[i] for i in sort_index]) if parsed_args.columns: include_index = [ indexes[c] for c in parsed_args.columns ] data = ((row[i] for i in include_index) for row in data) columns = parsed_args.columns else: columns = self.columns stdout = self.stdout sep = parsed_args.sep # header stdout.write("# ") stdout.write(sep.join(columns)) stdout.write("\n") for row in data: stdout.write(sep.join(row)) stdout.write("\n") def run(self, parsed_args): # Use custom output producer. # cliff.lister with default formatters does not work # with large arrays of data, because it does not support streaming # TODO(implement custom formatter) formatter = make_display_attr_getter(self.columns) data = six.moves.map(formatter, self.take_action(parsed_args)) self.produce_output(parsed_args, data) return 0 @abc.abstractmethod def take_repo_action(self, driver, parsed_args): """See Command.take_repo_action."""