From 5c66b774517c0f330f23f087b184caa3f9dbd6db Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Thu, 14 May 2020 15:55:27 -0600 Subject: [PATCH] provision.fog: Add optional sentinel file Similar to how provision.cloud.openstack works, wait for a particular file to be created before considering provisioning to be finished. Unlike the cloud.openstack module, teuthology doesn't control the entire process, so to avoid breaking any labs that aren't using a sentinel file, make the feature optional and default to being inoperative. Signed-off-by: Zack Cerza --- teuthology/provision/fog.py | 7 +++++++ teuthology/provision/test/test_fog.py | 22 ++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/teuthology/provision/fog.py b/teuthology/provision/fog.py index e579bad0c6..d4ad3444b2 100644 --- a/teuthology/provision/fog.py +++ b/teuthology/provision/fog.py @@ -6,6 +6,7 @@ import re from datetime import datetime from paramiko import SSHException +from paramiko.ssh_exception import NoValidConnectionsError import teuthology.orchestra @@ -263,10 +264,16 @@ class FOG(object): except ( socket.error, SSHException, + NoValidConnectionsError, MaxWhileTries, EOFError, ): pass + sentinel_file = config.fog.get('sentinel_file', None) + if sentinel_file: + cmd = "while [ ! -e '%s' ]; do sleep 5; done" % sentinel_file + self.remote.run(args=cmd, timeout=600) + self.log.info("Node is ready") def _fix_hostname(self): """ diff --git a/teuthology/provision/test/test_fog.py b/teuthology/provision/test/test_fog.py index f4a791f1dc..9119f74f1e 100644 --- a/teuthology/provision/test/test_fog.py +++ b/teuthology/provision/test/test_fog.py @@ -4,7 +4,7 @@ from mock import patch, DEFAULT, PropertyMock from pytest import raises, mark from teuthology.config import config -from teuthology.exceptions import MaxWhileTries +from teuthology.exceptions import MaxWhileTries, CommandFailedError from teuthology.provision import fog @@ -287,7 +287,7 @@ class TestFOG(object): 'tries', [1, 101], ) - def test_wait_for_ready(self, tries): + def test_wait_for_ready_tries(self, tries): connect_results = [MaxWhileTries for i in range(tries)] + [True] obj = self.klass('name.fqdn', 'type', '1.0') self.mocks['m_Remote_connect'].side_effect = connect_results @@ -297,3 +297,21 @@ class TestFOG(object): return obj._wait_for_ready() assert len(self.mocks['m_Remote_connect'].call_args_list) == tries + 1 + + @mark.parametrize( + 'sentinel_present', + ([False, True]), + ) + def test_wait_for_ready_sentinel(self, sentinel_present): + config.fog['sentinel_file'] = '/a_file' + obj = self.klass('name.fqdn', 'type', '1.0') + if not sentinel_present: + self.mocks['m_Remote_run'].side_effect = [ + CommandFailedError(command='cmd', exitstatus=1)] + with raises(CommandFailedError): + obj._wait_for_ready() + else: + obj._wait_for_ready() + assert len(self.mocks['m_Remote_run'].call_args_list) == 1 + assert "'/a_file'" in \ + self.mocks['m_Remote_run'].call_args_list[0][1]['args'] -- 2.39.5