Ken'ichi Ohmichi | 4d237e7 | 2015-11-05 06:32:33 +0000 | [diff] [blame] | 1 | # Copyright 2014 IBM Corp. |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| 4 | # not use this file except in compliance with the License. You may obtain |
| 5 | # a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 12 | # License for the specific language governing permissions and limitations |
| 13 | # under the License. |
| 14 | |
| 15 | import re |
| 16 | |
Ghanshyam | 1f47cf9 | 2016-02-25 04:57:18 +0900 | [diff] [blame] | 17 | from tempest.lib import exceptions |
Ken'ichi Ohmichi | 4d237e7 | 2015-11-05 06:32:33 +0000 | [diff] [blame] | 18 | |
| 19 | |
| 20 | # Define the minimum and maximum version of the API across all of the |
| 21 | # REST API. The format of the version is: |
| 22 | # X.Y where: |
| 23 | # |
| 24 | # - X will only be changed if a significant backwards incompatible API |
| 25 | # change is made which affects the API as whole. That is, something |
| 26 | # that is only very very rarely incremented. |
| 27 | # |
| 28 | # - Y when you make any change to the API. Note that this includes |
| 29 | # semantic changes which may not affect the input or output formats or |
| 30 | # even originate in the API code layer. We are not distinguishing |
| 31 | # between backwards compatible and backwards incompatible changes in |
| 32 | # the versioning system. It must be made clear in the documentation as |
| 33 | # to what is a backwards compatible change and what is a backwards |
| 34 | # incompatible one. |
| 35 | |
| 36 | class APIVersionRequest(object): |
| 37 | """This class represents an API Version Request. |
| 38 | |
| 39 | This class provides convenience methods for manipulation |
| 40 | and comparison of version numbers that we need to do to |
| 41 | implement microversions. |
Ghanshyam | 1f47cf9 | 2016-02-25 04:57:18 +0900 | [diff] [blame] | 42 | |
| 43 | :param version_string: String representation of APIVersionRequest. |
| 44 | Correct format is 'X.Y', where 'X' and 'Y' are int values. |
| 45 | None value should be used to create Null APIVersionRequest, |
| 46 | which is equal to 0.0 |
Ken'ichi Ohmichi | 4d237e7 | 2015-11-05 06:32:33 +0000 | [diff] [blame] | 47 | """ |
| 48 | |
| 49 | # NOTE: This 'latest' version is a magic number, we assume any |
| 50 | # projects(Nova, etc.) never achieve this number. |
| 51 | latest_ver_major = 99999 |
| 52 | latest_ver_minor = 99999 |
| 53 | |
| 54 | def __init__(self, version_string=None): |
Ghanshyam | 1f47cf9 | 2016-02-25 04:57:18 +0900 | [diff] [blame] | 55 | """Create an API version request object.""" |
Ghanshyam | 395c5b5 | 2015-12-04 17:27:16 +0900 | [diff] [blame] | 56 | # NOTE(gmann): 'version_string' as String "None" will be considered as |
| 57 | # invalid version string. |
Ken'ichi Ohmichi | 4d237e7 | 2015-11-05 06:32:33 +0000 | [diff] [blame] | 58 | self.ver_major = 0 |
| 59 | self.ver_minor = 0 |
| 60 | |
| 61 | if version_string is not None: |
| 62 | match = re.match(r"^([1-9]\d*)\.([1-9]\d*|0)$", |
| 63 | version_string) |
| 64 | if match: |
| 65 | self.ver_major = int(match.group(1)) |
| 66 | self.ver_minor = int(match.group(2)) |
| 67 | elif version_string == 'latest': |
| 68 | self.ver_major = self.latest_ver_major |
| 69 | self.ver_minor = self.latest_ver_minor |
| 70 | else: |
| 71 | raise exceptions.InvalidAPIVersionString( |
| 72 | version=version_string) |
| 73 | |
| 74 | def __str__(self): |
| 75 | """Debug/Logging representation of object.""" |
| 76 | return ("API Version Request: %s" % self.get_string()) |
| 77 | |
| 78 | def is_null(self): |
Ghanshyam | 1f47cf9 | 2016-02-25 04:57:18 +0900 | [diff] [blame] | 79 | """Checks whether version is null. |
| 80 | |
| 81 | Return True if version object is null otherwise False. |
| 82 | |
| 83 | :returns: boolean |
| 84 | """ |
Ken'ichi Ohmichi | 4d237e7 | 2015-11-05 06:32:33 +0000 | [diff] [blame] | 85 | return self.ver_major == 0 and self.ver_minor == 0 |
| 86 | |
| 87 | def _format_type_error(self, other): |
| 88 | return TypeError("'%(other)s' should be an instance of '%(cls)s'" % |
| 89 | {"other": other, "cls": self.__class__}) |
| 90 | |
| 91 | def __lt__(self, other): |
| 92 | if not isinstance(other, APIVersionRequest): |
| 93 | raise self._format_type_error(other) |
| 94 | |
| 95 | return ((self.ver_major, self.ver_minor) < |
| 96 | (other.ver_major, other.ver_minor)) |
| 97 | |
| 98 | def __eq__(self, other): |
| 99 | if not isinstance(other, APIVersionRequest): |
| 100 | raise self._format_type_error(other) |
| 101 | |
| 102 | return ((self.ver_major, self.ver_minor) == |
| 103 | (other.ver_major, other.ver_minor)) |
| 104 | |
| 105 | def __gt__(self, other): |
| 106 | if not isinstance(other, APIVersionRequest): |
| 107 | raise self._format_type_error(other) |
| 108 | |
| 109 | return ((self.ver_major, self.ver_minor) > |
| 110 | (other.ver_major, other.ver_minor)) |
| 111 | |
| 112 | def __le__(self, other): |
| 113 | return self < other or self == other |
| 114 | |
| 115 | def __ne__(self, other): |
| 116 | return not self.__eq__(other) |
| 117 | |
| 118 | def __ge__(self, other): |
| 119 | return self > other or self == other |
| 120 | |
| 121 | def matches(self, min_version, max_version): |
| 122 | """Matches the version object. |
| 123 | |
| 124 | Returns whether the version object represents a version |
| 125 | greater than or equal to the minimum version and less than |
| 126 | or equal to the maximum version. |
| 127 | |
Ghanshyam | 1f47cf9 | 2016-02-25 04:57:18 +0900 | [diff] [blame] | 128 | :param min_version: Minimum acceptable version. |
| 129 | :param max_version: Maximum acceptable version. |
| 130 | :returns: boolean |
Ken'ichi Ohmichi | 4d237e7 | 2015-11-05 06:32:33 +0000 | [diff] [blame] | 131 | |
| 132 | If min_version is null then there is no minimum limit. |
| 133 | If max_version is null then there is no maximum limit. |
| 134 | If self is null then raise ValueError |
| 135 | """ |
| 136 | |
| 137 | if self.is_null(): |
| 138 | raise ValueError |
| 139 | if max_version.is_null() and min_version.is_null(): |
| 140 | return True |
| 141 | elif max_version.is_null(): |
| 142 | return min_version <= self |
| 143 | elif min_version.is_null(): |
| 144 | return self <= max_version |
| 145 | else: |
| 146 | return min_version <= self <= max_version |
| 147 | |
| 148 | def get_string(self): |
| 149 | """Version string representation. |
| 150 | |
| 151 | Converts object to string representation which if used to create |
| 152 | an APIVersionRequest object results in the same version request. |
| 153 | """ |
| 154 | if self.is_null(): |
| 155 | return None |
| 156 | if (self.ver_major == self.latest_ver_major and |
| 157 | self.ver_minor == self.latest_ver_minor): |
| 158 | return 'latest' |
| 159 | return "%s.%s" % (self.ver_major, self.ver_minor) |