diff options
author | Jhon Honce <jhonce@redhat.com> | 2018-07-12 19:26:14 -0700 |
---|---|---|
committer | Jhon Honce <jhonce@redhat.com> | 2018-07-13 12:50:12 -0700 |
commit | 74ccd9ce5f29a1df4ffe70b4d8bd00c29d5d9d15 (patch) | |
tree | 75ba256d70545d79aa61d7c57c20df886be1555f /contrib/python/cmd | |
parent | 44b523c946c88e540b50d7ba59f441b5f8e0bad0 (diff) | |
download | podman-74ccd9ce5f29a1df4ffe70b4d8bd00c29d5d9d15.tar.gz podman-74ccd9ce5f29a1df4ffe70b4d8bd00c29d5d9d15.tar.bz2 podman-74ccd9ce5f29a1df4ffe70b4d8bd00c29d5d9d15.zip |
Update python directories to better support setup.py
Signed-off-by: Jhon Honce <jhonce@redhat.com>
Diffstat (limited to 'contrib/python/cmd')
-rw-r--r-- | contrib/python/cmd/lib/__init__.py | 5 | ||||
-rw-r--r-- | contrib/python/cmd/lib/action_base.py | 80 | ||||
-rw-r--r-- | contrib/python/cmd/lib/actions/__init__.py | 7 | ||||
-rw-r--r-- | contrib/python/cmd/lib/actions/images_action.py | 88 | ||||
-rw-r--r-- | contrib/python/cmd/lib/actions/ps_action.py | 76 | ||||
-rw-r--r-- | contrib/python/cmd/lib/actions/rm_action.py | 51 | ||||
-rw-r--r-- | contrib/python/cmd/lib/actions/rmi_action.py | 50 | ||||
-rw-r--r-- | contrib/python/cmd/lib/future_abstract.py | 29 | ||||
-rw-r--r-- | contrib/python/cmd/lib/report.py | 67 | ||||
-rwxr-xr-x | contrib/python/cmd/pydman.py | 248 |
10 files changed, 0 insertions, 701 deletions
diff --git a/contrib/python/cmd/lib/__init__.py b/contrib/python/cmd/lib/__init__.py deleted file mode 100644 index db0f640b1..000000000 --- a/contrib/python/cmd/lib/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Remote podman client support library.""" -from .action_base import AbstractActionBase -from .report import Report, ReportColumn - -__all__ = ['AbstractActionBase', 'Report', 'ReportColumn'] diff --git a/contrib/python/cmd/lib/action_base.py b/contrib/python/cmd/lib/action_base.py deleted file mode 100644 index bafddea03..000000000 --- a/contrib/python/cmd/lib/action_base.py +++ /dev/null @@ -1,80 +0,0 @@ -"""Base class for all actions of remote client.""" -import abc -from functools import lru_cache - -import podman - - -class AbstractActionBase(abc.ABC): - """Base class for all actions of remote client.""" - - @classmethod - @abc.abstractmethod - def subparser(cls, parser): - """Define parser for this action. Subclasses must implement. - - API: - Use set_defaults() to set attributes "klass" and "method". These will - be invoked as klass(parsed_args).method() - """ - parser.add_argument( - '--all', - action='store_true', - help=('list all items.' - ' (default: no-op, included for compatibility.)')) - parser.add_argument( - '--no-trunc', - '--notruncate', - action='store_false', - dest='truncate', - default=True, - help='Display extended information. (default: False)') - parser.add_argument( - '--noheading', - action='store_false', - dest='heading', - default=True, - help=('Omit the table headings from the output.' - ' (default: False)')) - parser.add_argument( - '--quiet', - action='store_true', - help='List only the IDs. (default: %(default)s)') - - def __init__(self, args): - """Construct class.""" - self._args = args - - @property - def remote_uri(self): - """URI for remote side of connection.""" - return self._args.remote_uri - - @property - def local_uri(self): - """URI for local side of connection.""" - return self._args.local_uri - - @property - def identity_file(self): - """Key for authenication.""" - return self._args.identity_file - - @property - @lru_cache(maxsize=1) - def client(self): - """Podman remote client for communicating.""" - return podman.Client( - uri=self.local_uri, - remote_uri=self.remote_uri, - identity_file=self.identity_file) - - def __repr__(self): - """Compute the “official” string representation of object.""" - return ("{}(local_uri='{}', remote_uri='{}'," - " identity_file='{}')").format( - self.__class__, - self.local_uri, - self.remote_uri, - self.identity_file, - ) diff --git a/contrib/python/cmd/lib/actions/__init__.py b/contrib/python/cmd/lib/actions/__init__.py deleted file mode 100644 index cdc58b6ab..000000000 --- a/contrib/python/cmd/lib/actions/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Module to export all the podman subcommands.""" -from .images_action import Images -from .ps_action import Ps -from .rm_action import Rm -from .rmi_action import Rmi - -__all__ = ['Images', 'Ps', 'Rm', 'Rmi'] diff --git a/contrib/python/cmd/lib/actions/images_action.py b/contrib/python/cmd/lib/actions/images_action.py deleted file mode 100644 index 74c77edbb..000000000 --- a/contrib/python/cmd/lib/actions/images_action.py +++ /dev/null @@ -1,88 +0,0 @@ -"""Remote client commands dealing with images.""" -import operator -from collections import OrderedDict - -import humanize -import podman - -from .. import AbstractActionBase, Report, ReportColumn - - -class Images(AbstractActionBase): - """Class for Image manipulation.""" - - @classmethod - def subparser(cls, parent): - """Add Images commands to parent parser.""" - parser = parent.add_parser('images', help='list images') - super().subparser(parser) - parser.add_argument( - '--sort', - choices=['created', 'id', 'repository', 'size', 'tag'], - default='created', - type=str.lower, - help=('Change sort ordered of displayed images.' - ' (default: %(default)s)')) - - group = parser.add_mutually_exclusive_group() - group.add_argument( - '--digests', - action='store_true', - help='Include digests with images. (default: %(default)s)') - parser.set_defaults(klass=cls, method='list') - - def __init__(self, args): - """Construct Images class.""" - super().__init__(args) - - self.columns = OrderedDict({ - 'name': - ReportColumn('name', 'REPOSITORY', 40), - 'tag': - ReportColumn('tag', 'TAG', 10), - 'id': - ReportColumn('id', 'IMAGE ID', 12), - 'created': - ReportColumn('created', 'CREATED', 12), - 'size': - ReportColumn('size', 'SIZE', 8), - 'repoDigests': - ReportColumn('repoDigests', 'DIGESTS', 35), - }) - - def list(self): - """List images.""" - images = sorted( - self.client.images.list(), - key=operator.attrgetter(self._args.sort)) - if len(images) == 0: - return 0 - - rows = list() - for image in images: - fields = dict(image) - fields.update({ - 'created': - humanize.naturaldate(podman.datetime_parse(image.created)), - 'size': - humanize.naturalsize(int(image.size)), - 'repoDigests': - ' '.join(image.repoDigests), - }) - - for r in image.repoTags: - name, tag = r.split(':', 1) - fields.update({ - 'name': name, - 'tag': tag, - }) - rows.append(fields) - - if not self._args.digests: - del self.columns['repoDigests'] - - with Report(self.columns, heading=self._args.heading) as report: - report.layout( - rows, self.columns.keys(), truncate=self._args.truncate) - for row in rows: - report.row(**row) diff --git a/contrib/python/cmd/lib/actions/ps_action.py b/contrib/python/cmd/lib/actions/ps_action.py deleted file mode 100644 index 9fc3a155b..000000000 --- a/contrib/python/cmd/lib/actions/ps_action.py +++ /dev/null @@ -1,76 +0,0 @@ -"""Remote client commands dealing with containers.""" -import operator -from collections import OrderedDict - -import humanize -import podman - -from .. import AbstractActionBase, Report, ReportColumn - - -class Ps(AbstractActionBase): - """Class for Container manipulation.""" - - @classmethod - def subparser(cls, parent): - """Add Images command to parent parser.""" - parser = parent.add_parser('ps', help='list containers') - super().subparser(parser) - parser.add_argument( - '--sort', - choices=[ - 'createdat', 'id', 'image', 'names', 'runningfor', 'size', - 'status' - ], - default='createdat', - type=str.lower, - help=('Change sort ordered of displayed containers.' - ' (default: %(default)s)')) - parser.set_defaults(klass=cls, method='list') - - def __init__(self, args): - """Construct Ps class.""" - super().__init__(args) - - self.columns = OrderedDict({ - 'id': - ReportColumn('id', 'CONTAINER ID', 14), - 'image': - ReportColumn('image', 'IMAGE', 30), - 'command': - ReportColumn('column', 'COMMAND', 20), - 'createdat': - ReportColumn('createdat', 'CREATED', 12), - 'status': - ReportColumn('status', 'STATUS', 10), - 'ports': - ReportColumn('ports', 'PORTS', 28), - 'names': - ReportColumn('names', 'NAMES', 18) - }) - - def list(self): - """List containers.""" - # TODO: Verify sorting on dates and size - ctnrs = sorted( - self.client.containers.list(), - key=operator.attrgetter(self._args.sort)) - if len(ctnrs) == 0: - return 0 - - rows = list() - for ctnr in ctnrs: - fields = dict(ctnr) - fields.update({ - 'command': - ' '.join(ctnr.command), - 'createdat': - humanize.naturaldate(podman.datetime_parse(ctnr.createdat)), - }) - rows.append(fields) - - with Report(self.columns, heading=self._args.heading) as report: - report.layout( - rows, self.columns.keys(), truncate=self._args.truncate) - for row in rows: - report.row(**row) diff --git a/contrib/python/cmd/lib/actions/rm_action.py b/contrib/python/cmd/lib/actions/rm_action.py deleted file mode 100644 index 7595fee6a..000000000 --- a/contrib/python/cmd/lib/actions/rm_action.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Remote client command for deleting containers.""" -import sys - -import podman - -from .. import AbstractActionBase - - -class Rm(AbstractActionBase): - """Class for removing containers from storage.""" - - @classmethod - def subparser(cls, parent): - """Add Rm command to parent parser.""" - parser = parent.add_parser('rm', help='delete container(s)') - parser.add_argument( - '-f', - '--force', - action='store_true', - help=('force delete of running container(s).' - ' (default: %(default)s)')) - parser.add_argument( - 'targets', nargs='*', help='container id(s) to delete') - parser.set_defaults(klass=cls, method='remove') - - def __init__(self, args): - """Construct Rm class.""" - super().__init__(args) - if len(args.targets) < 1: - raise ValueError('You must supply at least one container id' - ' or name to be deleted.') - - def remove(self): - """Remove container(s).""" - for id in self._args.targets: - try: - ctnr = self.client.containers.get(id) - ctnr.remove(self._args.force) - print(id) - except podman.ContainerNotFound as e: - sys.stdout.flush() - print( - 'Container {} not found.'.format(e.name), - file=sys.stderr, - flush=True) - except podman.ErrorOccurred as e: - sys.stdout.flush() - print( - '{}'.format(e.reason).capitalize(), - file=sys.stderr, - flush=True) diff --git a/contrib/python/cmd/lib/actions/rmi_action.py b/contrib/python/cmd/lib/actions/rmi_action.py deleted file mode 100644 index db59fe030..000000000 --- a/contrib/python/cmd/lib/actions/rmi_action.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Remote client command for deleting images.""" -import sys - -import podman - -from .. import AbstractActionBase - - -class Rmi(AbstractActionBase): - """Clas for removing images from storage.""" - - @classmethod - def subparser(cls, parent): - """Add Rmi command to parent parser.""" - parser = parent.add_parser('rmi', help='delete image(s)') - parser.add_argument( - '-f', - '--force', - action='store_true', - help=('force delete of image(s) and associated containers.' - ' (default: %(default)s)')) - parser.add_argument('targets', nargs='*', help='image id(s) to delete') - parser.set_defaults(klass=cls, method='remove') - - def __init__(self, args): - """Construct Rmi class.""" - super().__init__(args) - if len(args.targets) < 1: - raise ValueError('You must supply at least one image id' - ' or name to be deleted.') - - def remove(self): - """Remove image(s).""" - for id in self._args.targets: - try: - img = self.client.images.get(id) - img.remove(self._args.force) - print(id) - except podman.ImageNotFound as e: - sys.stdout.flush() - print( - 'Image {} not found.'.format(e.name), - file=sys.stderr, - flush=True) - except podman.ErrorOccurred as e: - sys.stdout.flush() - print( - '{}'.format(e.reason).capitalize(), - file=sys.stderr, - flush=True) diff --git a/contrib/python/cmd/lib/future_abstract.py b/contrib/python/cmd/lib/future_abstract.py deleted file mode 100644 index 75a1d42db..000000000 --- a/contrib/python/cmd/lib/future_abstract.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Utilities for with-statement contexts. See PEP 343.""" - -import abc - -import _collections_abc - -try: - from contextlib import AbstractContextManager -except ImportError: - # Copied from python3.7 library as "backport" - class AbstractContextManager(abc.ABC): - """An abstract base class for context managers.""" - - def __enter__(self): - """Return `self` upon entering the runtime context.""" - return self - - @abc.abstractmethod - def __exit__(self, exc_type, exc_value, traceback): - """Raise any exception triggered within the runtime context.""" - return None - - @classmethod - def __subclasshook__(cls, C): - """Check whether subclass is considered a subclass of this ABC.""" - if cls is AbstractContextManager: - return _collections_abc._check_methods(C, "__enter__", - "__exit__") - return NotImplemented diff --git a/contrib/python/cmd/lib/report.py b/contrib/python/cmd/lib/report.py deleted file mode 100644 index 25fe2ae0d..000000000 --- a/contrib/python/cmd/lib/report.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Report Manager.""" -import sys -from collections import namedtuple - -from .future_abstract import AbstractContextManager - - -class ReportColumn(namedtuple('ReportColumn', 'key display width default')): - """Hold attributes of output column.""" - - __slots__ = () - - def __new__(cls, key, display, width, default=None): - """Add defaults for attributes.""" - return super(ReportColumn, cls).__new__(cls, key, display, width, - default) - - -class Report(AbstractContextManager): - """Report Manager.""" - - def __init__(self, columns, heading=True, epilog=None, file=sys.stdout): - """Construct Report. - - columns is a mapping for named fields to column headings. - headers True prints headers on table. - epilog will be printed when the report context is closed. - """ - self._columns = columns - self._file = file - self._heading = heading - self.epilog = epilog - self._format = None - - def row(self, **fields): - """Print row for report.""" - if self._heading: - hdrs = {k: v.display for (k, v) in self._columns.items()} - print(self._format.format(**hdrs), flush=True, file=self._file) - self._heading = False - fields = {k: str(v) for k, v in fields.items()} - print(self._format.format(**fields)) - - def __exit__(self, exc_type, exc_value, traceback): - """Leave Report context and print epilog if provided.""" - if self.epilog: - print(self.epilog, flush=True, file=self._file) - - def layout(self, iterable, keys, truncate=True): - """Use data and headings build format for table to fit.""" - format = [] - - for key in keys: - value = max(map(lambda x: len(str(x.get(key, ''))), iterable)) - # print('key', key, 'value', value) - - if truncate: - row = self._columns.get( - key, ReportColumn(key, key.upper(), len(key))) - if value < row.width: - step = row.width if value == 0 else value - value = max(len(key), step) - elif value > row.width: - value = row.width if row.width != 0 else value - - format.append('{{{0}:{1}.{1}}}'.format(key, value)) - self._format = ' '.join(format) diff --git a/contrib/python/cmd/pydman.py b/contrib/python/cmd/pydman.py deleted file mode 100755 index 5008c706d..000000000 --- a/contrib/python/cmd/pydman.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env python3 -"""Remote podman client.""" - -import argparse -import curses -import getpass -import inspect -import logging -import os -import sys - -import pkg_resources - -import lib.actions -import pytoml - -assert lib.actions # silence pyflakes - -# TODO: setup.py and obtain __version__ from rpm.spec -try: - __version__ = pkg_resources.get_distribution('pydman').version -except Exception: - __version__ = '0.0.0' - - -class HelpFormatter(argparse.RawDescriptionHelpFormatter): - """Set help width to screen size.""" - - def __init__(self, *args, **kwargs): - """Construct HelpFormatter using screen width.""" - if 'width' not in kwargs: - kwargs['width'] = 80 - try: - height, width = curses.initscr().getmaxyx() - kwargs['width'] = width - finally: - curses.endwin() - super().__init__(*args, **kwargs) - - -class PodmanArgumentParser(argparse.ArgumentParser): - """Default remote podman configuration.""" - - def __init__(self, **kwargs): - """Construct the parser.""" - kwargs['add_help'] = True - kwargs['allow_abbrev'] = True - kwargs['description'] = __doc__ - kwargs['formatter_class'] = HelpFormatter - - super().__init__(**kwargs) - - def initialize_parser(self): - """Initialize parser without causing recursion meltdown.""" - self.add_argument( - '--version', - action='version', - version='%(prog)s v. ' + __version__) - self.add_argument( - '--log-level', - choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], - default='INFO', - type=str.upper, - help='set logging level for events. (default: %(default)s)', - ) - self.add_argument( - '--run-dir', - help=('directory to place local socket bindings.' - ' (default: XDG_RUNTIME_DIR)')) - self.add_argument( - '--user', - help=('Authenicating user on remote host.' - ' (default: {})').format(getpass.getuser())) - self.add_argument( - '--host', help='name of remote host. (default: None)') - self.add_argument( - '--remote-socket-path', - help=('path of podman socket on remote host' - ' (default: /run/podman/io.projectatomic.podman)')) - self.add_argument( - '--identity-file', - help=('path to ssh identity file. (default: ~/.ssh/id_rsa)')) - self.add_argument( - '--config', - default='/etc/containers/podman_client.conf', - dest='config_file', - help='path of configuration file. (default: %(default)s)') - - actions_parser = self.add_subparsers( - dest='subparser_name', help='actions') - - for name, obj in inspect.getmembers( - sys.modules['lib.actions'], - lambda member: inspect.isclass(member)): - if hasattr(obj, 'subparser'): - try: - obj.subparser(actions_parser) - except NameError as e: - logging.critical(e) - logging.warning( - 'See subparser configuration for Class "{}"'.format( - name)) - sys.exit(3) - - def parse_args(self, args=None, namespace=None): - """Parse command line arguments, backed by env var and config_file.""" - self.initialize_parser() - cooked = super().parse_args(args, namespace) - return self.resolve_configuration(cooked) - - def resolve_configuration(self, args): - """Find and fill in any arguments not passed on command line.""" - try: - # Configuration file optionall, arguments may be provided elsewhere - with open(args.config_file, 'r') as stream: - config = pytoml.load(stream) - except OSError: - logging.info( - 'Failed to read: {}'.format(args.config_file), - exc_info=args.log_level == logging.DEBUG) - config = {'default': {}} - else: - if 'default' not in config: - config['default'] = {} - - def resolve(name, value): - if value: - setattr(args, name, value) - return value - self.error('Required argument "%s" is not configured.' % name) - - xdg = os.path.join(os.environ['XDG_RUNTIME_DIR'], 'podman') \ - if os.environ.get('XDG_RUNTIME_DIR') else None - - resolve( - 'run_dir', - getattr(args, 'run_dir') - or os.environ.get('RUN_DIR') - or config['default'].get('run_dir') - or xdg - or '/tmp/podman' if os.path.isdir('/tmp') else None - ) # yapf: disable - - args.local_socket_path = os.path.join(args.run_dir, "podman.socket") - - resolve( - 'host', - getattr(args, 'host') - or os.environ.get('HOST') - or config['default'].get('host') - ) # yapf:disable - - resolve( - 'user', - getattr(args, 'user') - or os.environ.get('USER') - or config['default'].get('user') - or getpass.getuser() - ) # yapf:disable - - resolve( - 'remote_socket_path', - getattr(args, 'remote_socket_path') - or os.environ.get('REMOTE_SOCKET_PATH') - or config['default'].get('remote_socket_path') - or '/run/podman/io.projectatomic.podman' - ) # yapf:disable - - resolve( - 'identity_file', - getattr(args, 'identity_file') - or os.environ.get('IDENTITY_FILE') - or config['default'].get('identity_file') - or os.path.expanduser('~{}/.ssh/id_rsa'.format(args.user)) - ) # yapf:disable - - args.local_uri = "unix:{}".format(args.local_socket_path) - args.remote_uri = "ssh://{}@{}{}".format(args.user, args.host, - args.remote_socket_path) - return args - - def exit(self, status=0, message=None): - """Capture message and route to logger.""" - if message: - log = logging.info if status == 0 else logging.error - log(message) - super().exit(status) - - def error(self, message): - """Capture message and route to logger.""" - logging.error('{}: {}'.format(self.prog, message)) - logging.error("Try '{} --help' for more information.".format( - self.prog)) - super().exit(2) - - -if __name__ == '__main__': - # Setup logging so we use stderr and can change logging level later - # Do it now before there is any chance of a default setup. - log = logging.getLogger() - fmt = logging.Formatter('%(asctime)s | %(levelname)-8s | %(message)s', - '%Y-%m-%d %H:%M:%S %Z') - stderr = logging.StreamHandler(stream=sys.stderr) - stderr.setFormatter(fmt) - log.addHandler(stderr) - log.setLevel(logging.INFO) - - parser = PodmanArgumentParser() - args = parser.parse_args() - - log.setLevel(args.log_level) - logging.debug('Logging initialized at level {}'.format( - logging.getLevelName(logging.getLogger().getEffectiveLevel()))) - - def istraceback(): - """Add traceback when logging events.""" - return log.getEffectiveLevel() == logging.DEBUG - - try: - if not os.path.exists(args.run_dir): - os.makedirs(args.run_dir) - except PermissionError as e: - logging.critical(e, exc_info=istraceback()) - sys.exit(6) - - # Klass(args).method() are setup by the sub-command's parser - returncode = None - try: - obj = args.klass(args) - except Exception as e: - logging.critical(repr(e), exc_info=istraceback()) - logging.warning('See subparser "{}" configuration.'.format( - args.subparser_name)) - sys.exit(5) - - try: - returncode = getattr(obj, args.method)() - except AttributeError as e: - logging.critical(e, exc_info=istraceback()) - logging.warning('See subparser "{}" configuration.'.format( - args.subparser_name)) - returncode = 3 - except (ConnectionResetError, TimeoutError) as e: - logging.critical(e, exc_info=istraceback()) - logging.info('Review connection arguments for correctness.') - returncode = 4 - - sys.exit(0 if returncode is None else returncode) |