mirror of
https://github.com/containers/podman.git
synced 2025-06-20 00:51:16 +08: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:
@ -9,7 +9,7 @@ host:
|
|||||||
ram: 8192
|
ram: 8192
|
||||||
cpus: 4
|
cpus: 4
|
||||||
required: true
|
required: true
|
||||||
timeout: 90m
|
timeout: 120m
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
- CONTAINER_RUNTIME="podman" sh .papr_prepare.sh
|
- CONTAINER_RUNTIME="podman" sh .papr_prepare.sh
|
||||||
@ -35,7 +35,7 @@ extra-repos:
|
|||||||
|
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
timeout: 90m
|
timeout: 120m
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
- sh .papr_prepare.sh
|
- sh .papr_prepare.sh
|
||||||
@ -63,7 +63,7 @@ tests:
|
|||||||
- CONTAINER_RUNTIME="podman" sh .papr_prepare.sh
|
- CONTAINER_RUNTIME="podman" sh .papr_prepare.sh
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
timeout: 90m
|
timeout: 120m
|
||||||
context: "Fedora fedora/28/cloud Podman"
|
context: "Fedora fedora/28/cloud Podman"
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -214,7 +214,7 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
|
|||||||
"""Retrieve container logs."""
|
"""Retrieve container logs."""
|
||||||
with self._client() as podman:
|
with self._client() as podman:
|
||||||
results = podman.GetContainerLogs(self._id)
|
results = podman.GetContainerLogs(self._id)
|
||||||
yield from results
|
yield from results['container']
|
||||||
|
|
||||||
|
|
||||||
class Containers():
|
class Containers():
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Remote podman client support library."""
|
"""Remote podman client support library."""
|
||||||
from pypodman.lib.action_base import AbstractActionBase
|
from pypodman.lib.action_base import AbstractActionBase
|
||||||
from pypodman.lib.config import PodmanArgumentParser
|
from pypodman.lib.config import PodmanArgumentParser
|
||||||
from pypodman.lib.report import (Report, ReportColumn)
|
from pypodman.lib.report import Report, ReportColumn
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AbstractActionBase',
|
'AbstractActionBase',
|
||||||
|
@ -65,8 +65,7 @@ class AbstractActionBase(abc.ABC):
|
|||||||
def client(self):
|
def client(self):
|
||||||
"""Podman remote client for communicating."""
|
"""Podman remote client for communicating."""
|
||||||
if self._args.host is None:
|
if self._args.host is None:
|
||||||
return podman.Client(
|
return podman.Client(uri=self.local_uri)
|
||||||
uri=self.local_uri)
|
|
||||||
return podman.Client(
|
return podman.Client(
|
||||||
uri=self.local_uri,
|
uri=self.local_uri,
|
||||||
remote_uri=self.remote_uri,
|
remote_uri=self.remote_uri,
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
"""Module to export all the podman subcommands."""
|
"""Module to export all the podman subcommands."""
|
||||||
from pypodman.lib.actions.attach_action import Attach
|
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.create_action import Create
|
||||||
|
from pypodman.lib.actions.export_action import Export
|
||||||
from pypodman.lib.actions.images_action import Images
|
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.ps_action import Ps
|
||||||
from pypodman.lib.actions.pull_action import Pull
|
from pypodman.lib.actions.pull_action import Pull
|
||||||
from pypodman.lib.actions.rm_action import Rm
|
from pypodman.lib.actions.rm_action import Rm
|
||||||
@ -9,8 +17,16 @@ from pypodman.lib.actions.rmi_action import Rmi
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'Attach',
|
'Attach',
|
||||||
|
'Commit',
|
||||||
'Create',
|
'Create',
|
||||||
|
'Export',
|
||||||
'Images',
|
'Images',
|
||||||
|
'Inspect',
|
||||||
|
'Kill',
|
||||||
|
'Logs',
|
||||||
|
'Mount',
|
||||||
|
'Pause',
|
||||||
|
'Port',
|
||||||
'Ps',
|
'Ps',
|
||||||
'Pull',
|
'Pull',
|
||||||
'Rm',
|
'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
|
from collections import OrderedDict
|
||||||
|
|
||||||
import humanize
|
import humanize
|
||||||
import podman
|
|
||||||
|
|
||||||
|
import podman
|
||||||
from pypodman.lib import AbstractActionBase, Report, ReportColumn
|
from pypodman.lib import AbstractActionBase, Report, ReportColumn
|
||||||
|
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ class Ps(AbstractActionBase):
|
|||||||
'status':
|
'status':
|
||||||
ReportColumn('status', 'STATUS', 10),
|
ReportColumn('status', 'STATUS', 10),
|
||||||
'ports':
|
'ports':
|
||||||
ReportColumn('ports', 'PORTS', 28),
|
ReportColumn('ports', 'PORTS', 0),
|
||||||
'names':
|
'names':
|
||||||
ReportColumn('names', 'NAMES', 18)
|
ReportColumn('names', 'NAMES', 18)
|
||||||
})
|
})
|
||||||
@ -67,6 +67,9 @@ class Ps(AbstractActionBase):
|
|||||||
'createdat':
|
'createdat':
|
||||||
humanize.naturaldate(podman.datetime_parse(ctnr.createdat)),
|
humanize.naturaldate(podman.datetime_parse(ctnr.createdat)),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if self._args.truncate:
|
||||||
|
fields.update({'image': ctnr.image[-30:]})
|
||||||
rows.append(fields)
|
rows.append(fields)
|
||||||
|
|
||||||
with Report(self.columns, heading=self._args.heading) as report:
|
with Report(self.columns, heading=self._args.heading) as report:
|
||||||
|
@ -53,17 +53,14 @@ class Report():
|
|||||||
fmt = []
|
fmt = []
|
||||||
|
|
||||||
for key in keys:
|
for key in keys:
|
||||||
value = max(map(lambda x: len(str(x.get(key, ''))), iterable))
|
slice_ = [i.get(key, '') for i in iterable]
|
||||||
# print('key', key, 'value', value)
|
data_len = len(max(slice_, key=len))
|
||||||
|
|
||||||
if truncate:
|
info = self._columns.get(key,
|
||||||
row = self._columns.get(
|
ReportColumn(key, key.upper(), data_len))
|
||||||
key, ReportColumn(key, key.upper(), len(key)))
|
display_len = max(data_len, len(info.display))
|
||||||
if value < row.width:
|
if truncate and info.width != 0:
|
||||||
step = row.width if value == 0 else value
|
display_len = info.width
|
||||||
value = max(len(key), step)
|
|
||||||
elif value > row.width:
|
|
||||||
value = row.width if row.width != 0 else value
|
|
||||||
|
|
||||||
fmt.append('{{{0}:{1}.{1}}}'.format(key, value))
|
fmt.append('{{{0}:{1}.{1}}}'.format(key, display_len))
|
||||||
self._format = ' '.join(fmt)
|
self._format = ' '.join(fmt)
|
||||||
|
Reference in New Issue
Block a user