Try to fetch inspector URL from the service catalog

Change-Id: I710a2dc8dea3ad6b8a9bb2e4aac9225658dd8d1d
Closes-Bug: #1391865
This commit is contained in:
Dmitry Tantsur 2016-02-09 13:56:01 +01:00
parent 1709ba6bd6
commit 81bfb6d550
6 changed files with 81 additions and 22 deletions

View File

@ -17,14 +17,16 @@ Python API
To use Python API first create a ``ClientV1`` object::
import ironic_inspector_client
url = 'http://HOST:5050'
client = ironic_inspector_client.ClientV1(session=keystone_session,
inspector_url=url)
client = ironic_inspector_client.ClientV1(session=keystone_session)
This code creates a client with API version *1.0* and a given Keystone session.
If ``inspector_url`` is missing, local host is assumed for now. Service
catalog will be used in the future.
The service URL is fetched from the service catalog in this case. Optional
arguments ``service_type``, ``interface`` and ``region_name`` can be provided
to modify how the URL is looked up.
If the catalog lookup fails, the local host with port 5050 is tried. However,
this behaviour is deprecated and should not be relied on.
Also an explicit ``inspector_url`` can be passed to bypass service catalog.
Optional ``api_version`` argument is a minimum API version that a server must
support. It can be a tuple (MAJ, MIN), string "MAJ.MIN" or integer
@ -201,19 +203,20 @@ functionality:
* Starting introspection::
ironic_inspector_client.introspect(uuid[, new_ipmi_password[, new_ipmi_username]][, base_url][, api_version][, session])
ironic_inspector_client.introspect(uuid[, new_ipmi_password[, new_ipmi_username]][, base_url][, api_version] **)
* Getting introspection status::
ironic_inspector_client.get_status(uuid[, base_url][, api_version[, session]])
ironic_inspector_client.get_status(uuid[, base_url][, api_version] **)
* Getting API versions supported by a server::
ironic_inspector_client.server_api_versions([base_url][, session])
ironic_inspector_client.server_api_versions([base_url] **)
Here ``base_url`` argument is the same as ``inspector_url`` argument to
``ClientV1`` constructor. The first 2 functions also accept deprecated
``auth_token`` argument, which should not be used.
Here ``base_url`` argument is the same as ``inspector_url`` argument
to the ``ClientV1`` constructor. Keyword arguments are passed to the client
constructor intact. The first 2 functions also accept deprecated ``auth_token``
argument, which should not be used.
.. _Gerrit Workflow: http://docs.openstack.org/infra/manual/developers.html#development-workflow

View File

@ -28,7 +28,7 @@ VersionNotSupported = http.VersionNotSupported
def introspect(uuid, base_url=None, auth_token=None,
new_ipmi_password=None, new_ipmi_username=None,
api_version=DEFAULT_API_VERSION, session=None):
api_version=DEFAULT_API_VERSION, session=None, **kwargs):
"""Start introspection for a node.
:param uuid: node uuid
@ -43,18 +43,19 @@ def introspect(uuid, base_url=None, auth_token=None,
:param api_version: requested Ironic Inspector API version, defaults to
``DEFAULT_API_VERSION`` attribute.
:param session: keystone session.
:param kwargs: keyword arguments to pass to the ClientV1 constructor.
:raises: ClientError on error reported from a server
:raises: VersionNotSupported if requested api_version is not supported
:raises: *requests* library exception on connection problems.
"""
c = v1.ClientV1(api_version=api_version, auth_token=auth_token,
inspector_url=base_url, session=session)
inspector_url=base_url, session=session, **kwargs)
return c.introspect(uuid, new_ipmi_username=new_ipmi_username,
new_ipmi_password=new_ipmi_password)
def get_status(uuid, base_url=None, auth_token=None,
api_version=DEFAULT_API_VERSION, session=None):
api_version=DEFAULT_API_VERSION, session=None, **kwargs):
"""Get introspection status for a node.
New in Ironic Inspector version 1.0.0.
@ -65,27 +66,29 @@ def get_status(uuid, base_url=None, auth_token=None,
:param api_version: requested Ironic Inspector API version, defaults to
``DEFAULT_API_VERSION`` attribute.
:param session: keystone session.
:param kwargs: keyword arguments to pass to the ClientV1 constructor.
:raises: ClientError on error reported from a server
:raises: VersionNotSupported if requested api_version is not supported
:raises: *requests* library exception on connection problems.
"""
c = v1.ClientV1(api_version=api_version, auth_token=auth_token,
inspector_url=base_url, session=session)
inspector_url=base_url, session=session, **kwargs)
return c.get_status(uuid)
def server_api_versions(base_url=None, session=None):
def server_api_versions(base_url=None, session=None, **kwargs):
"""Get minimum and maximum supported API versions from a server.
:param base_url: *Ironic Inspector* URL in form: http://host:port[/ver],
defaults to ``http://<current host>:5050/v1``.
:param session: keystone session (authentication is not required).
:param kwargs: keyword arguments to pass to the BaseClient constructor.
:return: tuple (minimum version, maximum version) each version is returned
as a tuple (X, Y)
:raises: *requests* library exception on connection problems.
:raises: ValueError if returned version cannot be parsed
"""
c = http.BaseClient(1, inspector_url=base_url, session=session)
c = http.BaseClient(1, inspector_url=base_url, session=session, **kwargs)
return c.server_api_versions()

