diff options
author | Arthy Loganathan <aloganat@redhat.com> | 2017-12-09 23:26:42 +0530 |
---|---|---|
committer | John Mulligan <jmulligan@redhat.com> | 2017-12-22 10:59:03 -0500 |
commit | 07b3da9c25f2907047402373273443eacbbd8c8b (patch) | |
tree | bfa229ed9afc4db4ccd0af68321ce7dc43b72df7 | |
parent | 2af212ebbe85b1ad58b6bc546e3c19db4be17b9d (diff) |
Library functions to run commands in ocp pods
This version cleans up various aspects of the code wrt review
comments as well as trying to make it a bit more pythonic
Change-Id: Ic0ada62b3ba8ce1ebc471d358daedea4572c3b0d
Signed-off-by: Arthy Loganathan <aloganat@redhat.com>
Signed-off-by: John Mulligan <jmulligan@redhat.com>
-rw-r--r-- | cns-libs/cnslibs/common/openshift_ops.py | 166 | ||||
-rw-r--r-- | cns-libs/cnslibs/common/utils.py | 44 |
2 files changed, 210 insertions, 0 deletions
diff --git a/cns-libs/cnslibs/common/openshift_ops.py b/cns-libs/cnslibs/common/openshift_ops.py new file mode 100644 index 00000000..beff3272 --- /dev/null +++ b/cns-libs/cnslibs/common/openshift_ops.py @@ -0,0 +1,166 @@ +"""Library for openshift operations. + +Various utility functions for interacting with OCP/OpenShift. +""" + +import re +import types + +import yaml + +from glusto.core import Glusto as g + + +PODS_WIDE_RE = re.compile( + '(\S+)\s+(\S+)\s+(\w+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\n') + + +def oc_get_pods(ocp_node): + """Gets the pods info with 'wide' option in the current project. + + Args: + ocp_node (str): Node in which ocp command will be executed. + + Returns: + dict : dict of pods info in the current project. + """ + + cmd = "oc get -o wide --no-headers=true pods" + ret, out, err = g.run(ocp_node, cmd) + if ret != 0: + g.log.error("Failed to get ocp pods on node %s" % ocp_node) + raise AssertionError('failed to get pods: %r' % (err,)) + return _parse_wide_pods_output(out) + + +def _parse_wide_pods_output(output): + """Parse the output of `oc get -o wide pods`. + """ + # Interestingly, the output of get pods is "cooked" in such a way that + # the values in the ready, status, & restart fields are not accessible + # from YAML/JSON/templating forcing us to scrape the output for + # these values + # (at the time of this writing the logic is in + # printPodBase in kubernetes/pkg/printers/internalversion/printers.go ) + # Possibly obvious, but if you don't need those values you can + # use the YAML output directly. + # + # TODO: Add unit tests for this parser + pods_info = {} + for each_pod_info in PODS_WIDE_RE.findall(output): + pods_info[each_pod_info[0]] = { + 'ready': each_pod_info[1], + 'status': each_pod_info[2], + 'restarts': each_pod_info[3], + 'age': each_pod_info[4], + 'ip': each_pod_info[5], + 'node': each_pod_info[6], + } + return pods_info + + +def oc_get_pods_full(ocp_node): + """Gets all the pod info via YAML in the current project. + + Args: + ocp_node (str): Node in which ocp command will be executed. + + Returns: + dict: The YAML output converted to python objects + (a top-level dict) + """ + + cmd = "oc get -o yaml pods" + ret, out, err = g.run(ocp_node, cmd) + if ret != 0: + g.log.error("Failed to get ocp pods on node %s" % ocp_node) + raise AssertionError('failed to get pods: %r' % (err,)) + return yaml.load(out) + + +def get_ocp_gluster_pod_names(ocp_node): + """Gets the gluster pod names in the current project. + + Args: + ocp_node (str): Node in which ocp command will be executed. + + Returns: + list : list of gluster pod names in the current project. + Empty list, if there are no gluster pods. + + Example: + get_ocp_gluster_pod_names(ocp_node) + """ + + pod_names = oc_get_pods(ocp_node).keys() + return [pod for pod in pod_names if pod.startswith('glusterfs-')] + + +def oc_login(ocp_node, username, password): + """Login to ocp master node. + + Args: + ocp_node (str): Node in which ocp command will be executed. + username (str): username of ocp master node to login. + password (str): password of ocp master node to login. + + Returns: + bool : True on successful login to ocp master node. + False otherwise + + Example: + oc_login(ocp_node, "test","test") + """ + + cmd = "oc login --username=%s --password=%s" % (username, password) + ret, _, _ = g.run(ocp_node, cmd) + if ret != 0: + g.log.error("Failed to login to ocp master node %s" % ocp_node) + return False + return True + + +def switch_oc_project(ocp_node, project_name): + """Switch to the given project. + + Args: + ocp_node (str): Node in which ocp command will be executed. + project_name (str): Project name. + Returns: + bool : True on switching to given project. + False otherwise + + Example: + switch_oc_project(ocp_node, "storage-project") + """ + + cmd = "oc project %s" % project_name + ret, _, _ = g.run(ocp_node, cmd) + if ret != 0: + g.log.error("Failed to switch to project %s" % project_name) + return False + return True + + +def oc_rsh(ocp_node, pod_name, command): + """Run a command in the ocp pod using `oc rsh`. + + Args: + ocp_node (str): Node on which oc rsh command will be executed. + pod_name (str): Name of the pod on which the command will + be executed. + command (str|list): command to run. + + Returns: + A tuple consisting of the command return code, stdout, and stderr. + """ + prefix = ['oc', 'rsh', pod_name] + if isinstance(command, types.StringTypes): + cmd = ' '.join(prefix + [command]) + else: + cmd = prefix + command + + # unpack the tuple to make sure our return value exactly matches + # our docstring + ret, stdout, stderr = g.run(ocp_node, cmd) + return (ret, stdout, stderr) diff --git a/cns-libs/cnslibs/common/utils.py b/cns-libs/cnslibs/common/utils.py new file mode 100644 index 00000000..a47ccafa --- /dev/null +++ b/cns-libs/cnslibs/common/utils.py @@ -0,0 +1,44 @@ +"""Generic host utility functions. + +Generic utility functions not specifc to a larger suite of tools. +For example, not specific to OCP, Gluster, Heketi, etc. +""" + +import re + +from glusto.core import Glusto as g + + +ONE_GB_BYTES = 1073741824.0 + + +def get_device_size(host, device_name): + """Gets device size for the given device name. + + Args: + host (str): Node in command will be executed. + device_name (str): device name for which the size has to + be calculated. + + Returns: + str : returns device size in GB on success + False otherwise + + Example: + get_device_size(host, device_name) + """ + + cmd = "fdisk -l %s " % device_name + ret, out, _ = g.run(host, cmd) + if ret != 0: + g.log.error("Failed to execute fdisk -l command " + "on node %s" % host) + return False + + regex = 'Disk\s' + device_name + '.*?,\s(\d+)\sbytes\,.*' + match = re.search(regex, out) + if match is None: + g.log.error("Regex mismatch while parsing fdisk -l output") + return False + + return str(int(int(match.group(1))/ONE_GB_BYTES)) |