mirror of
https://github.com/containers/podman.git
synced 2025-06-19 00:06:43 +08:00
add podman remote client
podman client that is capable of: * images * ps * rm * rmi this is only a mockup to frame out and prove python library and ssh tunnelling usage. Signed-off-by: baude <bbaude@redhat.com> Closes: #986 Approved by: rhatdan
This commit is contained in:
21
contrib/python/cmd/images.py
Normal file
21
contrib/python/cmd/images.py
Normal file
@ -0,0 +1,21 @@
|
||||
from pman import PodmanRemote
|
||||
from utils import write_out, convert_size, stringTimeToHuman
|
||||
|
||||
def cli(subparser):
|
||||
imagesp = subparser.add_parser("images",
|
||||
help=("list images"))
|
||||
imagesp.add_argument("all", action="store_true", help="list all images")
|
||||
imagesp.set_defaults(_class=Images, func='display_all_image_info')
|
||||
|
||||
|
||||
class Images(PodmanRemote):
|
||||
|
||||
def display_all_image_info(self):
|
||||
col_fmt = "{0:40}{1:12}{2:14}{3:18}{4:14}"
|
||||
write_out(col_fmt.format("REPOSITORY", "TAG", "IMAGE ID", "CREATED", "SIZE"))
|
||||
for i in self.client.images.list():
|
||||
for r in i["repoTags"]:
|
||||
rsplit = r.rindex(":")
|
||||
name = r[0:rsplit-1]
|
||||
tag = r[rsplit+1:]
|
||||
write_out(col_fmt.format(name, tag, i["id"][:12], stringTimeToHuman(i["created"]), convert_size(i["size"])))
|
42
contrib/python/cmd/pman.py
Normal file
42
contrib/python/cmd/pman.py
Normal file
@ -0,0 +1,42 @@
|
||||
import podman as p
|
||||
|
||||
|
||||
class PodmanRemote(object):
|
||||
def __init__(self):
|
||||
self.args = None
|
||||
self._remote_uri= None
|
||||
self._local_uri= None
|
||||
self._identity_file= None
|
||||
self._client = None
|
||||
|
||||
def set_args(self, args, local_uri, remote_uri, identity_file):
|
||||
self.args = args
|
||||
self._local_uri = local_uri
|
||||
self.remote_uri = remote_uri
|
||||
self._identity_file = identity_file
|
||||
|
||||
@property
|
||||
def remote_uri(self):
|
||||
return self._remote_uri
|
||||
|
||||
@property
|
||||
def local_uri(self):
|
||||
return self._local_uri
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
if self._client is None:
|
||||
self._client = p.Client(uri=self.local_uri, remote_uri=self.remote_uri, identity_file=self.identity_file)
|
||||
return self._client
|
||||
|
||||
@remote_uri.setter
|
||||
def remote_uri(self, uri):
|
||||
self._remote_uri = uri
|
||||
|
||||
@local_uri.setter
|
||||
def local_uri(self, uri):
|
||||
self._local_uri= uri
|
||||
|
||||
@property
|
||||
def identity_file(self):
|
||||
return self._identity_file
|
19
contrib/python/cmd/ps.py
Normal file
19
contrib/python/cmd/ps.py
Normal file
@ -0,0 +1,19 @@
|
||||
from pman import PodmanRemote
|
||||
from utils import write_out, convert_size, stringTimeToHuman
|
||||
|
||||
def cli(subparser):
|
||||
imagesp = subparser.add_parser("ps",
|
||||
help=("list containers"))
|
||||
imagesp.add_argument("all", action="store_true", help="list all containers")
|
||||
imagesp.set_defaults(_class=Ps, func='display_all_containers')
|
||||
|
||||
|
||||
class Ps(PodmanRemote):
|
||||
|
||||
def display_all_containers(self):
|
||||
col_fmt = "{0:15}{1:32}{2:22}{3:14}{4:12}{5:30}{6:20}"
|
||||
write_out(col_fmt.format("CONTAINER ID", "IMAGE", "COMMAND", "CREATED", "STATUS", "PORTS", "NAMES"))
|
||||
|
||||
for i in self.client.containers.list():
|
||||
command = " ".join(i["command"])
|
||||
write_out(col_fmt.format(i["id"][0:12], i["image"][0:30], command[0:20], stringTimeToHuman(i["createdat"]), i["status"], "", i["names"][0:20]))
|
136
contrib/python/cmd/remote_client.py
Normal file
136
contrib/python/cmd/remote_client.py
Normal file
@ -0,0 +1,136 @@
|
||||
import os
|
||||
import getpass
|
||||
import argparse
|
||||
import images
|
||||
import ps, rm, rmi
|
||||
import sys
|
||||
from utils import write_err
|
||||
import pytoml
|
||||
|
||||
default_conf_path = "/etc/containers/podman_client.conf"
|
||||
|
||||
class HelpByDefaultArgumentParser(argparse.ArgumentParser):
|
||||
|
||||
def error(self, message):
|
||||
write_err('%s: %s' % (self.prog, message))
|
||||
write_err("Try '%s --help' for more information." % self.prog)
|
||||
sys.exit(2)
|
||||
|
||||
def print_usage(self, message="too few arguments"): # pylint: disable=arguments-differ
|
||||
self.prog = " ".join(sys.argv)
|
||||
self.error(message)
|
||||
|
||||
|
||||
def create_parser(help_text):
|
||||
parser = HelpByDefaultArgumentParser(description=help_text)
|
||||
parser.add_argument('-v', '--version', action='version', version="0.0",
|
||||
help=("show rpodman version and exit"))
|
||||
parser.add_argument('--debug', default=False, action='store_true',
|
||||
help=("show debug messages"))
|
||||
parser.add_argument('--run_dir', dest="run_dir",
|
||||
help=("directory to place socket bindings"))
|
||||
parser.add_argument('--user', dest="user",
|
||||
help=("remote user"))
|
||||
parser.add_argument('--host', dest="host",
|
||||
help=("remote host"))
|
||||
parser.add_argument('--remote_socket_path', dest="remote_socket_path",
|
||||
help=("remote socket path"))
|
||||
parser.add_argument('--identity_file', dest="identity_file",
|
||||
help=("path to identity file"))
|
||||
subparser = parser.add_subparsers(help=("commands"))
|
||||
images.cli(subparser)
|
||||
ps.cli(subparser)
|
||||
rm.cli(subparser)
|
||||
rmi.cli(subparser)
|
||||
|
||||
return parser
|
||||
|
||||
def load_toml(path):
|
||||
# Lets load the configuration file
|
||||
with open(path) as stream:
|
||||
return pytoml.load(stream)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
host = None
|
||||
remote_socket_path = None
|
||||
user = None
|
||||
run_dir = None
|
||||
|
||||
aparser = create_parser("podman remote tool")
|
||||
args = aparser.parse_args()
|
||||
if not os.path.exists(default_conf_path):
|
||||
conf = {"default": {}}
|
||||
else:
|
||||
conf = load_toml("/etc/containers/podman_client.conf")
|
||||
|
||||
# run_dir
|
||||
if "run_dir" in os.environ:
|
||||
run_dir = os.environ["run_dir"]
|
||||
elif "run_dir" in conf["default"] and conf["default"]["run_dir"] is not None:
|
||||
run_dir = conf["default"]["run_dir"]
|
||||
else:
|
||||
xdg = os.environ["XDG_RUNTIME_DIR"]
|
||||
run_dir = os.path.join(xdg, "podman")
|
||||
|
||||
# make the run_dir if it doesnt exist
|
||||
if not os.path.exists(run_dir):
|
||||
os.makedirs(run_dir)
|
||||
|
||||
local_socket_path = os.path.join(run_dir, "podman.socket")
|
||||
|
||||
# remote host
|
||||
if "host" in os.environ:
|
||||
host = os.environ["host"]
|
||||
elif getattr(args, "host") is not None:
|
||||
host = getattr(args, "host")
|
||||
else:
|
||||
host = conf["default"]["host"] if "host" in conf["default"] else None
|
||||
|
||||
# remote user
|
||||
if "user" in os.environ:
|
||||
user = os.environ["user"]
|
||||
elif getattr(args, "user") is not None:
|
||||
user = getattr(args, "user")
|
||||
elif "user" in conf["default"] and conf["default"]["user"] is not None:
|
||||
user = conf["default"]["user"]
|
||||
else:
|
||||
user = getpass.getuser()
|
||||
|
||||
# remote path
|
||||
if "remote_socket_path" in os.environ:
|
||||
remote_socket_path = os.environ["remote_socket_path"]
|
||||
elif getattr(args, "remote_socket_path") is not None:
|
||||
remote_socket_path = getattr(args, "remote_socket_path")
|
||||
elif "remote_socket_path" in conf["default"] and conf["default"]["remote_socket_path"]:
|
||||
remote_socket_path = conf["default"]["remote_socket_path"]
|
||||
else:
|
||||
remote_socket_path = None
|
||||
|
||||
|
||||
# identity file
|
||||
if "identity_file" in os.environ:
|
||||
identity_file = os.environ["identity_file"]
|
||||
elif getattr(args, "identity_file") is not None:
|
||||
identity_file = getattr(args, "identity_file")
|
||||
elif "identity_file" in conf["default"] and conf["default"]["identity_file"] is not None:
|
||||
identity_file = conf["default"]["identity_file"]
|
||||
else:
|
||||
identity_file = None
|
||||
|
||||
if None in [host, local_socket_path, user, remote_socket_path]:
|
||||
print("missing input for local_socket, user, host, or remote_socket_path")
|
||||
sys.exit(1)
|
||||
|
||||
local_uri = "unix:{}".format(local_socket_path)
|
||||
remote_uri = "ssh://{}@{}{}".format(user, host, remote_socket_path)
|
||||
|
||||
_class = args._class() # pylint: disable=protected-access
|
||||
_class.set_args(args, local_uri, remote_uri, identity_file)
|
||||
|
||||
if "func" in args:
|
||||
_func = getattr(_class, args.func)
|
||||
sys.exit(_func())
|
||||
else:
|
||||
aparser.print_usage()
|
||||
sys.exit(1)
|
22
contrib/python/cmd/rm.py
Normal file
22
contrib/python/cmd/rm.py
Normal file
@ -0,0 +1,22 @@
|
||||
from pman import PodmanRemote
|
||||
from utils import write_out, convert_size, stringTimeToHuman
|
||||
|
||||
def cli(subparser):
|
||||
imagesp = subparser.add_parser("rm",
|
||||
help=("delete one or more containers"))
|
||||
imagesp.add_argument("--force", "-f", action="store_true", help="force delete", dest="force")
|
||||
imagesp.add_argument("delete_targets", nargs='*', help="container images to delete")
|
||||
imagesp.set_defaults(_class=Rm, func='remove_containers')
|
||||
|
||||
|
||||
class Rm(PodmanRemote):
|
||||
|
||||
def remove_containers(self):
|
||||
delete_targets = getattr(self.args, "delete_targets")
|
||||
if len(delete_targets) < 1:
|
||||
raise ValueError("you must supply at least one container id or name to delete")
|
||||
force = getattr(self.args, "force")
|
||||
for d in delete_targets:
|
||||
con = self.client.containers.get(d)
|
||||
con.remove(force)
|
||||
write_out(con["id"])
|
25
contrib/python/cmd/rmi.py
Normal file
25
contrib/python/cmd/rmi.py
Normal file
@ -0,0 +1,25 @@
|
||||
from pman import PodmanRemote
|
||||
from utils import write_out, write_err
|
||||
|
||||
def cli(subparser):
|
||||
imagesp = subparser.add_parser("rmi",
|
||||
help=("delete one or more images"))
|
||||
imagesp.add_argument("--force", "-f", action="store_true", help="force delete", dest="force")
|
||||
imagesp.add_argument("delete_targets", nargs='*', help="images to delete")
|
||||
imagesp.set_defaults(_class=Rmi, func='remove_images')
|
||||
|
||||
|
||||
class Rmi(PodmanRemote):
|
||||
|
||||
def remove_images(self):
|
||||
delete_targets = getattr(self.args, "delete_targets")
|
||||
if len(delete_targets) < 1:
|
||||
raise ValueError("you must supply at least one image id or name to delete")
|
||||
force = getattr(self.args, "force")
|
||||
for d in delete_targets:
|
||||
image = self.client.images.get(d)
|
||||
if image["containers"] > 0 and not force:
|
||||
write_err("unable to delete {} because it has associated errors. retry with --force".format(d))
|
||||
continue
|
||||
image.remove(force)
|
||||
write_out(image["id"])
|
32
contrib/python/cmd/utils.py
Normal file
32
contrib/python/cmd/utils.py
Normal file
@ -0,0 +1,32 @@
|
||||
import sys
|
||||
import math
|
||||
import datetime
|
||||
|
||||
def write_out(output, lf="\n"):
|
||||
_output(sys.stdout, output, lf)
|
||||
|
||||
|
||||
def write_err(output, lf="\n"):
|
||||
_output(sys.stderr, output, lf)
|
||||
|
||||
|
||||
def _output(fd, output, lf):
|
||||
fd.flush()
|
||||
fd.write(output + str(lf))
|
||||
|
||||
|
||||
def convert_size(size):
|
||||
if size > 0:
|
||||
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
|
||||
i = int(math.floor(math.log(size, 1000)))
|
||||
p = math.pow(1000, i)
|
||||
s = round(size/p, 2) # pylint: disable=round-builtin,old-division
|
||||
if s > 0:
|
||||
return '%s %s' % (s, size_name[i])
|
||||
return '0B'
|
||||
|
||||
def stringTimeToHuman(t):
|
||||
#datetime.date(datetime.strptime("05/Feb/2016", '%d/%b/%Y'))
|
||||
#2018-04-30 13:55:45.019400581 +0000 UTC
|
||||
#d = datetime.date(datetime.strptime(t, "%Y-%m-%d"))
|
||||
return "sometime ago"
|
@ -98,7 +98,7 @@ class Tunnel(object):
|
||||
"""Create SSH tunnel from given context."""
|
||||
cmd = [
|
||||
'ssh',
|
||||
'-nNT',
|
||||
'-nNTq',
|
||||
'-L',
|
||||
'{}:{}'.format(self.context.local_socket,
|
||||
self.context.remote_socket),
|
||||
|
Reference in New Issue
Block a user