mirror of
https://github.com/containers/podman
synced 2024-10-20 17:23:30 +00:00
Add support for remote commands
* Add support for commit, export, inspect, kill, logs, mount, pause port commands * Refactored Report class to allow column lengths to be optionally driven by data * Refactored Ps class to truncate image names on the left vs right * Bug fixes Signed-off-by: Jhon Honce <jhonce@redhat.com> Closes: #1369 Approved by: rhatdan
This commit is contained in:
parent
de414c4354
commit
8245f09428
|
@ -9,7 +9,7 @@ host:
|
|||
ram: 8192
|
||||
cpus: 4
|
||||
required: true
|
||||
timeout: 90m
|
||||
timeout: 120m
|
||||
|
||||
tests:
|
||||
- CONTAINER_RUNTIME="podman" sh .papr_prepare.sh
|
||||
|
@ -35,7 +35,7 @@ extra-repos:
|
|||
|
||||
required: true
|
||||
|
||||
timeout: 90m
|
||||
timeout: 120m
|
||||
|
||||
tests:
|
||||
- sh .papr_prepare.sh
|
||||
|
@ -63,7 +63,7 @@ tests:
|
|||
- CONTAINER_RUNTIME="podman" sh .papr_prepare.sh
|
||||
required: false
|
||||
|
||||
timeout: 90m
|
||||
timeout: 120m
|
||||
context: "Fedora fedora/28/cloud Podman"
|
||||
|
||||
---
|
||||
|
|
|
@ -214,7 +214,7 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
|
|||
"""Retrieve container logs."""
|
||||
with self._client() as podman:
|
||||
results = podman.GetContainerLogs(self._id)
|
||||
yield from results
|
||||
yield from results['container']
|
||||
|
||||
|
||||
class Containers():
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""Remote podman client support library."""
|
||||
from pypodman.lib.action_base import AbstractActionBase
|
||||
from pypodman.lib.config import PodmanArgumentParser
|
||||
from pypodman.lib.report import (Report, ReportColumn)
|
||||
from pypodman.lib.report import Report, ReportColumn
|
||||
|
||||
__all__ = [
|
||||
'AbstractActionBase',
|
||||
|
|
|
@ -65,8 +65,7 @@ class AbstractActionBase(abc.ABC):
|
|||
def client(self):
|
||||
"""Podman remote client for communicating."""
|
||||
if self._args.host is None:
|
||||
return podman.Client(
|
||||
uri=self.local_uri)
|
||||
return podman.Client(uri=self.local_uri)
|
||||
return podman.Client(
|
||||
uri=self.local_uri,
|
||||
remote_uri=self.remote_uri,
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
"""Module to export all the podman subcommands."""
|
||||
from pypodman.lib.actions.attach_action import Attach
|
||||
from pypodman.lib.actions.commit_action import Commit
|
||||
from pypodman.lib.actions.create_action import Create
|
||||
from pypodman.lib.actions.export_action import Export
|
||||
from pypodman.lib.actions.images_action import Images
|
||||
from pypodman.lib.actions.inspect_action import Inspect
|
||||
from pypodman.lib.actions.kill_action import Kill
|
||||
from pypodman.lib.actions.logs_action import Logs
|
||||
from pypodman.lib.actions.mount_action import Mount
|
||||
from pypodman.lib.actions.pause_action import Pause
|
||||
from pypodman.lib.actions.port_action import Port
|
||||
from pypodman.lib.actions.ps_action import Ps
|
||||
from pypodman.lib.actions.pull_action import Pull
|
||||
from pypodman.lib.actions.rm_action import Rm
|
||||
|
@ -9,8 +17,16 @@ from pypodman.lib.actions.rmi_action import Rmi
|
|||
|
||||
__all__ = [
|
||||
'Attach',
|
||||
'Commit',
|
||||
'Create',
|
||||
'Export',
|
||||
'Images',
|
||||
'Inspect',
|
||||
'Kill',
|
||||
'Logs',
|
||||
'Mount',
|
||||
'Pause',
|
||||
'Port',
|
||||
'Ps',
|
||||
'Pull',
|
||||
'Rm',
|
||||
|
|
102
contrib/python/pypodman/pypodman/lib/actions/commit_action.py
Normal file
102
contrib/python/pypodman/pypodman/lib/actions/commit_action.py
Normal file
|
@ -0,0 +1,102 @@
|
|||
"""Remote client command for creating image from container."""
|
||||
import sys
|
||||
|
||||
import podman
|
||||
from pypodman.lib import AbstractActionBase
|
||||
|
||||
|
||||
class Commit(AbstractActionBase):
|
||||
"""Class for creating image from container."""
|
||||
|
||||
@classmethod
|
||||
def subparser(cls, parent):
|
||||
"""Add Commit command to parent parser."""
|
||||
parser = parent.add_parser(
|
||||
'commit', help='create image from container')
|
||||
parser.add_argument(
|
||||
'--author',
|
||||
help='Set the author for the committed image',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--change',
|
||||
'-c',
|
||||
choices=('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD',
|
||||
'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR'),
|
||||
action='append',
|
||||
type=str.upper,
|
||||
help='Apply the following possible changes to the created image',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--format',
|
||||
'-f',
|
||||
choices=('oci', 'docker'),
|
||||
default='oci',
|
||||
type=str.lower,
|
||||
help='Set the format of the image manifest and metadata',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--iidfile',
|
||||
metavar='PATH',
|
||||
help='Write the image ID to the file',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--message',
|
||||
'-m',
|
||||
help='Set commit message for committed image',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--pause',
|
||||
'-p',
|
||||
choices=('True', 'False'),
|
||||
default=True,
|
||||
type=bool,
|
||||
help='Pause the container when creating an image',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--quiet',
|
||||
'-q',
|
||||
help='Suppress output',
|
||||
)
|
||||
parser.add_argument(
|
||||
'container',
|
||||
nargs=1,
|
||||
help='container to use as source',
|
||||
)
|
||||
parser.add_argument(
|
||||
'image',
|
||||
nargs=1,
|
||||
help='image name to create',
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='commit')
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct Commit class."""
|
||||
super().__init__(args)
|
||||
if not args.container:
|
||||
raise ValueError('You must supply one container id'
|
||||
' or name to be used as source.')
|
||||
if not args.image:
|
||||
raise ValueError('You must supply one image id'
|
||||
' or name to be created.')
|
||||
|
||||
def commit(self):
|
||||
"""Create image from container."""
|
||||
try:
|
||||
try:
|
||||
ctnr = self.client.containers.get(self._args.container[0])
|
||||
ident = ctnr.commit(**self._args)
|
||||
print(ident)
|
||||
except podman.ContainerNotFound as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'Container {} not found.'.format(e.name),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'{}'.format(e.reason).capitalize(),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
|
@ -0,0 +1,60 @@
|
|||
"""Remote client command for export container filesystem to tarball."""
|
||||
import sys
|
||||
|
||||
import podman
|
||||
from pypodman.lib import AbstractActionBase
|
||||
|
||||
|
||||
class Export(AbstractActionBase):
|
||||
"""Class for exporting container filesystem to tarball."""
|
||||
|
||||
@classmethod
|
||||
def subparser(cls, parent):
|
||||
"""Add Export command to parent parser."""
|
||||
parser = parent.add_parser(
|
||||
'export', help='export container to tarball')
|
||||
parser.add_argument(
|
||||
'--output',
|
||||
'-o',
|
||||
metavar='PATH',
|
||||
nargs=1,
|
||||
help='Write to a file',
|
||||
)
|
||||
parser.add_argument(
|
||||
'container',
|
||||
nargs=1,
|
||||
help='container to use as source',
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='export')
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct Export class."""
|
||||
super().__init__(args)
|
||||
if not args.container:
|
||||
raise ValueError('You must supply one container id'
|
||||
' or name to be used as source.')
|
||||
|
||||
if not args.output:
|
||||
raise ValueError('You must supply one filename'
|
||||
' to be created as tarball using --output.')
|
||||
|
||||
def export(self):
|
||||
"""Create tarball from container filesystem."""
|
||||
try:
|
||||
try:
|
||||
ctnr = self.client.containers.get(self._args.container[0])
|
||||
ctnr.export(self._args.output[0])
|
||||
except podman.ContainerNotFound as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'Container {} not found.'.format(e.name),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'{}'.format(e.reason).capitalize(),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
|
@ -0,0 +1,90 @@
|
|||
"""Remote client command for inspecting podman objects."""
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import podman
|
||||
from pypodman.lib import AbstractActionBase
|
||||
|
||||
|
||||
class Inspect(AbstractActionBase):
|
||||
"""Class for inspecting podman objects."""
|
||||
|
||||
@classmethod
|
||||
def subparser(cls, parent):
|
||||
"""Add Inspect command to parent parser."""
|
||||
parser = parent.add_parser('inspect', help='inspect objects')
|
||||
parser.add_argument(
|
||||
'--type',
|
||||
'-t',
|
||||
choices=('all', 'container', 'image'),
|
||||
default='all',
|
||||
type=str.lower,
|
||||
help='Type of object to inspect',
|
||||
)
|
||||
parser.add_argument(
|
||||
'size',
|
||||
action='store_true',
|
||||
default=True,
|
||||
help='Display the total file size if the type is a container.'
|
||||
' Always True.')
|
||||
parser.add_argument(
|
||||
'objects',
|
||||
nargs='+',
|
||||
help='objects to inspect',
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='inspect')
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct Inspect class."""
|
||||
super().__init__(args)
|
||||
|
||||
def _get_container(self, ident):
|
||||
try:
|
||||
logging.debug("Get container %s", ident)
|
||||
ctnr = self.client.containers.get(ident)
|
||||
except podman.ContainerNotFound:
|
||||
pass
|
||||
else:
|
||||
return ctnr.inspect()
|
||||
|
||||
def _get_image(self, ident):
|
||||
try:
|
||||
logging.debug("Get image %s", ident)
|
||||
img = self.client.images.get(ident)
|
||||
except podman.ImageNotFound:
|
||||
pass
|
||||
else:
|
||||
return img.inspect()
|
||||
|
||||
def inspect(self):
|
||||
"""Inspect provided podman objects."""
|
||||
output = {}
|
||||
try:
|
||||
for ident in self._args.objects:
|
||||
obj = None
|
||||
|
||||
if self._args.type in ('all', 'container'):
|
||||
obj = self._get_container(ident)
|
||||
if obj is None and self._args.type in ('all', 'image'):
|
||||
obj = self._get_image(ident)
|
||||
|
||||
if obj is None:
|
||||
if self._args.type == 'container':
|
||||
msg = 'Container "{}" not found'.format(ident)
|
||||
elif self._args.type == 'image':
|
||||
msg = 'Image "{}" not found'.format(ident)
|
||||
else:
|
||||
msg = 'Object "{}" not found'.format(ident)
|
||||
print(msg, file=sys.stderr, flush=True)
|
||||
else:
|
||||
output.update(obj._asdict())
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'{}'.format(e.reason).capitalize(),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
||||
else:
|
||||
print(json.dumps(output, indent=2))
|
55
contrib/python/pypodman/pypodman/lib/actions/kill_action.py
Normal file
55
contrib/python/pypodman/pypodman/lib/actions/kill_action.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
"""Remote client command for signaling podman containers."""
|
||||
import signal
|
||||
import sys
|
||||
|
||||
import podman
|
||||
from pypodman.lib import AbstractActionBase
|
||||
|
||||
|
||||
class Kill(AbstractActionBase):
|
||||
"""Class for sending signal to main process in container."""
|
||||
|
||||
@classmethod
|
||||
def subparser(cls, parent):
|
||||
"""Add Kill command to parent parser."""
|
||||
parser = parent.add_parser('kill', help='signal container')
|
||||
parser.add_argument(
|
||||
'--signal',
|
||||
'-s',
|
||||
choices=range(1, signal.NSIG),
|
||||
metavar='[1,{}]'.format(signal.NSIG),
|
||||
default=9,
|
||||
help='Signal to send to the container. (Default: 9)')
|
||||
parser.add_argument(
|
||||
'containers',
|
||||
nargs='+',
|
||||
help='containers to signal',
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='kill')
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct Kill class."""
|
||||
super().__init__(args)
|
||||
|
||||
def kill(self):
|
||||
"""Signal provided containers."""
|
||||
try:
|
||||
for ident in self._args.containers:
|
||||
try:
|
||||
ctnr = self.client.containers.get(ident)
|
||||
ctnr.kill(self._args.signal)
|
||||
except podman.ContainerNotFound as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'Container "{}" not found'.format(e.name),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
else:
|
||||
print(ident)
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'{}'.format(e.reason).capitalize(),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
75
contrib/python/pypodman/pypodman/lib/actions/logs_action.py
Normal file
75
contrib/python/pypodman/pypodman/lib/actions/logs_action.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
"""Remote client command for retrieving container logs."""
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
from collections import deque
|
||||
|
||||
import podman
|
||||
from pypodman.lib import AbstractActionBase
|
||||
|
||||
|
||||
class PositiveIntAction(argparse.Action):
|
||||
"""Validate number given is positive integer."""
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
"""Validate input."""
|
||||
if values > 0:
|
||||
setattr(namespace, self.dest, values)
|
||||
return
|
||||
|
||||
msg = 'Must be a positive integer.'
|
||||
raise argparse.ArgumentError(self, msg)
|
||||
|
||||
|
||||
class Logs(AbstractActionBase):
|
||||
"""Class for retrieving logs from container."""
|
||||
|
||||
@classmethod
|
||||
def subparser(cls, parent):
|
||||
"""Add Logs command to parent parser."""
|
||||
parser = parent.add_parser('logs', help='retrieve logs from container')
|
||||
parser.add_argument(
|
||||
'--tail',
|
||||
metavar='LINES',
|
||||
action=PositiveIntAction,
|
||||
type=int,
|
||||
help='Output the specified number of LINES at the end of the logs')
|
||||
parser.add_argument(
|
||||
'container',
|
||||
nargs=1,
|
||||
help='retrieve container logs',
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='logs')
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct Logs class."""
|
||||
super().__init__(args)
|
||||
|
||||
def logs(self):
|
||||
"""Retrieve logs from containers."""
|
||||
try:
|
||||
ident = self._args.container[0]
|
||||
try:
|
||||
logging.debug('Get container "%s" logs', ident)
|
||||
ctnr = self.client.containers.get(ident)
|
||||
except podman.ContainerNotFound as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'Container "{}" not found'.format(e.name),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
else:
|
||||
if self._args.tail:
|
||||
logs = iter(deque(ctnr.logs(), maxlen=self._args.tail))
|
||||
else:
|
||||
logs = ctnr.logs()
|
||||
|
||||
for line in logs:
|
||||
sys.stdout.write(line)
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'{}'.format(e.reason).capitalize(),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
78
contrib/python/pypodman/pypodman/lib/actions/mount_action.py
Normal file
78
contrib/python/pypodman/pypodman/lib/actions/mount_action.py
Normal file
|
@ -0,0 +1,78 @@
|
|||
"""Remote client command for retrieving mounts from containers."""
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
|
||||
import podman
|
||||
from pypodman.lib import AbstractActionBase, Report, ReportColumn
|
||||
|
||||
|
||||
class Mount(AbstractActionBase):
|
||||
"""Class for retrieving mounts from container."""
|
||||
|
||||
@classmethod
|
||||
def subparser(cls, parent):
|
||||
"""Add mount command to parent parser."""
|
||||
parser = parent.add_parser(
|
||||
'mount', help='retrieve mounts from containers.')
|
||||
super().subparser(parser)
|
||||
parser.add_argument(
|
||||
'containers',
|
||||
nargs='*',
|
||||
help='containers to list ports',
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='mount')
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct Mount class."""
|
||||
super().__init__(args)
|
||||
|
||||
self.columns = OrderedDict({
|
||||
'id':
|
||||
ReportColumn('id', 'CONTAINER ID', 14),
|
||||
'destination':
|
||||
ReportColumn('destination', 'DESTINATION', 0)
|
||||
})
|
||||
|
||||
def mount(self):
|
||||
"""Retrieve mounts from containers."""
|
||||
try:
|
||||
ctnrs = []
|
||||
if not self._args.containers:
|
||||
ctnrs = self.client.containers.list()
|
||||
else:
|
||||
for ident in self._args.containers:
|
||||
try:
|
||||
ctnrs.append(self.client.containers.get(ident))
|
||||
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)
|
||||
return 1
|
||||
|
||||
if not ctnrs:
|
||||
print(
|
||||
'Unable to find any containers.', file=sys.stderr, flush=True)
|
||||
return 1
|
||||
|
||||
rows = list()
|
||||
for ctnr in ctnrs:
|
||||
details = ctnr.inspect()
|
||||
rows.append({
|
||||
'id': ctnr.id,
|
||||
'destination': details.graphdriver['data']['mergeddir']
|
||||
})
|
||||
|
||||
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)
|
47
contrib/python/pypodman/pypodman/lib/actions/pause_action.py
Normal file
47
contrib/python/pypodman/pypodman/lib/actions/pause_action.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
"""Remote client command for pausing processes in containers."""
|
||||
import sys
|
||||
|
||||
import podman
|
||||
from pypodman.lib import AbstractActionBase
|
||||
|
||||
|
||||
class Pause(AbstractActionBase):
|
||||
"""Class for pausing processes in container."""
|
||||
|
||||
@classmethod
|
||||
def subparser(cls, parent):
|
||||
"""Add Pause command to parent parser."""
|
||||
parser = parent.add_parser('pause', help='pause container processes')
|
||||
parser.add_argument(
|
||||
'containers',
|
||||
nargs='+',
|
||||
help='containers to pause',
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='pause')
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct Pause class."""
|
||||
super().__init__(args)
|
||||
|
||||
def pause(self):
|
||||
"""Pause provided containers."""
|
||||
try:
|
||||
for ident in self._args.containers:
|
||||
try:
|
||||
ctnr = self.client.containers.get(ident)
|
||||
ctnr.pause()
|
||||
except podman.ContainerNotFound as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'Container "{}" not found'.format(e.name),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
else:
|
||||
print(ident)
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'{}'.format(e.reason).capitalize(),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
63
contrib/python/pypodman/pypodman/lib/actions/port_action.py
Normal file
63
contrib/python/pypodman/pypodman/lib/actions/port_action.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
"""Remote client command for retrieving ports from containers."""
|
||||
import sys
|
||||
|
||||
import podman
|
||||
from pypodman.lib import AbstractActionBase
|
||||
|
||||
|
||||
class Port(AbstractActionBase):
|
||||
"""Class for retrieving ports from container."""
|
||||
|
||||
@classmethod
|
||||
def subparser(cls, parent):
|
||||
"""Add Port command to parent parser."""
|
||||
parser = parent.add_parser(
|
||||
'port', help='retrieve ports from containers.')
|
||||
parser.add_argument(
|
||||
'--all',
|
||||
'-a',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='List all known port mappings for running containers')
|
||||
parser.add_argument(
|
||||
'containers',
|
||||
nargs='*',
|
||||
default=None,
|
||||
help='containers to list ports',
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='port')
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct Port class."""
|
||||
super().__init__(args)
|
||||
if not args.all and not args.containers:
|
||||
ValueError('You must supply at least one'
|
||||
' container id or name, or --all.')
|
||||
|
||||
def port(self):
|
||||
"""Retrieve ports from containers."""
|
||||
try:
|
||||
ctnrs = []
|
||||
if self._args.all:
|
||||
ctnrs = self.client.containers.list()
|
||||
else:
|
||||
for ident in self._args.containers:
|
||||
try:
|
||||
ctnrs.append(self.client.containers.get(ident))
|
||||
except podman.ContainerNotFound as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'Container "{}" not found'.format(e.name),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
|
||||
for ctnr in ctnrs:
|
||||
print("{}\n{}".format(ctnr.id, ctnr.ports))
|
||||
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'{}'.format(e.reason).capitalize(),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
|
@ -3,8 +3,8 @@ import operator
|
|||
from collections import OrderedDict
|
||||
|
||||
import humanize
|
||||
import podman
|
||||
|
||||
import podman
|
||||
from pypodman.lib import AbstractActionBase, Report, ReportColumn
|
||||
|
||||
|
||||
|
@ -44,7 +44,7 @@ class Ps(AbstractActionBase):
|
|||
'status':
|
||||
ReportColumn('status', 'STATUS', 10),
|
||||
'ports':
|
||||
ReportColumn('ports', 'PORTS', 28),
|
||||
ReportColumn('ports', 'PORTS', 0),
|
||||
'names':
|
||||
ReportColumn('names', 'NAMES', 18)
|
||||
})
|
||||
|
@ -67,6 +67,9 @@ class Ps(AbstractActionBase):
|
|||
'createdat':
|
||||
humanize.naturaldate(podman.datetime_parse(ctnr.createdat)),
|
||||
})
|
||||
|
||||
if self._args.truncate:
|
||||
fields.update({'image': ctnr.image[-30:]})
|
||||
rows.append(fields)
|
||||
|
||||
with Report(self.columns, heading=self._args.heading) as report:
|
||||
|
|
|
@ -53,17 +53,14 @@ class Report():
|
|||
fmt = []
|
||||
|
||||
for key in keys:
|
||||
value = max(map(lambda x: len(str(x.get(key, ''))), iterable))
|
||||
# print('key', key, 'value', value)
|
||||
slice_ = [i.get(key, '') for i in iterable]
|
||||
data_len = len(max(slice_, key=len))
|
||||
|
||||
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
|
||||
info = self._columns.get(key,
|
||||
ReportColumn(key, key.upper(), data_len))
|
||||
display_len = max(data_len, len(info.display))
|
||||
if truncate and info.width != 0:
|
||||
display_len = info.width
|
||||
|
||||
fmt.append('{{{0}:{1}.{1}}}'.format(key, value))
|
||||
fmt.append('{{{0}:{1}.{1}}}'.format(key, display_len))
|
||||
self._format = ' '.join(fmt)
|
||||
|
|
Loading…
Reference in a new issue