diff options
Diffstat (limited to 'tests/cloud_tests/instances/lxd.py')
-rw-r--r-- | tests/cloud_tests/instances/lxd.py | 132 |
1 files changed, 76 insertions, 56 deletions
diff --git a/tests/cloud_tests/instances/lxd.py b/tests/cloud_tests/instances/lxd.py index f0aa1214..b9c2cc6b 100644 --- a/tests/cloud_tests/instances/lxd.py +++ b/tests/cloud_tests/instances/lxd.py @@ -1,115 +1,135 @@ # This file is part of cloud-init. See LICENSE file for license information. +"""Base LXD instance.""" + from tests.cloud_tests.instances import base +from tests.cloud_tests import util class LXDInstance(base.Instance): - """ - LXD container backed instance - """ + """LXD container backed instance.""" + platform_name = "lxd" - def __init__(self, name, platform, pylxd_container): - """ - setup + 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.platform = platform self._pylxd_container = pylxd_container - super(LXDInstance, self).__init__(name) + super(LXDInstance, self).__init__( + platform, name, properties, config, features) @property def pylxd_container(self): + """Property function.""" self._pylxd_container.sync() return self._pylxd_container - def execute(self, command, stdin=None, stdout=None, stderr=None, env={}): - """ - command: the command to execute as root inside the image - stdin, stderr, stdout: file handles - env: environment variables + def execute(self, command, stdout=None, stderr=None, env={}, + rcs=None, description=None): + """Execute command in instance, recording output, error and exit code. - Execute assumes functional networking and execution as root with the + Assumes functional networking and execution as root with the target filesystem being available at /. - return_value: tuple containing stdout data, stderr data, exit code + @param command: the command to execute as root inside the image + @param stdout: file handler to write output + @param stderr: file handler to write error + @param env: environment variables + @param rcs: allowed return codes from command + @param description: purpose of command + @return_value: tuple containing stdout data, stderr data, exit code """ - # TODO: the pylxd api handler for container.execute needs to be - # extended to properly pass in stdin - # TODO: the pylxd api handler for container.execute needs to be - # extended to get the return code, for now just use 0 + # ensure instance is running and execute the command self.start() - if stdin: - raise NotImplementedError res = self.pylxd_container.execute(command, environment=env) - for (f, data) in (i for i in zip((stdout, stderr), res) if i[0]): - f.write(data) - return res + (0,) + + # get out, exit and err from pylxd return + if hasattr(res, 'exit_code'): + # pylxd 2.2 returns ContainerExecuteResult, named tuple of + # (exit_code, out, err) + (exit, out, err) = res + else: + # pylxd 2.1.3 and earlier only return out and err, no exit + # LOG.warning('using pylxd version < 2.2') + (out, err) = res + exit = 0 + + # write data to file descriptors if needed + if stdout: + stdout.write(out) + if stderr: + stderr.write(err) + + # if the command exited with a code not allowed in rcs, then fail + if exit not in (rcs if rcs else (0,)): + error_desc = ('Failed command to: {}'.format(description) + if description else None) + raise util.InTargetExecuteError( + out, err, exit, command, self.name, error_desc) + + return (out, err, exit) def read_data(self, remote_path, decode=False): - """ - read data from instance filesystem - remote_path: path in instance - decode: return as string - return_value: data as str or bytes + """Read data from instance filesystem. + + @param remote_path: path in instance + @param decode: return as string + @return_value: data as str or bytes """ data = self.pylxd_container.files.get(remote_path) return data.decode() if decode and isinstance(data, bytes) else data def write_data(self, remote_path, data): - """ - write data to instance filesystem - remote_path: path in instance - data: data to write, either str or bytes + """Write data to instance filesystem. + + @param remote_path: path in instance + @param data: data to write, either str or bytes """ self.pylxd_container.files.put(remote_path, data) def console_log(self): - """ - return_value: bytes of this instance’s console + """Console log. + + @return_value: bytes of this instance’s console """ raise NotImplementedError def reboot(self, wait=True): - """ - reboot instance - """ + """Reboot instance.""" self.shutdown(wait=wait) self.start(wait=wait) def shutdown(self, wait=True): - """ - shutdown instance - """ + """Shutdown instance.""" if self.pylxd_container.status != 'Stopped': self.pylxd_container.stop(wait=wait) - def start(self, wait=True, wait_time=None): - """ - start instance - """ + 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 and isinstance(wait_time, int): - self._wait_for_cloud_init(wait_time) + if wait: + self._wait_for_system(wait_for_cloud_init) def freeze(self): - """ - freeze instance - """ + """Freeze instance.""" if self.pylxd_container.status != 'Frozen': self.pylxd_container.freeze(wait=True) def unfreeze(self): - """ - unfreeze instance - """ + """Unfreeze instance.""" if self.pylxd_container.status == 'Frozen': self.pylxd_container.unfreeze(wait=True) def destroy(self): - """ - clean up instance - """ + """Clean up instance.""" self.unfreeze() self.shutdown() self.pylxd_container.delete(wait=True) |