summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2015-10-02 00:23:06 +0000
committerGerrit Code Review <review@openstack.org>2015-10-02 00:23:06 +0000
commitb751f4debf4c72cb4a2e78bb5a4cb016efafa91e (patch)
treeb67de7eccf1c0c71c1ccd7ee5007fdb2f3b850e1
parente74cd66a4b7a9772d2225e561b68839ded1b7cb5 (diff)
parent9d6b0864e35bcf9d24e5b6b77af92ccf0a619c3c (diff)
downloadironic-python-agent-b751f4debf4c72cb4a2e78bb5a4cb016efafa91e.tar.gz
Merge "Add "logs" and "extra-hardware" inspection collectors"
-rw-r--r--ironic_python_agent/inspector.py68
-rw-r--r--ironic_python_agent/tests/unit/test_inspector.py74
-rw-r--r--plugin-requirements.txt2
-rw-r--r--setup.cfg2
4 files changed, 146 insertions, 0 deletions
diff --git a/ironic_python_agent/inspector.py b/ironic_python_agent/inspector.py
index ac4a8ac9..49ddfc37 100644
--- a/ironic_python_agent/inspector.py
+++ b/ironic_python_agent/inspector.py
@@ -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)
diff --git a/ironic_python_agent/tests/unit/test_inspector.py b/ironic_python_agent/tests/unit/test_inspector.py
index d2fa0891..0e6c18b0 100644
--- a/ironic_python_agent/tests/unit/test_inspector.py
+++ b/ironic_python_agent/tests/unit/test_inspector.py
@@ -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')
diff --git a/plugin-requirements.txt b/plugin-requirements.txt
new file mode 100644
index 00000000..543823f6
--- /dev/null
+++ b/plugin-requirements.txt
@@ -0,0 +1,2 @@
+# Required for 'extra-hardware' inspection collector
+hardware>=0.9
diff --git a/setup.cfg b/setup.cfg
index f6150dfd..c58b26d5 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -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