summaryrefslogtreecommitdiffstats
path: root/cns-libs/cnslibs/common
diff options
context:
space:
mode:
authorJohn Mulligan <jmulligan@redhat.com>2017-12-19 17:44:47 -0500
committerJohn Mulligan <jmulligan@redhat.com>2018-01-19 14:31:07 -0500
commitc9c9f72e2e038643bb96436701ea8abac3fe47c7 (patch)
treebdeed728e53f0380dbd21d06dde2256d10f51d9b /cns-libs/cnslibs/common
parent993cc40e8e79e9a09114286d018660c98a336ac0 (diff)
add a test to verify multiple PVC requests create multiple volumes
This is sort of a trial of the both the test framework, my ability to add tests to it, and the workflow of adding changes to the repo. Change-Id: I17b61f298c04f45df8b77555b16d292fd2b2a3a8 Signed-off-by: John Mulligan <jmulligan@redhat.com>
Diffstat (limited to 'cns-libs/cnslibs/common')
-rw-r--r--cns-libs/cnslibs/common/naming.py56
-rw-r--r--cns-libs/cnslibs/common/openshift_ops.py101
-rw-r--r--cns-libs/cnslibs/common/waiter.py34
3 files changed, 191 insertions, 0 deletions
diff --git a/cns-libs/cnslibs/common/naming.py b/cns-libs/cnslibs/common/naming.py
new file mode 100644
index 00000000..b44559ad
--- /dev/null
+++ b/cns-libs/cnslibs/common/naming.py
@@ -0,0 +1,56 @@
+"""Helper functions for working with names for volumes, resources, etc.
+"""
+
+import string
+import random
+import re
+
+# we only use lowercase here because kubernetes requires
+# names to be lowercase or digits, so that is our default
+UNIQUE_CHARS = (string.lowercase + string.digits)
+
+
+def make_unique_label(prefix=None, suffix=None, sep='-',
+ clean=r'[^a-zA-Z0-9]+', unique_len=8,
+ unique_chars=UNIQUE_CHARS):
+ """Generate a unique name string based on an optional prefix,
+ suffix, and pseudo-random set of alphanumeric characters.
+
+ Args:
+ prefix (str): Start of the unique string.
+ suffix (str): End of the unique string.
+ sep (str): Separator string (between sections/invalid chars).
+ clean (str): Reqular expression matching invalid chars.
+ that will be replaced by `sep` if found in the prefix or suffix
+ unique_len (int): Length of the unique part.
+ unique_chars (str): String representing the set of characters
+ the unique part will draw from.
+ Returns:
+ str: The uniqueish string.
+ """
+ cre = re.compile(clean)
+ parts = []
+ if prefix:
+ parts.append(cre.sub(sep, prefix))
+ parts.append(''.join(random.choice(unique_chars)
+ for _ in range(unique_len)))
+ if suffix:
+ parts.append(cre.sub(sep, suffix))
+ return sep.join(parts)
+
+
+def extract_method_name(full_name, keep_class=False):
+ """Given a full test name as returned from TestCase.id() return
+ just the method part or class.method.
+
+ Args:
+ full_name (str): Dot separated name of test.
+ keep_class (str): Retain the class name, if false only the
+ method name will be returned.
+ Returns:
+ str: Method name or class.method_name.
+ """
+ offset = -1
+ if keep_class:
+ offset = -2
+ return '.'.join(full_name.split('.')[offset:])
diff --git a/cns-libs/cnslibs/common/openshift_ops.py b/cns-libs/cnslibs/common/openshift_ops.py
index dbe89d0e..5920d51f 100644
--- a/cns-libs/cnslibs/common/openshift_ops.py
+++ b/cns-libs/cnslibs/common/openshift_ops.py
@@ -166,3 +166,104 @@ def oc_rsh(ocp_node, pod_name, command, log_level=None):
# our docstring
ret, stdout, stderr = g.run(ocp_node, cmd, log_level=log_level)
return (ret, stdout, stderr)
+
+
+def oc_create(ocp_node, filename):
+ """Create a resource based on the contents of the given file name.
+
+ Args:
+ ocp_node (str): Node on which the ocp command will run
+ filename (str): Filename (on remote) to be passed to oc create
+ command
+ Raises:
+ AssertionError: Raised when resource fails to create.
+ """
+ ret, out, err = g.run(ocp_node, ['oc', 'create', '-f', filename])
+ if ret != 0:
+ g.log.error('Failed to create resource: %r; %r', out, err)
+ raise AssertionError('failed to create resource: %r; %r' % (out, err))
+ g.log.info('Created resource from file (%s)', filename)
+ return
+
+
+def oc_delete(ocp_node, rtype, name):
+ """Delete an OCP resource by name.
+
+ Args:
+ ocp_node (str): Node on which the ocp command will run.
+ rtype (str): Name of the resource type (pod, storageClass, etc).
+ name (str): Name of the resource to delete.
+ Raises:
+ AssertionError: Raised when resource fails to create.
+ """
+ ret, out, err = g.run(ocp_node, ['oc', 'delete', rtype, name])
+ if ret != 0:
+ g.log.error('Failed to delete resource: %s, %s: %r; %r',
+ rtype, name, out, err)
+ raise AssertionError('failed to delete resource: %r; %r' % (out, err))
+ g.log.info('Deleted resource: %r %r', rtype, name)
+ return
+
+
+def oc_get_yaml(ocp_node, rtype, name=None, raise_on_error=True):
+ """Get an OCP resource by name.
+
+ Args:
+ ocp_node (str): Node on which the ocp command will run.
+ rtype (str): Name of the resource type (pod, storageClass, etc).
+ name (str|None): Name of the resource to fetch.
+ raise_on_error (bool): If set to true a failure to fetch
+ resource inforation will raise an error, otherwise
+ an empty dict will be returned.
+ Returns:
+ dict: Dictionary containting data about the resource
+ Raises:
+ AssertionError: Raised when unable to get resource and
+ `raise_on_error` is true.
+ """
+ cmd = ['oc', 'get', '-oyaml', rtype]
+ if name is not None:
+ cmd.append(name)
+ ret, out, err = g.run(ocp_node, cmd)
+ if ret != 0:
+ g.log.error('Failed to get %s: %s: %r', rtype, name, err)
+ if raise_on_error:
+ raise AssertionError('failed to get %s: %s: %r'
+ % (rtype, name, err))
+ return {}
+ return yaml.load(out)
+
+
+def oc_get_pvc(ocp_node, name):
+ """Get information on a persistant volume claim.
+
+ Args:
+ ocp_node (str): Node on which the ocp command will run.
+ name (str): Name of the PVC.
+ Returns:
+ dict: Dictionary containting data about the PVC.
+ """
+ return oc_get_yaml(ocp_node, 'pvc', name)
+
+
+def oc_get_pv(ocp_node, name):
+ """Get information on a persistant volume.
+
+ Args:
+ ocp_node (str): Node on which the ocp command will run.
+ name (str): Name of the PV.
+ Returns:
+ dict: Dictionary containting data about the PV.
+ """
+ return oc_get_yaml(ocp_node, 'pv', name)
+
+
+def oc_get_all_pvs(ocp_node):
+ """Get information on all persistent volumes.
+
+ Args:
+ ocp_node (str): Node on which the ocp command will run.
+ Returns:
+ dict: Dictionary containting data about the PV.
+ """
+ return oc_get_yaml(ocp_node, 'pv', None)
diff --git a/cns-libs/cnslibs/common/waiter.py b/cns-libs/cnslibs/common/waiter.py
new file mode 100644
index 00000000..89a264df
--- /dev/null
+++ b/cns-libs/cnslibs/common/waiter.py
@@ -0,0 +1,34 @@
+"""Helper object to encapsulate waiting for timeouts.
+
+Provide a Waiter class which encapsulates the operation
+of doing an action in a loop until a timeout values elapses.
+It aims to avoid having to write boilerplate code comparing times.
+"""
+
+import time
+
+class Waiter(object):
+ """A wait-retry loop as iterable.
+ This object abstracts away the wait logic allowing functions
+ to write the retry logic in a for-loop.
+ """
+ def __init__(self, timeout=60, interval=1):
+ self.timeout = timeout
+ self.interval = interval
+ self.expired = False
+ self._attempt = 0
+ self._start = None
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ if self._start is None:
+ self._start = time.time()
+ if time.time() - self._start > self.timeout:
+ self.expired = True
+ raise StopIteration()
+ if self._attempt != 0:
+ time.sleep(self.interval)
+ self._attempt += 1
+ return self