diff --git a/nodepool/driver/openstack/adapter.py b/nodepool/driver/openstack/adapter.py index 29b01220b..d8cb99097 100644 --- a/nodepool/driver/openstack/adapter.py +++ b/nodepool/driver/openstack/adapter.py @@ -1001,3 +1001,11 @@ class OpenStackAdapter(statemachine.Adapter): key = ('nodepool.provider.%s.leaked.floatingips' % self.provider.name) self._statsd.incr(key, did_clean) + + def getConsoleLog(self, label, external_id): + if not label.console_log: + return None + try: + return self._client.get_server_console(external_id) + except openstack.exceptions.OpenStackCloudException: + return None diff --git a/nodepool/driver/statemachine.py b/nodepool/driver/statemachine.py index 368e7624e..b5d02869d 100644 --- a/nodepool/driver/statemachine.py +++ b/nodepool/driver/statemachine.py @@ -340,6 +340,21 @@ class StateMachineNodeLauncher(stats.StatsReporter): except Exception: self.log.exception("Exception while reporting stats:") + if isinstance(e, exceptions.LaunchKeyscanException): + try: + label = self.handler.pool.labels[node.type[0]] + console = self.manager.adapter.getConsoleLog( + label, node.external_id) + if console: + self.log.info('Console log from external id %s:', + node.external_id) + for line in console.splitlines(): + self.log.info(line.rstrip()) + except NotImplementedError: + pass + except Exception: + self.log.exception("Exception while logging console:") + if self.attempts >= self.retries: node.state = zk.FAILED return True @@ -1234,3 +1249,12 @@ class Adapter: :param external_id str: The external id of the image to delete """ raise NotImplementedError() + + # The following method is optional + def getConsoleLog(self, label, external_id): + """Return the console log from the specified server + + :param label ConfigLabel: The label config for the node + :param external_id str: The external id of the server + """ + raise NotImplementedError()