summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbaude <bbaude@redhat.com>2018-06-22 08:15:37 -0500
committerAtomic Bot <atomic-devel@projectatomic.io>2018-06-27 04:52:22 +0000
commit60427ab3d26bbe5a5ab00c4e7b550f111b4f72e5 (patch)
tree64f11dab9bcdc426956160b6b56ee536bb3aa6e8
parent56133f7263337e9b2c9cfb1cfce35adc2366f52b (diff)
downloadpodman-60427ab3d26bbe5a5ab00c4e7b550f111b4f72e5.tar.gz
podman-60427ab3d26bbe5a5ab00c4e7b550f111b4f72e5.tar.bz2
podman-60427ab3d26bbe5a5ab00c4e7b550f111b4f72e5.zip
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
-rw-r--r--contrib/python/cmd/images.py21
-rw-r--r--contrib/python/cmd/pman.py42
-rw-r--r--contrib/python/cmd/ps.py19
-rw-r--r--contrib/python/cmd/remote_client.py136
-rw-r--r--contrib/python/cmd/rm.py22
-rw-r--r--contrib/python/cmd/rmi.py25
-rw-r--r--contrib/python/cmd/utils.py32
-rw-r--r--contrib/python/podman/libs/tunnel.py2
8 files changed, 298 insertions, 1 deletions
diff --git a/contrib/python/cmd/images.py b/contrib/python/cmd/images.py
new file mode 100644
index 000000000..3e0dff626
--- /dev/null
+++ b/contrib/python/cmd/images.py
@@ -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"])))
diff --git a/contrib/python/cmd/pman.py b/contrib/python/cmd/pman.py
new file mode 100644
index 000000000..c75c3d174
--- /dev/null
+++ b/contrib/python/cmd/pman.py
@@ -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
diff --git a/contrib/python/cmd/ps.py b/contrib/python/cmd/ps.py
new file mode 100644
index 000000000..85db5489e
--- /dev/null
+++ b/contrib/python/cmd/ps.py
@@ -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]))
diff --git a/contrib/python/cmd/remote_client.py b/contrib/python/cmd/remote_client.py
new file mode 100644
index 000000000..9bb5a0d9a
--- /dev/null
+++ b/contrib/python/cmd/remote_client.py
@@ -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) \ No newline at end of file
diff --git a/contrib/python/cmd/rm.py b/contrib/python/cmd/rm.py
new file mode 100644
index 000000000..c9dfaa688
--- /dev/null
+++ b/contrib/python/cmd/rm.py
@@ -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"])
diff --git a/contrib/python/cmd/rmi.py b/contrib/python/cmd/rmi.py
new file mode 100644
index 000000000..807c5c1e4
--- /dev/null
+++ b/contrib/python/cmd/rmi.py
@@ -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"])
diff --git a/contrib/python/cmd/utils.py b/contrib/python/cmd/utils.py
new file mode 100644
index 000000000..d4a14164d
--- /dev/null
+++ b/contrib/python/cmd/utils.py
@@ -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"
diff --git a/contrib/python/podman/libs/tunnel.py b/contrib/python/podman/libs/tunnel.py
index 534326ff0..42fd3356b 100644
--- a/contrib/python/podman/libs/tunnel.py
+++ b/contrib/python/podman/libs/tunnel.py
@@ -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),