summaryrefslogtreecommitdiff
path: root/tests/cloud_tests/platforms/instances.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/cloud_tests/platforms/instances.py')
-rw-r--r--tests/cloud_tests/platforms/instances.py145
1 files changed, 145 insertions, 0 deletions
diff --git a/tests/cloud_tests/platforms/instances.py b/tests/cloud_tests/platforms/instances.py
new file mode 100644
index 00000000..3bad021f
--- /dev/null
+++ b/tests/cloud_tests/platforms/instances.py
@@ -0,0 +1,145 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+
+"""Base instance."""
+import time
+
+import paramiko
+from paramiko.ssh_exception import (
+ BadHostKeyException, AuthenticationException, SSHException)
+
+from ..util import TargetBase
+from tests.cloud_tests import LOG, util
+
+
+class Instance(TargetBase):
+ """Base instance object."""
+
+ platform_name = None
+ _ssh_client = None
+
+ def __init__(self, platform, name, properties, config, features):
+ """Set up instance.
+
+ @param platform: platform object
+ @param name: hostname of instance
+ @param properties: image properties
+ @param config: image config
+ @param features: supported feature flags
+ """
+ self.platform = platform
+ self.name = name
+ self.properties = properties
+ self.config = config
+ self.features = features
+ self._tmp_count = 0
+
+ self.ssh_ip = None
+ self.ssh_port = None
+ self.ssh_key_file = None
+ self.ssh_username = 'ubuntu'
+
+ def console_log(self):
+ """Instance console.
+
+ @return_value: bytes of this instance’s console
+ """
+ raise NotImplementedError
+
+ def reboot(self, wait=True):
+ """Reboot instance."""
+ raise NotImplementedError
+
+ def shutdown(self, wait=True):
+ """Shutdown instance."""
+ raise NotImplementedError
+
+ def start(self, wait=True, wait_for_cloud_init=False):
+ """Start instance."""
+ raise NotImplementedError
+
+ def destroy(self):
+ """Clean up instance."""
+ self._ssh_close()
+
+ def _ssh(self, command, stdin=None):
+ """Run a command via SSH."""
+ client = self._ssh_connect()
+
+ cmd = util.shell_pack(command)
+ fp_in, fp_out, fp_err = client.exec_command(cmd)
+ channel = fp_in.channel
+
+ if stdin is not None:
+ fp_in.write(stdin)
+ fp_in.close()
+
+ channel.shutdown_write()
+ rc = channel.recv_exit_status()
+
+ return (fp_out.read(), fp_err.read(), rc)
+
+ def _ssh_close(self):
+ if self._ssh_client:
+ try:
+ self._ssh_client.close()
+ except SSHException:
+ LOG.warning('Failed to close SSH connection.')
+ self._ssh_client = None
+
+ def _ssh_connect(self):
+ """Connect via SSH."""
+ if self._ssh_client:
+ return self._ssh_client
+
+ if not self.ssh_ip or not self.ssh_port:
+ raise ValueError
+
+ client = paramiko.SSHClient()
+ client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+ private_key = paramiko.RSAKey.from_private_key_file(self.ssh_key_file)
+
+ retries = 30
+ while retries:
+ try:
+ client.connect(username=self.ssh_username,
+ hostname=self.ssh_ip, port=self.ssh_port,
+ pkey=private_key, banner_timeout=30)
+ self._ssh_client = client
+ return client
+ except (ConnectionRefusedError, AuthenticationException,
+ BadHostKeyException, ConnectionResetError, SSHException,
+ OSError) as e:
+ retries -= 1
+ time.sleep(10)
+
+ ssh_cmd = 'Failed ssh connection to %s@%s:%s after 300 seconds' % (
+ self.ssh_username, self.ssh_ip, self.ssh_port
+ )
+ raise util.InTargetExecuteError(b'', b'', 1, ssh_cmd, 'ssh')
+
+ def _wait_for_system(self, wait_for_cloud_init):
+ """Wait until system has fully booted and cloud-init has finished.
+
+ @param wait_time: maximum time to wait
+ @return_value: None, may raise OSError if wait_time exceeded
+ """
+ def clean_test(test):
+ """Clean formatting for system ready test testcase."""
+ return ' '.join(l for l in test.strip().splitlines()
+ if not l.lstrip().startswith('#'))
+
+ time = self.config['boot_timeout']
+ tests = [self.config['system_ready_script']]
+ if wait_for_cloud_init:
+ tests.append(self.config['cloud_init_ready_script'])
+
+ formatted_tests = ' && '.join(clean_test(t) for t in tests)
+ cmd = ('i=0; while [ $i -lt {time} ] && i=$(($i+1)); do {test} && '
+ 'exit 0; sleep 1; done; exit 1').format(time=time,
+ test=formatted_tests)
+
+ if self.execute(cmd, rcs=(0, 1))[-1] != 0:
+ raise OSError('timeout: after {}s system not started'.format(time))
+
+
+# vi: ts=4 expandtab