Add "logs" and "extra-hardware" inspection collectors
This is a port of downstream inspector ramdisk plugins we found helpful. * logs - sends journald logs with inspection data. * extra-hardware - uses hardware-detect utility to collect bigger hardware inventory and to run benchmarks. Change-Id: If05402606c45185d618279eef46e68c51209f82b
This commit is contained in:
parent
378197caee
commit
9d6b0864e3
@ -13,7 +13,11 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import io
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import tarfile
|
||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
from oslo_concurrency import processutils
|
from oslo_concurrency import processutils
|
||||||
@ -251,3 +255,67 @@ def collect_default(data, failures):
|
|||||||
# dropped after inspector is ready (probably in Mitaka cycle).
|
# dropped after inspector is ready (probably in Mitaka cycle).
|
||||||
discover_network_properties(inventory, data, failures)
|
discover_network_properties(inventory, data, failures)
|
||||||
discover_scheduling_properties(inventory, data, root_disk)
|
discover_scheduling_properties(inventory, data, root_disk)
|
||||||
|
|
||||||
|
|
||||||
|
def collect_logs(data, failures):
|
||||||
|
"""Collect journald logs from the ramdisk.
|
||||||
|
|
||||||
|
As inspection runs before any nodes details are known, it's handy to have
|
||||||
|
logs returned with data. This collector sends logs to inspector in format
|
||||||
|
expected by the 'ramdisk_error' plugin: base64 encoded tar.gz.
|
||||||
|
|
||||||
|
This collector should be installed last in the collector chain, otherwise
|
||||||
|
it won't collect enough logs.
|
||||||
|
|
||||||
|
This collector does not report failures.
|
||||||
|
|
||||||
|
:param data: mutable data that we'll send to inspector
|
||||||
|
:param failures: AccumulatedFailures object
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
out, _e = utils.execute('journalctl', '--full', '--no-pager', '-b',
|
||||||
|
'-n', '10000')
|
||||||
|
except (processutils.ProcessExecutionError, OSError):
|
||||||
|
LOG.warn('failed to get system journal')
|
||||||
|
return
|
||||||
|
|
||||||
|
journal = io.BytesIO(out.encode('utf-8'))
|
||||||
|
with io.BytesIO() as fp:
|
||||||
|
with tarfile.open(fileobj=fp, mode='w:gz') as tar:
|
||||||
|
tarinfo = tarfile.TarInfo('journal')
|
||||||
|
tarinfo.size = len(out)
|
||||||
|
tar.addfile(tarinfo, journal)
|
||||||
|
|
||||||
|
fp.seek(0)
|
||||||
|
data['logs'] = base64.b64encode(fp.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
def collect_extra_hardware(data, failures):
|
||||||
|
"""Collect detailed inventory using 'hardware-detect' utility.
|
||||||
|
|
||||||
|
Recognizes ipa-inspection-benchmarks with list of benchmarks (possible
|
||||||
|
values are cpu, disk, mem) to run. No benchmarks are run by default, as
|
||||||
|
they're pretty time-consuming.
|
||||||
|
|
||||||
|
Puts collected data as JSON under 'data' key.
|
||||||
|
Requires 'hardware' python package to be installed on the ramdisk in
|
||||||
|
addition to the packages in requirements.txt.
|
||||||
|
|
||||||
|
:param data: mutable data that we'll send to inspector
|
||||||
|
:param failures: AccumulatedFailures object
|
||||||
|
"""
|
||||||
|
benchmarks = utils.get_agent_params().get('ipa-inspection-benchmarks', [])
|
||||||
|
if benchmarks:
|
||||||
|
benchmarks = ['--benchmark'] + benchmarks.split(',')
|
||||||
|
|
||||||
|
try:
|
||||||
|
out, err = utils.execute('hardware-detect', *benchmarks)
|
||||||
|
except (processutils.ProcessExecutionError, OSError) as exc:
|
||||||
|
failures.add('failed to run hardware-detect utility: %s', exc)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
data['data'] = json.loads(out)
|
||||||
|
except ValueError as exc:
|
||||||
|
msg = 'JSON returned from hardware-detect cannot be decoded: %s'
|
||||||
|
failures.add(msg, exc)
|
||||||
|
@ -13,8 +13,11 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import base64
|
||||||
import collections
|
import collections
|
||||||
import copy
|
import copy
|
||||||
|
import io
|
||||||
|
import tarfile
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
@ -363,3 +366,74 @@ class TestCollectDefault(BaseDiscoverTest):
|
|||||||
self.failures)
|
self.failures)
|
||||||
mock_discover_sched.assert_called_once_with(
|
mock_discover_sched.assert_called_once_with(
|
||||||
self.inventory, self.data, root_disk=None)
|
self.inventory, self.data, root_disk=None)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(utils, 'execute', autospec=True)
|
||||||
|
class TestCollectLogs(unittest.TestCase):
|
||||||
|
def test(self, mock_execute):
|
||||||
|
contents = 'journal contents'
|
||||||
|
mock_execute.return_value = (contents, '')
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
inspector.collect_logs(data, None)
|
||||||
|
res = io.BytesIO(base64.b64decode(data['logs']))
|
||||||
|
|
||||||
|
with tarfile.open(fileobj=res) as tar:
|
||||||
|
members = [(m.name, m.size) for m in tar]
|
||||||
|
self.assertEqual([('journal', len(contents))], members)
|
||||||
|
|
||||||
|
member = tar.extractfile('journal')
|
||||||
|
self.assertEqual(contents, member.read().decode('utf-8'))
|
||||||
|
|
||||||
|
def test_no_journal(self, mock_execute):
|
||||||
|
mock_execute.side_effect = OSError()
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
inspector.collect_logs(data, None)
|
||||||
|
self.assertFalse(data)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(utils, 'execute', autospec=True)
|
||||||
|
class TestCollectExtraHardware(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestCollectExtraHardware, self).setUp()
|
||||||
|
self.data = {}
|
||||||
|
self.failures = utils.AccumulatedFailures()
|
||||||
|
|
||||||
|
def test_no_benchmarks(self, mock_execute):
|
||||||
|
mock_execute.return_value = ("[1, 2, 3]", "")
|
||||||
|
|
||||||
|
inspector.collect_extra_hardware(self.data, None)
|
||||||
|
|
||||||
|
self.assertEqual({'data': [1, 2, 3]}, self.data)
|
||||||
|
mock_execute.assert_called_once_with('hardware-detect')
|
||||||
|
|
||||||
|
@mock.patch.object(utils, 'get_agent_params', autospec=True)
|
||||||
|
def test_benchmarks(self, mock_params, mock_execute):
|
||||||
|
mock_params.return_value = {'ipa-inspection-benchmarks': 'cpu,mem'}
|
||||||
|
mock_execute.return_value = ("[1, 2, 3]", "")
|
||||||
|
|
||||||
|
inspector.collect_extra_hardware(self.data, None)
|
||||||
|
|
||||||
|
self.assertEqual({'data': [1, 2, 3]}, self.data)
|
||||||
|
mock_execute.assert_called_once_with('hardware-detect',
|
||||||
|
'--benchmark',
|
||||||
|
'cpu', 'mem')
|
||||||
|
|
||||||
|
def test_execute_failed(self, mock_execute):
|
||||||
|
mock_execute.side_effect = processutils.ProcessExecutionError()
|
||||||
|
|
||||||
|
inspector.collect_extra_hardware(self.data, self.failures)
|
||||||
|
|
||||||
|
self.assertNotIn('data', self.data)
|
||||||
|
self.assertTrue(self.failures)
|
||||||
|
mock_execute.assert_called_once_with('hardware-detect')
|
||||||
|
|
||||||
|
def test_parsing_failed(self, mock_execute):
|
||||||
|
mock_execute.return_value = ("foobar", "")
|
||||||
|
|
||||||
|
inspector.collect_extra_hardware(self.data, self.failures)
|
||||||
|
|
||||||
|
self.assertNotIn('data', self.data)
|
||||||
|
self.assertTrue(self.failures)
|
||||||
|
mock_execute.assert_called_once_with('hardware-detect')
|
||||||
|
2
plugin-requirements.txt
Normal file
2
plugin-requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Required for 'extra-hardware' inspection collector
|
||||||
|
hardware>=0.9
|
@ -32,6 +32,8 @@ ironic_python_agent.hardware_managers =
|
|||||||
|
|
||||||
ironic_python_agent.inspector.collectors =
|
ironic_python_agent.inspector.collectors =
|
||||||
default = ironic_python_agent.inspector:collect_default
|
default = ironic_python_agent.inspector:collect_default
|
||||||
|
logs = ironic_python_agent.inspector:collect_logs
|
||||||
|
extra-hardware = ironic_python_agent.inspector:collect_extra_hardware
|
||||||
|
|
||||||
[pbr]
|
[pbr]
|
||||||
autodoc_index_modules = True
|
autodoc_index_modules = True
|
||||||
|
Loading…
x
Reference in New Issue
Block a user