diff --git a/README.rst b/README.rst index 441ab8e..0d91e39 100644 --- a/README.rst +++ b/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. diff --git a/ironic_inspector_client/__init__.py b/ironic_inspector_client/__init__.py index a53d2ea..6b0a7fa 100644 --- a/ironic_inspector_client/__init__.py +++ b/ironic_inspector_client/__init__.py @@ -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 diff --git a/ironic_inspector_client/client.py b/ironic_inspector_client/client.py index 7ef6120..f1f750d 100644 --- a/ironic_inspector_client/client.py +++ b/ironic_inspector_client/client.py @@ -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://: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), diff --git a/ironic_inspector_client/test/test_client.py b/ironic_inspector_client/test/test_client.py index d09a445..c525d0f 100644 --- a/ironic_inspector_client/test/test_client.py +++ b/ironic_inspector_client/test/test_client.py @@ -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))