packetary/packetary/cli/commands/base.py

251 lines
7.4 KiB
Python

# -*- 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 BaseCommand(command.Command):
"""Super class for packetary commands."""
@property
def stdout(self):
"""Shortcut for self.app.stdout."""
return self.app.stdout
class BaseRepoCommand(BaseCommand):
"""Class for repo commands."""
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", "aarch64"],
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."""
class BasePackagingCommand(BaseCommand):
def get_parser(self, prog_name):
"""Specifies common options."""
parser = super(BasePackagingCommand, self).get_parser(prog_name)
parser.add_argument(
'-t', '--type',
required=True,
metavar='TYPE',
help='The type of package.'
)
parser.add_argument(
'-C', '--driver-config',
required=False,
metavar='PATH',
help='The path to configuration file for used driver.'
)
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_package_action(
api.PackagingApi.create(
self.app_args, parsed_args.type, parsed_args.driver_config
),
parsed_args
)
def take_package_action(self, packaging_api, parsed_args):
"""Takes action with package(s).
:param packaging_api: the RepositoryApi instance
:param parsed_args: the command-line arguments
:return: the action result
"""