manual introspection trigger command
Change-Id: I64e66682c1e54f6edc260a22f46f5f6df8e85af1 Story: 2005896 Task: 33756
This commit is contained in:
parent
94048fe97e
commit
696606f682
@ -60,6 +60,33 @@ full endpoint of Ironic Inspector, for example::
|
||||
|
||||
Make sure your DHCP environment is set to boot IPA by default.
|
||||
|
||||
For the cases where the infrastructure operator and cloud user are the same,
|
||||
an additional tool exists that can be installed alongside the agent inside
|
||||
a running instance. This is the ``ironic-collect-introspection-data``
|
||||
command which allows for a node in ``ACTIVE`` state to publish updated
|
||||
introspection data to ironic-inspector. This ability requires ironic-inspector
|
||||
to be configured with ``[processing]permit_active_introspection`` set to
|
||||
``True``. For example::
|
||||
|
||||
ironic-collect-introspection-data --inspection_callback_url http://IP:5050/v1/continue
|
||||
|
||||
Alternatively, this command may also be used with multicast DNS
|
||||
functionality to identify the `Ironic Inspector`_ service endpoint.
|
||||
For example::
|
||||
|
||||
ironic-collect-introspection-data --inspection_callback_url mdns
|
||||
|
||||
An additional daemon mode may be useful for some operators who wish to receive
|
||||
regular updates, in the form of the ``[DEFAULT]introspection_daemon`` boolean
|
||||
configuration option.
|
||||
For example::
|
||||
|
||||
ironic-collect-introspection-data --inspection_callback_url mdns --introspection_daemon
|
||||
|
||||
The above command will attempt to connect to introspection and will then enter
|
||||
a loop to publish every 300 seconds. This can be tuned with the
|
||||
``[DEFAULT]introspection_daemon_post_interval`` configuration option.
|
||||
|
||||
.. _Ironic Inspector: https://docs.openstack.org/ironic-inspector/
|
||||
|
||||
Hardware Inventory
|
||||
|
@ -388,8 +388,13 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
|
||||
# lookup will fail due to unknown MAC.
|
||||
uuid = None
|
||||
if cfg.CONF.inspection_callback_url:
|
||||
uuid = inspector.inspect()
|
||||
|
||||
try:
|
||||
# Attempt inspection. This may fail, and previously
|
||||
# an error would be logged.
|
||||
uuid = inspector.inspect()
|
||||
except errors.InspectionError as e:
|
||||
LOG.error('Failed to perform inspection: %(err)s',
|
||||
{'error': e})
|
||||
if self.api_url:
|
||||
self._wait_for_interface()
|
||||
content = self.api_client.lookup_node(
|
||||
|
30
ironic_python_agent/cmd/inspect.py
Normal file
30
ironic_python_agent/cmd/inspect.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright 2013 Rackspace, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
|
||||
from ironic_python_agent import inspect as inspection
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def run():
|
||||
"""Entrypoint for IronicPythonAgent."""
|
||||
log.register_options(CONF)
|
||||
CONF(args=sys.argv[1:])
|
||||
log.setup(CONF, 'ironic-python-agent')
|
||||
inspection.IronicInspection().run()
|
@ -217,6 +217,18 @@ cli_opts = [
|
||||
'Must be provided together with "certfile" option. '
|
||||
'Default is to not present any client certificates to '
|
||||
'the server.'),
|
||||
cfg.BoolOpt('introspection_daemon',
|
||||
default=False,
|
||||
help='When the ``ironic-collect-introspection-data`` '
|
||||
'command is executed, continue running as '
|
||||
'a background process and continue to post data '
|
||||
'to the bare metal inspection service.'),
|
||||
cfg.IntOpt('introspection_daemon_post_interval',
|
||||
default=300,
|
||||
help='The interval in seconds by which to transmit data to '
|
||||
'the bare metal introspection service when the '
|
||||
'``ironic-collect-introspection-data`` program is '
|
||||
'executing in daemon mode.'),
|
||||
]
|
||||
|
||||
CONF.register_cli_opts(cli_opts)
|
||||
|
86
ironic_python_agent/inspect.py
Normal file
86
ironic_python_agent/inspect.py
Normal file
@ -0,0 +1,86 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import select
|
||||
import threading
|
||||
|
||||
from ironic_lib import exception
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
|
||||
from ironic_python_agent import errors
|
||||
from ironic_python_agent import inspector
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class IronicInspection(threading.Thread):
|
||||
"""Class for manual inspection functionality."""
|
||||
|
||||
def __init__(self):
|
||||
super(IronicInspection, self).__init__()
|
||||
if bool(cfg.CONF.keyfile) != bool(cfg.CONF.certfile):
|
||||
LOG.warning("Only one of 'keyfile' and 'certfile' options is "
|
||||
"defined in config file. Its value will be ignored.")
|
||||
|
||||
def _run(self):
|
||||
try:
|
||||
daemon_mode = cfg.CONF.introspection_daemon
|
||||
post_interval = cfg.CONF.introspection_daemon_post_interval
|
||||
|
||||
inspector.inspect()
|
||||
if not daemon_mode:
|
||||
# No reason to continue unless we're in daemon mode.
|
||||
return
|
||||
|
||||
self.reader, self.writer = os.pipe()
|
||||
p = select.poll()
|
||||
p.register(self.reader)
|
||||
|
||||
try:
|
||||
while daemon_mode:
|
||||
LOG.info('Sleeping until next check-in.')
|
||||
# TODO(TheJulia): It would likely be good to introduce
|
||||
# some jitter into this at some point...
|
||||
if p.poll(post_interval * 1000):
|
||||
if os.read(self.reader, 1).decode() == 'a':
|
||||
break
|
||||
try:
|
||||
inspector.inspect()
|
||||
except errors.InspectionError as e:
|
||||
# Failures happen, no reason to exit as
|
||||
# the failure could be intermittent.
|
||||
LOG.warning('Error reporting introspection '
|
||||
'data: %(err)s',
|
||||
{'err': e})
|
||||
except exception.ServiceLookupFailure as e:
|
||||
# Likely a mDNS lookup failure. We should
|
||||
# keep retrying.
|
||||
LOG.error('Error looking up introspection '
|
||||
'endpoint: %(err)s',
|
||||
{'err': e})
|
||||
|
||||
finally:
|
||||
os.close(self.reader)
|
||||
os.close(self.writer)
|
||||
self.reader = None
|
||||
self.writer = None
|
||||
except errors.InspectionError as e:
|
||||
msg = "Inspection failed: %s" % e
|
||||
raise errors.InspectionError(msg)
|
||||
|
||||
def run(self):
|
||||
"""Run Inspection."""
|
||||
if not cfg.CONF.inspection_callback_url:
|
||||
cfg.CONF.set_override('inspection_callback_url', 'mdns')
|
||||
self._run()
|
@ -104,8 +104,8 @@ def inspect():
|
||||
failures.raise_if_needed()
|
||||
|
||||
if resp is None:
|
||||
LOG.info('stopping inspection, as inspector returned an error')
|
||||
return
|
||||
raise errors.InspectionError('stopping inspection, as inspector '
|
||||
'returned an error')
|
||||
|
||||
LOG.info('inspection finished successfully')
|
||||
return resp.get('uuid')
|
||||
|
@ -137,11 +137,11 @@ class TestInspect(base.IronicAgentTest):
|
||||
mock_call.return_value = None
|
||||
mock_ext_mgr.return_value = [self.mock_ext]
|
||||
|
||||
result = inspector.inspect()
|
||||
self.assertRaises(errors.InspectionError,
|
||||
inspector.inspect)
|
||||
|
||||
self.mock_collect.assert_called_with_failure()
|
||||
mock_call.assert_called_with_failure()
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
@mock.patch.object(requests, 'post', autospec=True)
|
||||
|
@ -0,0 +1,13 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds a new CLI command ``ironic-collect-introspection-data`` to enable
|
||||
manually publishing into the ``baremetal-introspection`` service.
|
||||
Executing this command on a system unknown to the Bare Metal service
|
||||
will likely result in the machine becoming registered to Ironic, and
|
||||
as such this command should be used with caution.
|
||||
|
||||
If the capability to update introspection data for running machines
|
||||
has been enabled in the Bare Metal introspection service, then an
|
||||
operator may use this command in the ``active`` or ``rescue`` states
|
||||
to update introspection data.
|
@ -23,6 +23,7 @@ oslo.config.opts =
|
||||
|
||||
console_scripts =
|
||||
ironic-python-agent = ironic_python_agent.cmd.agent:run
|
||||
ironic-collect-introspection-data = ironic_python_agent.cmd.inspect:run
|
||||
|
||||
ironic_python_agent.extensions =
|
||||
standby = ironic_python_agent.extensions.standby:StandbyExtension
|
||||
|
Loading…
Reference in New Issue
Block a user