Add compare header version function to tempest.lib

The motivation for this commit is that some API responses like
backing up a server image return the location of the image_id
in either the response body or the response header depending
on the microversion, e.g. [0]. In the case of server backup
action, image_id is added to response body in microversion 2.45.

Add `compare_version_header_to_request` to
`api_version_utils` to accept a new kwarg called
`operation`. At run time, 'eq' is translated
to the __eq__ attribute of `APIVersionRequest`.
The other operations include le, lt, gt, ge, and ne.

This makes it possible to do for example:

    if api_version_utils.compare_version_header_to_response(
            "X-OpenStack-Nova-API-Version", "2.45", resp, "lt"):
        image1_id = resp['image_id']
    else:
        image1_id = data_utils.parse_image_id(resp['location'])

Which means that if "2.45" < "microversion in resp" then
we can grab the image_id from the response body -- else we have
to grab it from resp.response['location'].

This commit:
  - adds compare_version_header_to_response to api_version_utils
    allowing to compare the request's header microversion
    to an expected microversion
  - modifies test_server_actions to use the new function in tests that
    always assume that the image_id attribute is in the resp header
    (not true across all microversions) -- this can be done to other
    tests in follow-up patch
  - adds related unit tests for all scenarios

[0] https://developer.openstack.org/api-ref/compute/#create-server-back-up-createbackup-action

Change-Id: Ib97e65cca468a09bbeaf68fcfe0e8192674a481e
diff --git a/tempest/lib/common/api_version_utils.py b/tempest/lib/common/api_version_utils.py
index 1371b3c..98f174d 100644
--- a/tempest/lib/common/api_version_utils.py
+++ b/tempest/lib/common/api_version_utils.py
@@ -12,6 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log as logging
 import testtools
 
 from tempest.lib.common import api_version_request
@@ -19,6 +20,7 @@
 
 
 LATEST_MICROVERSION = 'latest'
+LOG = logging.getLogger(__name__)
 
 
 class BaseMicroversionTest(object):
@@ -120,3 +122,60 @@
                                     api_microversion,
                                     response_header))
         raise exceptions.InvalidHTTPResponseHeader(msg)
+
+
+def compare_version_header_to_response(api_microversion_header_name,
+                                       api_microversion,
+                                       response_header,
+                                       operation='eq'):
+    """Compares API microversion in response header to ``api_microversion``.
+
+    Compare the ``api_microversion`` value in response header if microversion
+    header is present in response, otherwise return false.
+
+    To make this function work for APIs which do not return microversion
+    header in response (example compute v2.0), this function does *not* raise
+    InvalidHTTPResponseHeader.
+
+    :param api_microversion_header_name: Microversion header name. Example:
+        'Openstack-Api-Version'.
+    :param api_microversion: Microversion number. Example:
+
+        * '2.10' for the old-style header name, 'X-OpenStack-Nova-API-Version'
+        * 'Compute 2.10' for the new-style header name, 'Openstack-Api-Version'
+
+    :param response_header: Response header where microversion is
+        expected to be present.
+    :param operation: The boolean operation to use to compare the
+        ``api_microversion`` to the microversion in ``response_header``.
+        Can be 'lt', 'eq', 'gt', 'le', 'ne', 'ge'. Default is 'eq'. The
+        operation type should be based on the order of the arguments:
+        ``api_microversion`` <operation> ``response_header`` microversion.
+    :returns: True if the comparison is logically true, else False if the
+        comparison is logically false or if ``api_microversion_header_name`` is
+        missing in the ``response_header``.
+    :raises InvalidParam: If the operation is not lt, eq, gt, le, ne or ge.
+    """
+    api_microversion_header_name = api_microversion_header_name.lower()
+    if api_microversion_header_name not in response_header:
+        return False
+
+    op = getattr(api_version_request.APIVersionRequest,
+                 '__%s__' % operation, None)
+
+    if op is None:
+        msg = ("Operation %s is invalid. Valid options include: lt, eq, gt, "
+               "le, ne, ge." % operation)
+        LOG.debug(msg)
+        raise exceptions.InvalidParam(invalid_param=msg)
+
+    # Remove "volume" from "volume <microversion>", for example, so that the
+    # microversion can be converted to `APIVersionRequest`.
+    api_version = api_microversion.split(' ')[-1]
+    resp_version = response_header[api_microversion_header_name].split(' ')[-1]
+    if not op(
+        api_version_request.APIVersionRequest(api_version),
+        api_version_request.APIVersionRequest(resp_version)):
+        return False
+
+    return True