Basic support for API versions
Currently only one version (1, 0) is supported, and no header is send to the server. However, this change allows calling code to fix version (1, 0) already. Change-Id: I1636e39d5a4c4344bd3e159d559886607d0c285e Closes-Bug: #1465359
This commit is contained in:
10
README.rst
10
README.rst
@@ -46,6 +46,16 @@ Every call accepts additional optional arguments:
|
||||
* ``base_url`` **ironic-inspector** API endpoint, defaults to
|
||||
``127.0.0.1:5050``,
|
||||
* ``auth_token`` Keystone authentication token.
|
||||
* ``api_version`` requested API version; can be a tuple (MAJ, MIN), string
|
||||
"MAJ.MIN" or integer (only major). Right now only (1, 0) is supported, other
|
||||
versions will raise an exception. Defaults to ``DEFAULT_API_VERSION``.
|
||||
|
||||
Two constants are exposed by the client:
|
||||
|
||||
* ``DEFAULT_API_VERSION`` server API version used by default.
|
||||
* ``MAX_API_VERSION`` maximum API version this client was designed to work
|
||||
with. Right now providing bigger value for ``api_version`` argument raises
|
||||
on exception, this limitation may be lifted later.
|
||||
|
||||
Refer to HTTP-API.rst_ for information on the **ironic-inspector** HTTP API.
|
||||
|
||||
|
||||
@@ -12,3 +12,4 @@
|
||||
# limitations under the License.
|
||||
|
||||
from .client import introspect, get_status # noqa
|
||||
from .client import DEFAULT_API_VERSION, MAX_API_VERSION # noqa
|
||||
|
||||
@@ -26,6 +26,10 @@ _ERROR_ENCODING = 'utf-8'
|
||||
LOG = logging.getLogger('ironic_inspector_client')
|
||||
|
||||
|
||||
DEFAULT_API_VERSION = (1, 0)
|
||||
MAX_API_VERSION = (1, 0)
|
||||
|
||||
|
||||
def _prepare(base_url, auth_token):
|
||||
base_url = (base_url or _DEFAULT_URL).rstrip('/')
|
||||
if not base_url.endswith('v1'):
|
||||
@@ -34,6 +38,30 @@ def _prepare(base_url, auth_token):
|
||||
return base_url, headers
|
||||
|
||||
|
||||
def _check_api_version(api_version):
|
||||
if isinstance(api_version, int):
|
||||
api_version = (api_version, 0)
|
||||
if isinstance(api_version, six.string_types):
|
||||
try:
|
||||
api_version = tuple(int(x) for x in api_version.split('.'))
|
||||
except (ValueError, TypeError):
|
||||
raise ValueError(_("Malformed API version: expect tuple, string "
|
||||
"in form of X.Y or integer"))
|
||||
api_version = tuple(api_version)
|
||||
if not all(isinstance(x, int) for x in api_version):
|
||||
raise TypeError(_("All API version components should be integers"))
|
||||
if len(api_version) != 2:
|
||||
raise ValueError(_("API version should be of length 2"))
|
||||
|
||||
# TODO(dtantsur): support more than one API version
|
||||
if api_version != (1, 0):
|
||||
raise RuntimeError(_("Unsupported API version %s, only (1, 0) is "
|
||||
"supported in this version of client"),
|
||||
api_version)
|
||||
|
||||
return api_version
|
||||
|
||||
|
||||
class ClientError(requests.HTTPError):
|
||||
"""Error returned from a server."""
|
||||
def __init__(self, response):
|
||||
@@ -56,7 +84,8 @@ class ClientError(requests.HTTPError):
|
||||
|
||||
|
||||
def introspect(uuid, base_url=None, auth_token=None,
|
||||
new_ipmi_password=None, new_ipmi_username=None):
|
||||
new_ipmi_password=None, new_ipmi_username=None,
|
||||
api_version=DEFAULT_API_VERSION):
|
||||
"""Start introspection for a node.
|
||||
|
||||
:param uuid: node uuid
|
||||
@@ -68,6 +97,8 @@ def introspect(uuid, base_url=None, auth_token=None,
|
||||
:param new_ipmi_username: if new_ipmi_password is set, this values sets
|
||||
new IPMI user name. Defaults to one in
|
||||
driver_info.
|
||||
:param api_version: requested ironic-inspector API version, defaults to
|
||||
``DEFAULT_API_VERSION`` attribute.
|
||||
:raises: ClientError on error reported from a server
|
||||
:raises: *requests* library exception on connection problems.
|
||||
"""
|
||||
@@ -75,6 +106,7 @@ def introspect(uuid, base_url=None, auth_token=None,
|
||||
raise TypeError(_("Expected string for uuid argument, got %r") % uuid)
|
||||
if new_ipmi_username and not new_ipmi_password:
|
||||
raise ValueError(_("Setting IPMI user name requires a new password"))
|
||||
_check_api_version(api_version)
|
||||
|
||||
base_url, headers = _prepare(base_url, auth_token)
|
||||
params = {'new_ipmi_username': new_ipmi_username,
|
||||
@@ -84,7 +116,8 @@ def introspect(uuid, base_url=None, auth_token=None,
|
||||
ClientError.raise_if_needed(res)
|
||||
|
||||
|
||||
def get_status(uuid, base_url=None, auth_token=None):
|
||||
def get_status(uuid, base_url=None, auth_token=None,
|
||||
api_version=DEFAULT_API_VERSION):
|
||||
"""Get introspection status for a node.
|
||||
|
||||
New in ironic-inspector version 1.0.0.
|
||||
@@ -92,11 +125,14 @@ def get_status(uuid, base_url=None, auth_token=None):
|
||||
:param base_url: *ironic-inspector* URL in form: http://host:port[/ver],
|
||||
defaults to ``http://<current host>:5050/v1``.
|
||||
:param auth_token: Keystone authentication token.
|
||||
:param api_version: requested ironic-inspector API version, defaults to
|
||||
``DEFAULT_API_VERSION`` attribute.
|
||||
:raises: ClientError on error reported from a server
|
||||
:raises: *requests* library exception on connection problems.
|
||||
"""
|
||||
if not isinstance(uuid, six.string_types):
|
||||
raise TypeError(_("Expected string for uuid argument, got %r") % uuid)
|
||||
_check_api_version(api_version)
|
||||
|
||||
base_url, headers = _prepare(base_url, auth_token)
|
||||
res = requests.get("%s/introspection/%s" % (base_url, uuid),
|
||||
|
||||
@@ -137,3 +137,26 @@ class TestGetStatus(unittest.TestCase):
|
||||
mock_post.return_value.content = b'42'
|
||||
self.assertRaisesRegexp(client.ClientError, "42",
|
||||
client.get_status, self.uuid)
|
||||
|
||||
|
||||
class TestCheckVesion(unittest.TestCase):
|
||||
def test_tuple(self):
|
||||
self.assertEqual((1, 0), client._check_api_version((1, 0)))
|
||||
|
||||
def test_int(self):
|
||||
self.assertEqual((1, 0), client._check_api_version(1))
|
||||
|
||||
def test_str(self):
|
||||
self.assertEqual((1, 0), client._check_api_version("1.0"))
|
||||
|
||||
def test_invalid_tuple(self):
|
||||
self.assertRaises(TypeError, client._check_api_version, (1, "x"))
|
||||
self.assertRaises(ValueError, client._check_api_version, (1, 2, 3))
|
||||
|
||||
def test_invalid_str(self):
|
||||
self.assertRaises(ValueError, client._check_api_version, "a.b")
|
||||
self.assertRaises(ValueError, client._check_api_version, "1.2.3")
|
||||
self.assertRaises(ValueError, client._check_api_version, "foo")
|
||||
|
||||
def test_only_1_0_supported(self):
|
||||
self.assertRaises(RuntimeError, client._check_api_version, (1, 1))
|
||||
|
||||
Reference in New Issue
Block a user