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.
|
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/
|
.. _Ironic Inspector: https://docs.openstack.org/ironic-inspector/
|
||||||
|
|
||||||
Hardware Inventory
|
Hardware Inventory
|
||||||
|
@ -388,8 +388,13 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
|
|||||||
# lookup will fail due to unknown MAC.
|
# lookup will fail due to unknown MAC.
|
||||||
uuid = None
|
uuid = None
|
||||||
if cfg.CONF.inspection_callback_url:
|
if cfg.CONF.inspection_callback_url:
|
||||||
|
try:
|
||||||
|
# Attempt inspection. This may fail, and previously
|
||||||
|
# an error would be logged.
|
||||||
uuid = inspector.inspect()
|
uuid = inspector.inspect()
|
||||||
|
except errors.InspectionError as e:
|
||||||
|
LOG.error('Failed to perform inspection: %(err)s',
|
||||||
|
{'error': e})
|
||||||
if self.api_url:
|
if self.api_url:
|
||||||
self._wait_for_interface()
|
self._wait_for_interface()
|
||||||
content = self.api_client.lookup_node(
|
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. '
|
'Must be provided together with "certfile" option. '
|
||||||
'Default is to not present any client certificates to '
|
'Default is to not present any client certificates to '
|
||||||
'the server.'),
|
'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)
|
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()
|
failures.raise_if_needed()
|
||||||
|
|
||||||
if resp is None:
|
if resp is None:
|
||||||
LOG.info('stopping inspection, as inspector returned an error')
|
raise errors.InspectionError('stopping inspection, as inspector '
|
||||||
return
|
'returned an error')
|
||||||
|
|
||||||
LOG.info('inspection finished successfully')
|
LOG.info('inspection finished successfully')
|
||||||
return resp.get('uuid')
|
return resp.get('uuid')
|
||||||
|
@ -137,11 +137,11 @@ class TestInspect(base.IronicAgentTest):
|
|||||||
mock_call.return_value = None
|
mock_call.return_value = None
|
||||||
mock_ext_mgr.return_value = [self.mock_ext]
|
mock_ext_mgr.return_value = [self.mock_ext]
|
||||||
|
|
||||||
result = inspector.inspect()
|
self.assertRaises(errors.InspectionError,
|
||||||
|
inspector.inspect)
|
||||||
|
|
||||||
self.mock_collect.assert_called_with_failure()
|
self.mock_collect.assert_called_with_failure()
|
||||||
mock_call.assert_called_with_failure()
|
mock_call.assert_called_with_failure()
|
||||||
self.assertIsNone(result)
|
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.object(requests, 'post', autospec=True)
|
@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 =
|
console_scripts =
|
||||||
ironic-python-agent = ironic_python_agent.cmd.agent:run
|
ironic-python-agent = ironic_python_agent.cmd.agent:run
|
||||||
|
ironic-collect-introspection-data = ironic_python_agent.cmd.inspect:run
|
||||||
|
|
||||||
ironic_python_agent.extensions =
|
ironic_python_agent.extensions =
|
||||||
standby = ironic_python_agent.extensions.standby:StandbyExtension
|
standby = ironic_python_agent.extensions.standby:StandbyExtension
|
||||||
|
Loading…
x
Reference in New Issue
Block a user