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
|
||||
# limitations under the License.
|
||||
|
||||
import base64
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import tarfile
|
||||
|
||||
import netaddr
|
||||
from oslo_concurrency import processutils
|
||||
@ -251,3 +255,67 @@ def collect_default(data, failures):
|
||||
# dropped after inspector is ready (probably in Mitaka cycle).
|
||||
discover_network_properties(inventory, data, failures)
|
||||
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
|
||||
# limitations under the License.
|
||||
|
||||
import base64
|
||||
import collections
|
||||
import copy
|
||||
import io
|
||||
import tarfile
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
@ -363,3 +366,74 @@ class TestCollectDefault(BaseDiscoverTest):
|
||||
self.failures)
|
||||
mock_discover_sched.assert_called_once_with(
|
||||
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 =
|
||||
default = ironic_python_agent.inspector:collect_default
|
||||
logs = ironic_python_agent.inspector:collect_logs
|
||||
extra-hardware = ironic_python_agent.inspector:collect_extra_hardware
|
||||
|
||||
[pbr]
|
||||
autodoc_index_modules = True
|
||||
|
Loading…
x
Reference in New Issue
Block a user