From 26b53a06efad9f63e3f93dce092c2a9e958f2fea Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Thu, 18 Aug 2016 15:53:02 -0600 Subject: [PATCH] Add console_log task Signed-off-by: Zack Cerza --- teuthology/task/console_log.py | 90 ++++++++++++++++++++++++ teuthology/test/task/test_console_log.py | 78 ++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 teuthology/task/console_log.py create mode 100644 teuthology/test/task/test_console_log.py diff --git a/teuthology/task/console_log.py b/teuthology/task/console_log.py new file mode 100644 index 0000000000..2624aed2e3 --- /dev/null +++ b/teuthology/task/console_log.py @@ -0,0 +1,90 @@ +import logging +import os + +from teuthology.orchestra.cluster import Cluster + +from . import Task + +log = logging.getLogger(__name__) + + +class ConsoleLog(Task): + enabled = True + name = 'console_log' + + def __init__(self, ctx=None, config=None): + super(ConsoleLog, self).__init__(ctx, config) + if self.config.get('enabled') is False: + self.enabled = False + if not getattr(self.ctx, 'archive', None): + self.enabled = False + + def filter_hosts(self): + super(ConsoleLog, self).filter_hosts() + if not hasattr(self.ctx, 'cluster'): + return + new_cluster = Cluster() + for (remote, roles) in self.cluster.remotes.iteritems(): + if not hasattr(remote.console, 'spawn_sol_log'): + log.debug("%s does not support IPMI; excluding", + remote.shortname) + elif not remote.console.has_credentials: + log.debug("IPMI credentials not found for %s; excluding", + remote.shortname) + else: + new_cluster.add(remote, roles) + self.cluster = new_cluster + return self.cluster + + def setup(self): + if not self.enabled: + return + super(ConsoleLog, self).setup() + self.processes = list() + self.setup_archive() + + def setup_archive(self): + self.archive_dir = os.path.join( + self.ctx.archive, + 'console_logs', + ) + os.makedirs(self.archive_dir) + + def begin(self): + if not self.enabled: + return + super(ConsoleLog, self).begin() + self.start_logging() + + def start_logging(self): + for remote in self.cluster.remotes.keys(): + log_path = os.path.join( + self.archive_dir, + "%s.log" % remote.shortname, + ) + proc = remote.console.spawn_sol_log(log_path) + self.processes.append(proc) + + def end(self): + if not self.enabled: + return + super(ConsoleLog, self).end() + self.stop_logging() + + def stop_logging(self, force=False): + for proc in self.processes: + if proc.poll() is not None: + continue + if force: + proc.kill() + else: + proc.terminate() + + def teardown(self): + if not self.enabled: + return + self.stop_logging(force=True) + super(ConsoleLog, self).teardown() + + +task = ConsoleLog diff --git a/teuthology/test/task/test_console_log.py b/teuthology/test/task/test_console_log.py new file mode 100644 index 0000000000..e16eec2691 --- /dev/null +++ b/teuthology/test/task/test_console_log.py @@ -0,0 +1,78 @@ +import os + +from mock import patch + +from teuthology.config import FakeNamespace +from teuthology.config import config as teuth_config +from teuthology.orchestra.cluster import Cluster +from teuthology.orchestra.remote import Remote +from teuthology.task.console_log import ConsoleLog + +from . import TestTask + + +class TestConsoleLog(TestTask): + klass = ConsoleLog + task_name = 'console_log' + + def setup(self): + teuth_config.ipmi_domain = 'ipmi.domain' + teuth_config.ipmi_user = 'ipmi_user' + teuth_config.ipmi_password = 'ipmi_pass' + self.ctx = FakeNamespace() + self.ctx.cluster = Cluster() + self.ctx.cluster.add(Remote('user@remote1'), ['role1']) + self.ctx.cluster.add(Remote('user@remote2'), ['role2']) + self.ctx.config = dict() + self.ctx.archive = '/fake/path' + self.task_config = dict() + self.start_patchers() + + def start_patchers(self): + self.patchers = list() + self.patchers.append(patch( + 'teuthology.task.console_log.os.makedirs', + )) + for patcher in self.patchers: + patcher.start() + + def teardown(self): + for patcher in self.patchers: + patcher.stop() + + def test_enabled(self): + task = self.klass(self.ctx, self.task_config) + assert task.enabled is True + + def test_disabled_noarchive(self): + self.ctx.archive = None + task = self.klass(self.ctx, self.task_config) + assert task.enabled is False + + def test_has_credentials(self): + for remote in self.ctx.cluster.remotes.keys(): + remote.console.has_credentials = False + task = self.klass(self.ctx, self.task_config) + assert len(task.cluster.remotes.keys()) == 0 + + def test_remotes(self): + with self.klass(self.ctx, self.task_config) as task: + assert len(task.cluster.remotes) == len(self.ctx.cluster.remotes) + + @patch('teuthology.orchestra.console.PhysicalConsole') + def test_begin(self, m_pconsole): + with self.klass(self.ctx, self.task_config) as task: + assert len(task.processes) == len(self.ctx.cluster.remotes) + for remote in task.cluster.remotes.keys(): + dest_path = os.path.join( + self.ctx.archive, '%s.log' % remote.shortname) + assert remote.console.spawn_sol_log.called_once_with( + dest_path=dest_path) + + @patch('teuthology.orchestra.console.PhysicalConsole') + def test_end(self, m_pconsole): + with self.klass(self.ctx, self.task_config) as task: + pass + for proc in task.processes: + assert proc.terminate.called_once_with() + assert proc.kill.called_once_with() -- 2.39.5