diff --git a/doc/source/cli/standalone.rst b/doc/source/cli/standalone.rst index ff694a9de..78146bb26 100644 --- a/doc/source/cli/standalone.rst +++ b/doc/source/cli/standalone.rst @@ -29,6 +29,18 @@ exceptions: Check the :doc:`OSC CLI reference ` for a list of available commands. +Inspector support +----------------- + +The standalone ``baremetal`` tool optionally supports the low-level bare metal +introspection API provided by ironic-inspector_. If ironic-inspector-client_ is +installed, its commands_ are automatically available (also without the +``openstack`` prefix). + +.. _ironic-inspector: https://docs.openstack.org/ironic-inspector/ +.. _ironic-inspector-client: https://docs.openstack.org/python-ironic-inspector-client/ +.. _commands: https://docs.openstack.org/python-ironic-inspector-client/latest/cli/index.html + Standalone usage ---------------- @@ -60,6 +72,22 @@ endpoint to connect to. It can be done in three ways: $ export OS_CLOUD=ironic $ baremetal node list +#. `Inspector support`_ works similarly, but the ``clouds.yaml`` option is + called ``baremetal_introspection_endpoint_override``. The two endpoints can + be configured simultaneously, e.g.: + + .. code-block:: bash + + $ cat ~/.config/openstack/clouds.yaml + clouds: + ironic: + auth_type: none + baremetal_endpoint_override: http://127.0.0.1:6385 + baremetal_introspection_endpoint_override: http://127.0.0.1:5050 + $ export OS_CLOUD=ironic + $ baremetal node list + $ baremetal introspection list + .. _clouds.yaml: https://docs.openstack.org/openstacksdk/latest/user/guides/connect_from_config.html Usage with OpenStack diff --git a/ironicclient/shell.py b/ironicclient/shell.py index 67e1d245f..fcb358f0b 100644 --- a/ironicclient/shell.py +++ b/ironicclient/shell.py @@ -10,12 +10,15 @@ # License for the specific language governing permissions and limitations # under the License. -import collections import logging import sys from cliff import app from cliff import commandmanager +try: + import ironic_inspector_client +except ImportError: + ironic_inspector_client = None from openstack import config as os_config from osc_lib import utils import pbr.version @@ -30,18 +33,92 @@ _DEFAULTS = { 'auth_type': 'none', } _TYPE = 'baremetal' +_INSPECTOR_TYPE = 'baremetal-introspection' _DESCRIPTION = 'Bare Metal service (ironic) client' _NAMESPACE = 'openstack.baremetal.v1' +_INSPECTOR_NAMESPACE = 'openstack.baremetal_introspection.v1' +_HELP = _("%(err)s.\n* Use --os-endpoint for standalone %(project)s.\n" + "* Use --os-auth-url and credentials for authentication.\n" + "* Use --os-cloud to load configuration from clouds.yaml\n" + "* See `%(cmd)s --help` for more details") LOG = logging.getLogger(__name__) -ClientManager = collections.namedtuple('ClientManager', ['baremetal']) + +class ClientManager(object): + + def __init__(self, cloud_region, options): + self.cloud_region = cloud_region + self.options = options + self._ironic = None + self._inspector = None + + @property + def baremetal(self): + if self._ironic is None: + self._ironic = self._create_ironic_client() + return self._ironic + + @property + def baremetal_introspection(self): + if self._inspector is None: + self._inspector = self._create_inspector_client() + return self._inspector + + def _create_ironic_client(self): + api_version = self.options.os_baremetal_api_version + allow_api_version_downgrade = False + if not api_version: + api_version = self.cloud_region.get_default_microversion(_TYPE) + if not api_version: + api_version = http.LATEST_VERSION + allow_api_version_downgrade = True + LOG.debug( + 'Using bare metal API version %s, downgrade %s', api_version, + 'allowed' if allow_api_version_downgrade else 'disallowed') + + # NOTE(dtantsur): endpoint_override is required to respect settings in + # clouds.yaml, such as baremetal_endpoint_override. + endpoint_override = self.cloud_region.get_endpoint(_TYPE) + try: + return client.Client( + os_ironic_api_version=api_version, + allow_api_version_downgrade=allow_api_version_downgrade, + session=self.cloud_region.get_session(), + region_name=self.cloud_region.get_region_name(_TYPE), + endpoint_override=endpoint_override, + max_retries=self.options.max_retries, + retry_interval=self.options.retry_interval, + ) + except exc.EndpointNotFound as e: + # Re-raise with a more obvious message. + raise exc.EndpointNotFound(_HELP % {'err': e, 'cmd': sys.argv[0], + 'project': 'ironic'}) + + def _create_inspector_client(self): + assert ironic_inspector_client is not None, \ + 'BUG: _create_inspector_client called without inspector client' + # NOTE(dtantsur): endpoint_override is required to respect settings in + # clouds.yaml, such as baremetal_introspection_endpoint_override. + endpoint_override = self.cloud_region.get_endpoint(_INSPECTOR_TYPE) + try: + return ironic_inspector_client.ClientV1( + inspector_url=endpoint_override, + session=self.cloud_region.get_session(), + region_name=self.cloud_region.get_region_name(_INSPECTOR_TYPE), + ) + except ironic_inspector_client.EndpointNotFound as e: + # Re-raise with a more obvious message. + raise exc.EndpointNotFound(_HELP % {'err': e, 'cmd': sys.argv[0], + 'project': 'ironic-inspector'}) class CommandManager(commandmanager.CommandManager): def load_commands(self, namespace): super(CommandManager, self).load_commands(namespace) + if ironic_inspector_client is not None: + super(CommandManager, self).load_commands(_INSPECTOR_NAMESPACE) # Stip the 'baremetal' prefix used in OSC prefix = 'baremetal ' prefix_len = len(prefix) @@ -94,40 +171,8 @@ class App(app.App): def initialize_app(self, argv): super(App, self).initialize_app(argv) self.cloud_region = self.config.get_one(argparse=self.options) - - api_version = self.options.os_baremetal_api_version - allow_api_version_downgrade = False - if not api_version: - api_version = self.cloud_region.get_default_microversion(_TYPE) - if not api_version: - api_version = http.LATEST_VERSION - allow_api_version_downgrade = True - LOG.debug('Using API version %s, downgrade %s', api_version, - 'allowed' if allow_api_version_downgrade else 'disallowed') - - # NOTE(dtantsur): endpoint_override is required to respect settings in - # clouds.yaml, such as baremetal_endpoint_override. - endpoint_override = self.cloud_region.get_endpoint(_TYPE) - try: - self.client = client.Client( - os_ironic_api_version=api_version, - allow_api_version_downgrade=allow_api_version_downgrade, - session=self.cloud_region.get_session(), - region_name=self.cloud_region.get_region_name(_TYPE), - endpoint_override=endpoint_override, - max_retries=self.options.max_retries, - retry_interval=self.options.retry_interval, - ) - except exc.EndpointNotFound as e: - # Re-raise with a more obvious message. - msg = _("%(err)s.\n* Use --os-endpoint for standalone ironic.\n" - "* Use --os-auth-url and credentials for authentication.\n" - "* Use --os-cloud to load configuration from clouds.yaml\n" - "* See `%(cmd)s --help` for more details") - raise exc.EndpointNotFound(msg % {'err': e, 'cmd': sys.argv[0]}) - # Compatibility with OSC - self.client_manager = ClientManager(self.client) + self.client_manager = ClientManager(self.cloud_region, self.options) def main(argv=sys.argv[1:]):