summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Mulligan <jmulligan@redhat.com>2018-01-03 14:40:38 -0500
committerJohn Mulligan <jmulligan@redhat.com>2018-01-10 10:50:21 -0500
commit0a48b5d2fb2dfde79e8117188863006ff2d8e849 (patch)
tree93544aec6c9ca86a2909ca0b42f39265227efa12
parent07b3da9c25f2907047402373273443eacbbd8c8b (diff)
add a utility library to support patching glusto for pod support
This module provides the ability to patch the glusto run method with the ability to run commands in pods. This module implements the custom run method and a helper class that acts as a context manager or decorator to patch glusto. This version adds a log_level argument to oc_rsh in order to plumb the argument all the way through to the inner glusto.run method. It also cleans up some the docstrings and variable names. Change-Id: I0eeea234b1af5e60e41aae63e46e2d816e8929d2 Signed-off-by: John Mulligan <jmulligan@redhat.com>
-rw-r--r--cns-libs/cnslibs/common/openshift_ops.py6
-rw-r--r--cns-libs/cnslibs/common/podcmd.py120
2 files changed, 124 insertions, 2 deletions
diff --git a/cns-libs/cnslibs/common/openshift_ops.py b/cns-libs/cnslibs/common/openshift_ops.py
index beff3272..dbe89d0e 100644
--- a/cns-libs/cnslibs/common/openshift_ops.py
+++ b/cns-libs/cnslibs/common/openshift_ops.py
@@ -142,7 +142,7 @@ def switch_oc_project(ocp_node, project_name):
return True
-def oc_rsh(ocp_node, pod_name, command):
+def oc_rsh(ocp_node, pod_name, command, log_level=None):
"""Run a command in the ocp pod using `oc rsh`.
Args:
@@ -150,6 +150,8 @@ def oc_rsh(ocp_node, pod_name, command):
pod_name (str): Name of the pod on which the command will
be executed.
command (str|list): command to run.
+ log_level (str|None): log level to be passed to glusto's run
+ method.
Returns:
A tuple consisting of the command return code, stdout, and stderr.
@@ -162,5 +164,5 @@ def oc_rsh(ocp_node, pod_name, command):
# unpack the tuple to make sure our return value exactly matches
# our docstring
- ret, stdout, stderr = g.run(ocp_node, cmd)
+ ret, stdout, stderr = g.run(ocp_node, cmd, log_level=log_level)
return (ret, stdout, stderr)
diff --git a/cns-libs/cnslibs/common/podcmd.py b/cns-libs/cnslibs/common/podcmd.py
new file mode 100644
index 00000000..f8c89d5b
--- /dev/null
+++ b/cns-libs/cnslibs/common/podcmd.py
@@ -0,0 +1,120 @@
+"""Convenience wrappers for running commands within a pod
+
+The goal of this module is to support running glusto commands in pods
+without a lot of boilerplate and hassle. The basic idea is that we
+have our own run() function that can be addressed to a pod using the
+Pod object (namedtuple). This run function will work like a normal
+g.run() when not using the Pod object.
+
+Example:
+ >>> run("my-host.local", ["parted", "/dev/sda", "p"])
+ 0, "<...stdout...>", "<...stderr...>"
+
+ >>> run(Pod("my-host.local", "my-pod-426ln"),
+ ... ["pvs"])
+ 0, "<...stdout...>", "<...stderr...>"
+
+In addition, if there's a need to to use some higher level functions
+that directly call into glusto run we can monkey-patch the glusto object
+using the GlustoPod context manager. GlustoPod can also be used as a
+decorator.
+
+Imagine a function that direcly calls g.run:
+ >>> def get_magic_number(host, ticket):
+ ... s, out, _ = g.run(host, ['magicall', '--ticket', ticket])
+ ... if s != 0:
+ ... return None
+ ... return out.strip()
+
+If you want to have this operate within a pod you can use the GlustoPod
+manager to enable the pod-aware run method and pass it a Pod object
+as the first argument. Example:
+ >>> def check_magic_number(ticket):
+ ... with GlustoPod():
+ ... m = get_magic_number(Pod('myhost', 'mypod'), ticket)
+ ... return m > 42
+
+Similarly it can be used as a context manager:
+ >>> @GlustoPod()
+ ... def funky(x):
+ ... m = get_magic_number(Pod('myhost', 'mypod'), ticket)
+ ... return m > 42
+
+Because the custom run fuction only runs commands in pods when passed
+a Pod object it is fairly safe to enable the monkey-patch over the
+lifetime of a function that addresses both hosts and pods.
+"""
+
+from collections import namedtuple
+from functools import partial, wraps
+
+from glusto.core import Glusto as g
+
+from cnslibs.common.openshift_ops import oc_rsh
+
+# Define a namedtuple that allows us to address pods instead of just
+# hosts,
+Pod = namedtuple('Pod', 'node podname')
+
+
+def run(target, command, log_level=None, orig_run=g.run):
+ """Function that runs a command on a host or in a pod via a host.
+ Wraps glusto's run function.
+
+ Args:
+ target (str|Pod): If target is a anything other than a Pod
+ object the command will be run directly on the target
+ (hostname or IP). If target is a Pod object it will run
+ on the named pod, going through the node named in the
+ Pod object.
+ command (str|list): Command to run.
+ log_level (str|None): log level to be passed on to glusto's
+ run method
+ orig_run (function): The default implementation of the
+ run method. Will be used when target is not a pod.
+
+ Returns:
+ A tuple of the command's return code, stdout, and stderr.
+ """
+ # NOTE: orig_run captures the glusto run method at function
+ # definition time in order to capture the method before
+ # any additional monkeypatching by other code
+ if isinstance(target, Pod):
+ return oc_rsh(target.node, target.podname, command,
+ log_level=log_level)
+ else:
+ return orig_run(target, command, log_level=log_level)
+
+
+class GlustoPod(object):
+ """A context manager / decorator that monkeypatches the
+ glusto object to support running commands in pods.
+ """
+
+ def __init__(self, glusto_obj=None):
+ self.runfunc = None
+ self._g = glusto_obj or g
+
+ def __enter__(self):
+ """Patch glusto to use the wrapped run method.
+ """
+ self.runfunc = self._g.run
+ # we "capture" the prior glusto run method here in order to
+ # stack on top of any previous monkeypatches if they exist
+ self._g.run = partial(run, orig_run=self.runfunc)
+
+ def __exit__(self, etype, value, tb):
+ """Restore the orginal run method.
+ """
+ self._g.run = self.runfunc
+ self.runfunc = None
+
+ def __call__(self, func):
+ """Allow GlustoPod to be used as a decorator.
+ """
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ with self:
+ result = func(*args, **kwargs)
+ return result
+ return wrapper