View File

@ -17,6 +17,7 @@ import json
import logging
from keystoneclient.auth import token_endpoint
from keystoneclient import exceptions as ks_exc
from keystoneclient import session as ks_session
from oslo_utils import netutils
import requests
@ -84,18 +85,24 @@ class BaseClient(object):
"""Base class for clients, provides common HTTP code."""
def __init__(self, api_version, inspector_url=None, auth_token=None,
session=None):
session=None, service_type='baremetal-introspection',
interface=None, region_name=None):
"""Create a client.
:param api_version: minimum API version that must be supported by
the server
:param inspector_url: *Ironic Inspector* URL in form:
http://host:port[/ver],
http://host:port[/ver]. When session is provided, defaults to
service URL from the catalog. As a last resort
defaults to ``http://<current host>:5050/v<MAJOR>``.
:param auth_token: authentication token (deprecated, use session)
:param session: existing keystone session
:param service_type: service type to use when looking up the URL
:param interface: interface type (public, internal, etc) to use when
looking up the URL
:param region_name: region name to use when looking up the URL
"""
self._base_url = (inspector_url or _DEFAULT_URL).rstrip('/')
self._base_url = inspector_url or _DEFAULT_URL
self._auth_token = auth_token
if session is None:
@ -110,7 +117,18 @@ class BaseClient(object):
self._session = ks_session.Session(auth)
else:
self._session = session
if not inspector_url:
try:
self._base_url = session.get_endpoint(
service_type=service_type,
interface=interface,
region_name=region_name) or _DEFAULT_URL
except ks_exc.EndpointNotFound:
LOG.warning(_LW('Endpoint for service %s was not found, '
'falling back to local host on port 5050'),
service_type)
self._base_url = self._base_url.rstrip('/')
self._api_version = self._check_api_version(api_version)
self._version_str = '%d.%d' % self._api_version
ver_postfix = '/v%d' % self._api_version[0]

View File

@ -43,7 +43,9 @@ def make_client(instance):
return ironic_inspector_client.ClientV1(
inspector_url=instance.get_configuration().get('inspector_url'),
session=instance.session,
api_version=instance._api_version[API_NAME])
api_version=instance._api_version[API_NAME],
interface=instance._interface,
region_name=instance._region_name)
def build_option_parser(parser):

View File

@ -14,6 +14,7 @@
import json
import unittest
from keystoneclient import exceptions
from keystoneclient import session
import mock
@ -107,6 +108,7 @@ class TestRequest(unittest.TestCase):
super(TestRequest, self).setUp()
self.headers = {http._VERSION_HEADER: '1.0'}
self.session = mock.Mock(spec=session.Session)
self.session.get_endpoint.return_value = self.base_url
self.req = self.session.request
self.req.return_value.status_code = 200
@ -129,6 +131,31 @@ class TestRequest(unittest.TestCase):
self.assertIs(self.req.return_value, res)
self.req.assert_called_once_with(self.base_url + '/foo/bar', 'get',
raise_exc=False, headers=self.headers)
self.session.get_endpoint.assert_called_once_with(
service_type='baremetal-introspection',
interface=None, region_name=None)
def test_ok_no_endpoint(self):
self.session.get_endpoint.return_value = None
res = self.get_client().request('get', '/foo/bar')
self.assertIs(self.req.return_value, res)
self.req.assert_called_once_with(self.base_url + '/foo/bar', 'get',
raise_exc=False, headers=self.headers)
self.session.get_endpoint.assert_called_once_with(
service_type='baremetal-introspection',
interface=None, region_name=None)
def test_ok_endpoint_not_found(self):
self.session.get_endpoint.side_effect = exceptions.EndpointNotFound()
res = self.get_client().request('get', '/foo/bar')
self.assertIs(self.req.return_value, res)
self.req.assert_called_once_with(self.base_url + '/foo/bar', 'get',
raise_exc=False, headers=self.headers)
self.session.get_endpoint.assert_called_once_with(
service_type='baremetal-introspection',
interface=None, region_name=None)
@mock.patch.object(session.Session, 'request', autospec=True,
**{'return_value.status_code': 200})

View File

@ -0,0 +1,6 @@
---
features:
- Inspector service URL can now be fetched from the service catalog.
deprecations:
- Using default service URL of locahost:5050 is deprecated now. Either use
the service catalog or provide an explicit URL.