summaryrefslogtreecommitdiff
path: root/tests/cloud_tests/instances
diff options
context:
space:
mode:
Diffstat (limited to 'tests/cloud_tests/instances')
-rw-r--r--tests/cloud_tests/instances/__init__.py10
-rw-r--r--tests/cloud_tests/instances/base.py77
-rw-r--r--tests/cloud_tests/instances/lxd.py157
-rw-r--r--tests/cloud_tests/instances/nocloudkvm.py179
4 files changed, 0 insertions, 423 deletions
diff --git a/tests/cloud_tests/instances/__init__.py b/tests/cloud_tests/instances/__init__.py
deleted file mode 100644
index fc2e9cbc..00000000
--- a/tests/cloud_tests/instances/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# This file is part of cloud-init. See LICENSE file for license information.
-
-"""Main init."""
-
-
-def get_instance(snapshot, *args, **kwargs):
- """Get instance from snapshot."""
- return snapshot.launch(*args, **kwargs)
-
-# vi: ts=4 expandtab
diff --git a/tests/cloud_tests/instances/base.py b/tests/cloud_tests/instances/base.py
deleted file mode 100644
index 8c59d62c..00000000
--- a/tests/cloud_tests/instances/base.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# This file is part of cloud-init. See LICENSE file for license information.
-
-"""Base instance."""
-
-from ..util import TargetBase
-
-
-class Instance(TargetBase):
- """Base instance object."""
-
- platform_name = 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
-
- 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."""
- pass
-
- 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
diff --git a/tests/cloud_tests/instances/lxd.py b/tests/cloud_tests/instances/lxd.py
deleted file mode 100644
index 3b035d86..00000000
--- a/tests/cloud_tests/instances/lxd.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# This file is part of cloud-init. See LICENSE file for license information.
-
-"""Base LXD instance."""
-
-from . import base
-
-import os
-import shutil
-from tempfile import mkdtemp
-
-
-class LXDInstance(base.Instance):
- """LXD container backed instance."""
-
- platform_name = "lxd"
-
- def __init__(self, platform, name, properties, config, features,
- pylxd_container):
- """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._pylxd_container = pylxd_container
- super(LXDInstance, self).__init__(
- platform, name, properties, config, features)
- self.tmpd = mkdtemp(prefix="%s-%s" % (type(self).__name__, name))
- self._setup_console_log()
-
- @property
- def pylxd_container(self):
- """Property function."""
- self._pylxd_container.sync()
- return self._pylxd_container
-
- def _setup_console_log(self):
- logf = os.path.join(self.tmpd, "console.log")
-
- # doing this ensures we can read it. Otherwise it ends up root:root.
- with open(logf, "w") as fp:
- fp.write("# %s\n" % self.name)
-
- cfg = "lxc.console.logfile=%s" % logf
- orig = self._pylxd_container.config.get('raw.lxc', "")
- if orig:
- orig += "\n"
- self._pylxd_container.config['raw.lxc'] = orig + cfg
- self._pylxd_container.save()
- self._console_log_file = logf
-
- def _execute(self, command, stdin=None, env=None):
- if env is None:
- env = {}
-
- if stdin is not None:
- # pylxd does not support input to execute.
- # https://github.com/lxc/pylxd/issues/244
- #
- # The solution here is write a tmp file in the container
- # and then execute a shell that sets it standard in to
- # be from that file, removes it, and calls the comand.
- tmpf = self.tmpfile()
- self.write_data(tmpf, stdin)
- ncmd = 'exec <"{tmpf}"; rm -f "{tmpf}"; exec "$@"'
- command = (['sh', '-c', ncmd.format(tmpf=tmpf), 'stdinhack'] +
- list(command))
-
- # ensure instance is running and execute the command
- self.start()
- # execute returns a ContainerExecuteResult, named tuple
- # (exit_code, stdout, stderr)
- res = self.pylxd_container.execute(command, environment=env)
-
- # get out, exit and err from pylxd return
- if not hasattr(res, 'exit_code'):
- # pylxd 2.1.3 and earlier only return out and err, no exit
- raise RuntimeError(
- "No 'exit_code' in pylxd.container.execute return.\n"
- "pylxd > 2.2 is required.")
-
- return res.stdout, res.stderr, res.exit_code
-
- def read_data(self, remote_path, decode=False):
- """Read data from instance filesystem.
-
- @param remote_path: path in instance
- @param decode: decode data before returning.
- @return_value: content of remote_path as bytes if 'decode' is False,
- and as string if 'decode' is True.
- """
- data = self.pylxd_container.files.get(remote_path)
- return data.decode() if decode else data
-
- def write_data(self, remote_path, data):
- """Write data to instance filesystem.
-
- @param remote_path: path in instance
- @param data: data to write in bytes
- """
- self.pylxd_container.files.put(remote_path, data)
-
- def console_log(self):
- """Console log.
-
- @return_value: bytes of this instance’s console
- """
- if not os.path.exists(self._console_log_file):
- raise NotImplementedError(
- "Console log '%s' does not exist. If this is a remote "
- "lxc, then this is really NotImplementedError. If it is "
- "A local lxc, then this is a RuntimeError."
- "https://github.com/lxc/lxd/issues/1129")
- with open(self._console_log_file, "rb") as fp:
- return fp.read()
-
- def reboot(self, wait=True):
- """Reboot instance."""
- self.shutdown(wait=wait)
- self.start(wait=wait)
-
- def shutdown(self, wait=True):
- """Shutdown instance."""
- if self.pylxd_container.status != 'Stopped':
- self.pylxd_container.stop(wait=wait)
-
- def start(self, wait=True, wait_for_cloud_init=False):
- """Start instance."""
- if self.pylxd_container.status != 'Running':
- self.pylxd_container.start(wait=wait)
- if wait:
- self._wait_for_system(wait_for_cloud_init)
-
- def freeze(self):
- """Freeze instance."""
- if self.pylxd_container.status != 'Frozen':
- self.pylxd_container.freeze(wait=True)
-
- def unfreeze(self):
- """Unfreeze instance."""
- if self.pylxd_container.status == 'Frozen':
- self.pylxd_container.unfreeze(wait=True)
-
- def destroy(self):
- """Clean up instance."""
- self.unfreeze()
- self.shutdown()
- self.pylxd_container.delete(wait=True)
- if self.platform.container_exists(self.name):
- raise OSError('container {} was not properly removed'
- .format(self.name))
- shutil.rmtree(self.tmpd)
- super(LXDInstance, self).destroy()
-
-# vi: ts=4 expandtab
diff --git a/tests/cloud_tests/instances/nocloudkvm.py b/tests/cloud_tests/instances/nocloudkvm.py
deleted file mode 100644
index bc06a79e..00000000
--- a/tests/cloud_tests/instances/nocloudkvm.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# This file is part of cloud-init. See LICENSE file for license information.
-
-"""Base NoCloud KVM instance."""
-
-import os
-import paramiko
-import socket
-import subprocess
-import time
-
-from cloudinit import util as c_util
-from tests.cloud_tests.instances import base
-from tests.cloud_tests import util
-
-# This domain contains reverse lookups for hostnames that are used.
-# The primary reason is so sudo will return quickly when it attempts
-# to look up the hostname. i9n is just short for 'integration'.
-# see also bug 1730744 for why we had to do this.
-CI_DOMAIN = "i9n.cloud-init.io"
-
-
-class NoCloudKVMInstance(base.Instance):
- """NoCloud KVM backed instance."""
-
- platform_name = "nocloud-kvm"
- _ssh_client = None
-
- def __init__(self, platform, name, image_path, properties, config,
- features, user_data, meta_data):
- """Set up instance.
-
- @param platform: platform object
- @param name: image path
- @param image_path: path to disk image to boot.
- @param properties: dictionary of properties
- @param config: dictionary of configuration values
- @param features: dictionary of supported feature flags
- """
- self.user_data = user_data
- self.meta_data = meta_data
- self.ssh_key_file = os.path.join(platform.config['data_dir'],
- platform.config['private_key'])
- self.ssh_port = None
- self.pid = None
- self.pid_file = None
- self.console_file = None
- self.disk = image_path
-
- super(NoCloudKVMInstance, self).__init__(
- platform, name, properties, config, features)
-
- def destroy(self):
- """Clean up instance."""
- if self.pid:
- try:
- c_util.subp(['kill', '-9', self.pid])
- except util.ProcessExectuionError:
- pass
-
- if self.pid_file:
- os.remove(self.pid_file)
-
- self.pid = None
- if self._ssh_client:
- self._ssh_client.close()
- self._ssh_client = None
-
- super(NoCloudKVMInstance, self).destroy()
-
- def _execute(self, command, stdin=None, env=None):
- env_args = []
- if env:
- env_args = ['env'] + ["%s=%s" for k, v in env.items()]
-
- return self.ssh(['sudo'] + env_args + list(command), stdin=stdin)
-
- def generate_seed(self, tmpdir):
- """Generate nocloud seed from user-data"""
- seed_file = os.path.join(tmpdir, '%s_seed.img' % self.name)
- user_data_file = os.path.join(tmpdir, '%s_user_data' % self.name)
-
- with open(user_data_file, "w") as ud_file:
- ud_file.write(self.user_data)
-
- c_util.subp(['cloud-localds', seed_file, user_data_file])
-
- return seed_file
-
- def get_free_port(self):
- """Get a free port assigned by the kernel."""
- s = socket.socket()
- s.bind(('', 0))
- num = s.getsockname()[1]
- s.close()
- return num
-
- def ssh(self, command, stdin=None):
- """Run a command via SSH."""
- client = self._ssh_connect()
-
- cmd = util.shell_pack(command)
- try:
- 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)
- except paramiko.SSHException as e:
- raise util.InTargetExecuteError(
- b'', b'', -1, command, self.name, reason=e)
-
- def _ssh_connect(self, hostname='localhost', username='ubuntu',
- banner_timeout=120, retry_attempts=30):
- """Connect via SSH."""
- if self._ssh_client:
- return self._ssh_client
-
- private_key = paramiko.RSAKey.from_private_key_file(self.ssh_key_file)
- client = paramiko.SSHClient()
- client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
- while retry_attempts:
- try:
- client.connect(hostname=hostname, username=username,
- port=self.ssh_port, pkey=private_key,
- banner_timeout=banner_timeout)
- self._ssh_client = client
- return client
- except (paramiko.SSHException, TypeError):
- time.sleep(1)
- retry_attempts = retry_attempts - 1
-
- error_desc = 'Failed command to: %s@%s:%s' % (username, hostname,
- self.ssh_port)
- raise util.InTargetExecuteError('', '', -1, 'ssh connect',
- self.name, error_desc)
-
- def start(self, wait=True, wait_for_cloud_init=False):
- """Start instance."""
- tmpdir = self.platform.config['data_dir']
- seed = self.generate_seed(tmpdir)
- self.pid_file = os.path.join(tmpdir, '%s.pid' % self.name)
- self.console_file = os.path.join(tmpdir, '%s-console.log' % self.name)
- self.ssh_port = self.get_free_port()
-
- cmd = ['./tools/xkvm',
- '--disk', '%s,cache=unsafe' % self.disk,
- '--disk', '%s,cache=unsafe' % seed,
- '--netdev', ','.join(['user',
- 'hostfwd=tcp::%s-:22' % self.ssh_port,
- 'dnssearch=%s' % CI_DOMAIN]),
- '--', '-pidfile', self.pid_file, '-vnc', 'none',
- '-m', '2G', '-smp', '2', '-nographic',
- '-serial', 'file:' + self.console_file]
- subprocess.Popen(cmd,
- close_fds=True,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
-
- while not os.path.exists(self.pid_file):
- time.sleep(1)
-
- with open(self.pid_file, 'r') as pid_f:
- self.pid = pid_f.readlines()[0].strip()
-
- if wait:
- self._wait_for_system(wait_for_cloud_init)
-
- def console_log(self):
- if not self.console_file:
- return b''
- with open(self.console_file, "rb") as fp:
- return fp.read()
-
-# vi: ts=4 expandtab