diff options
| author | Joshua Powers <josh.powers@canonical.com> | 2017-12-07 20:54:46 (GMT) |
|---|---|---|
| committer | Joshua Powers <josh.powers@canonical.com> | 2017-12-11 22:15:24 (GMT) |
| commit | 2d5c6d156cb4506260cb1abef54daefb7a0ffe05 (patch) | |
| tree | 2e19d17de49099e305a39068e2ecca2f1e9b19b0 | |
| parent | 1d1c31292a0d10e3dd8940739b577fad9d18f5c5 (diff) | |
tests: Enable AWS EC2 Integration Testing
Utilizing the boto3 library, this enables integration tests to utilize
the 'ec2' platform.
TODO:
* Configure image with custom version of cloud-init
* Launch with dual-stack networking
* Launch with root instance-store instead of ebs
* Launch with specific instance type, t2.micro by default
| -rw-r--r-- | test-requirements.txt | 1 | ||||
| -rw-r--r-- | tests/cloud_tests/collect.py | 4 | ||||
| -rw-r--r-- | tests/cloud_tests/platforms.yaml | 9 | ||||
| -rw-r--r-- | tests/cloud_tests/platforms/__init__.py | 2 | ||||
| -rw-r--r-- | tests/cloud_tests/platforms/ec2/image.py | 53 | ||||
| -rw-r--r-- | tests/cloud_tests/platforms/ec2/instance.py | 102 | ||||
| -rw-r--r-- | tests/cloud_tests/platforms/ec2/platform.py | 218 | ||||
| -rw-r--r-- | tests/cloud_tests/platforms/ec2/snapshot.py | 52 | ||||
| -rw-r--r-- | tests/cloud_tests/platforms/instances.py | 61 | ||||
| -rw-r--r-- | tests/cloud_tests/platforms/nocloudkvm/image.py | 4 | ||||
| -rw-r--r-- | tests/cloud_tests/platforms/nocloudkvm/instance.py | 55 | ||||
| -rw-r--r-- | tests/cloud_tests/platforms/nocloudkvm/platform.py | 5 | ||||
| -rw-r--r-- | tests/cloud_tests/platforms/nocloudkvm/snapshot.py | 7 | ||||
| -rw-r--r-- | tests/cloud_tests/platforms/platforms.py | 15 | ||||
| -rw-r--r-- | tests/cloud_tests/releases.yaml | 3 | ||||
| -rw-r--r-- | tests/cloud_tests/setup_image.py | 18 |
16 files changed, 531 insertions, 78 deletions
diff --git a/test-requirements.txt b/test-requirements.txt index d9d41b5..d7a9de5 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,6 +4,7 @@ mock nose unittest2 coverage +paramiko # Only needed if you want to know the test times # nose-timer diff --git a/tests/cloud_tests/collect.py b/tests/cloud_tests/collect.py index 4805cea..bb72245 100644 --- a/tests/cloud_tests/collect.py +++ b/tests/cloud_tests/collect.py @@ -31,8 +31,8 @@ def collect_console(instance, base_dir): LOG.debug('getting console log') try: data = instance.console_log() - except NotImplementedError as e: - data = 'Not Implemented: %s' % e + except NotImplementedError: + data = b'instance.console_log: not implemented' with open(os.path.join(base_dir, 'console.log'), "wb") as fp: fp.write(data) diff --git a/tests/cloud_tests/platforms.yaml b/tests/cloud_tests/platforms.yaml index fa4f845..46c93bb 100644 --- a/tests/cloud_tests/platforms.yaml +++ b/tests/cloud_tests/platforms.yaml @@ -6,8 +6,13 @@ default_platform_config: get_image_timeout: 300 # maximum time to create instance (before waiting for cloud-init) create_instance_timeout: 60 - platforms: + ec2: + enabled: true + instance-type: t2.micro + private_key: id_rsa + public_key: id_rsa.pub + vpc-tag: cloud-init-testing lxd: enabled: true # overrides for image templates @@ -63,7 +68,5 @@ platforms: enabled: true private_key: id_rsa public_key: id_rsa.pub - ec2: {} - azure: {} # vi: ts=4 expandtab diff --git a/tests/cloud_tests/platforms/__init__.py b/tests/cloud_tests/platforms/__init__.py index 92ed162..a01e51a 100644 --- a/tests/cloud_tests/platforms/__init__.py +++ b/tests/cloud_tests/platforms/__init__.py @@ -2,10 +2,12 @@ """Main init.""" +from .ec2 import platform as ec2 from .lxd import platform as lxd from .nocloudkvm import platform as nocloudkvm PLATFORMS = { + 'ec2': ec2.EC2Platform, 'nocloud-kvm': nocloudkvm.NoCloudKVMPlatform, 'lxd': lxd.LXDPlatform, } diff --git a/tests/cloud_tests/platforms/ec2/image.py b/tests/cloud_tests/platforms/ec2/image.py new file mode 100644 index 0000000..8608210 --- /dev/null +++ b/tests/cloud_tests/platforms/ec2/image.py @@ -0,0 +1,53 @@ +# This file is part of cloud-init. See LICENSE file for license information. + +"""EC2 Image Base Class.""" + +from ..images import Image +from .snapshot import EC2Snapshot + + +class EC2Image(Image): + """EC2 backed image.""" + + platform_name = 'ec2' + + def __init__(self, platform, config, image_ami): + """Set up image. + + @param platform: platform object + @param config: image configuration + @param image_ami: string of image ami ID + """ + super(EC2Image, self).__init__(platform, config) + + self.image_ami = image_ami + + @property + def properties(self): + """Dictionary containing: 'arch', 'os', 'version', 'release'.""" + return { + 'arch': self.config['arch'], + 'os': self.config['family'], + 'release': self.config['release'], + 'version': self.config['version'], + } + + def _execute(self, command, stdin=None, env=None): + """Execute command in image, modifying image.""" + raise NotImplementedError + + def snapshot(self): + """Create snapshot of image, block until done.""" + return EC2Snapshot(self.platform, self.properties, self.config, + self.features, self.image_ami) + + def destroy(self): + """Unset path to signal image is no longer used. + + The removal of the images and all other items is handled by the + framework. In some cases we want to keep the images, so let the + framework decide whether to keep or destroy everything. + """ + super(EC2Image, self).destroy() + +# vi: ts=4 expandtab diff --git a/tests/cloud_tests/platforms/ec2/instance.py b/tests/cloud_tests/platforms/ec2/instance.py new file mode 100644 index 0000000..449684f --- /dev/null +++ b/tests/cloud_tests/platforms/ec2/instance.py @@ -0,0 +1,102 @@ +# This file is part of cloud-init. See LICENSE file for license information. + +"""Base EC2 instance.""" +import os +import time + +import botocore + +from ..instances import Instance +from tests.cloud_tests import util + + +class EC2Instance(Instance): + """EC2 backed instance.""" + + platform_name = "ec2" + _ssh_client = None + + def __init__(self, platform, properties, config, features, + image_ami, user_data): + """Set up instance. + + @param platform: platform object + @param properties: dictionary of properties + @param config: dictionary of configuration values + @param features: dictionary of supported feature flags + @param image_ami: AWS AMI ID for image to use + @param user_data: Test user-data to pass to instance + @param use_desc: + """ + super(EC2Instance, self).__init__( + platform, image_ami, properties, config, features) + + self.image_ami = image_ami + self.instance_id = None + self.user_data = user_data + self.ssh_ip = None + self.ssh_port = 22 + self.ssh_key_file = os.path.join( + platform.config['data_dir'], platform.config['private_key']) + self.ssh_pubkey_file = os.path.join( + platform.config['data_dir'], platform.config['public_key']) + + def destroy(self): + """Clean up instance.""" + if self._ssh_client: + self._ssh_client.close() + self._ssh_client = None + + if self.instance: + self.instance.terminate() + + super(EC2Instance, self).destroy() + + def _execute(self, command, stdin=None, env=None): + """Execute command on instance.""" + 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 start(self, wait=True, wait_for_cloud_init=False): + """Start instance on EC2 with the platfrom's VPC.""" + name_tag = { + 'ResourceType': 'instance', + 'Tags': [ + { + 'Key': 'Name', + 'Value': self.platform.ec2_tag, + }, + ] + } + + try: + instance = self.platform.ec2_resource.create_instances( + ImageId=self.image_ami, + InstanceType=self.platform.instance_type, + KeyName=self.platform.key_name, + MaxCount=1, + MinCount=1, + SecurityGroupIds=[self.platform.vpc_security_group.id], + SubnetId=self.platform.vpc_subnet.id, + TagSpecifications=[name_tag], + UserData=self.user_data, + ) + except botocore.exceptions.ClientError as error: + error_msg = error.response['Error']['Message'] + raise util.InTargetExecuteError(b'', b'', 1, '', 'start', + reason=error_msg) + + self.instance = instance[0] + while self.instance.state['Name'] != 'running': + time.sleep(5) + self.instance.reload() + + self.ssh_ip = self.instance.public_ip_address + + if wait: + self._wait_for_system(wait_for_cloud_init) + +# vi: ts=4 expandtab diff --git a/tests/cloud_tests/platforms/ec2/platform.py b/tests/cloud_tests/platforms/ec2/platform.py new file mode 100644 index 0000000..ad74583 --- /dev/null +++ b/tests/cloud_tests/platforms/ec2/platform.py @@ -0,0 +1,218 @@ +# This file is part of cloud-init. See LICENSE file for license information. + +"""Base EC2 platform.""" +import os +import uuid + +import boto3 + +from ..platforms import Platform +from .image import EC2Image +from .instance import EC2Instance + + +class EC2Platform(Platform): + """EC2 test platform.""" + + platform_name = 'ec2' + ipv4_cidr = '192.168.1.0/20' + + def __init__(self, config): + """Set up platform.""" + super(EC2Platform, self).__init__(config) + + self._generate_ssh_keys(config['data_dir']) + self.ec2_client = boto3.client('ec2') + self.ec2_resource = boto3.resource('ec2') + self.ec2_tag = config['vpc-tag'] + self.instance_type = config['instance-type'] + self.key_name = self._upload_public_key(config) + self.vpc = self._setup_vpc() + self.vpc_security_group = self._get_security_group() + self.vpc_subnet = self._get_subnet() + + def destroy(self): + """Delete platform.""" + if self.key_name: + self.ec2_client.delete_key_pair(KeyName=self.key_name) + + def _upload_public_key(self, config): + """Generate random name and upload SSH key with that name. + + @param config: platform config + """ + key_file = os.path.join(config['data_dir'], config['public_key']) + with open(key_file, 'r') as file: + public_key = file.read().strip('\n') + + name = '%s-%s' % (self.ec2_tag, str(uuid.uuid1())[0:8]) + self.ec2_client.import_key_pair(KeyName=name, + PublicKeyMaterial=public_key) + + return name + + def _tag_resource(self, resource): + """Tag a resouce with the specified tag. + + This makes finding and deleting resources specific to this testing + much easier to find. + + @param resource: resource to tag""" + tag = { + 'Key': 'Name', + 'Value': self.ec2_tag + } + resource.create_tags(Tags=[tag]) + + def _setup_vpc(self): + """Setup AWS EC2 VPC or return existing VPC.""" + for vpc in self.ec2_resource.vpcs.all(): + if not vpc.tags: + continue + for tag in vpc.tags: + if tag['Value'] == self.ec2_tag: + return vpc + + vpc = self.ec2_resource.create_vpc( + CidrBlock=self.ipv4_cidr, + AmazonProvidedIpv6CidrBlock=True + ) + vpc.wait_until_available() + self._tag_resource(vpc) + + internet_gateway = self._create_internet_gateway(vpc) + self._create_subnet(vpc) + self._update_routing_table(vpc, internet_gateway.id) + self._update_security_group(vpc) + + return vpc + + def _create_internet_gateway(self, vpc): + """Create Internet Gateway and assign to VPC. + + @param vpc: VPC to create internet gateway on + """ + internet_gateway = self.ec2_resource.create_internet_gateway() + internet_gateway.attach_to_vpc(VpcId=vpc.vpc_id) + self._tag_resource(internet_gateway) + return internet_gateway + + def _create_subnet(self, vpc): + """Generate IPv4 and IPv6 subnets for use. + + @param vpc: VPC to create subnets on + """ + ipv6_block = vpc.ipv6_cidr_block_association_set[0]['Ipv6CidrBlock'] + ipv6_cidr = ipv6_block[:-2] + '64' + + subnet = vpc.create_subnet(CidrBlock=self.ipv4_cidr, + Ipv6CidrBlock=ipv6_cidr) + modify_subnet = subnet.meta.client.modify_subnet_attribute + modify_subnet(SubnetId=subnet.id, + MapPublicIpOnLaunch={'Value': True}) + self._tag_resource(subnet) + + def _update_routing_table(self, vpc, internet_gateway_id): + """Update default routing table with internet gateway. + + This sets up internet access between the VPC via the internet gateway + by configuring routing tables for IPv4 and IPv6. + + @param vpc: VPC containing routing table to update + @param internet_gateway_id: gateway ID to use for routing + """ + route_table = list(vpc.route_tables.all())[0] + route_table.create_route(DestinationCidrBlock='0.0.0.0/0', + GatewayId=internet_gateway_id) + route_table.create_route(DestinationIpv6CidrBlock='::/0', + GatewayId=internet_gateway_id) + self._tag_resource(route_table) + + def _update_security_group(self, vpc): + """Allow only SSH inbound to default VPC security group. + + This revokes the initial accept all permissions and only allows + the SSH inbound. + + @param vpc: VPC containing security group to update + """ + ssh = { + 'IpProtocol': 'TCP', + 'FromPort': 22, + 'ToPort': 22, + 'IpRanges': [{'CidrIp': '0.0.0.0/0'}], + 'Ipv6Ranges': [{'CidrIpv6': '::/0'}] + } + + security_group = list(vpc.security_groups.all())[0] + security_group.revoke_ingress( + IpPermissions=security_group.ip_permissions + ) + security_group.authorize_ingress( + IpPermissions=[ssh] + ) + self._tag_resource(security_group) + + def _get_subnet(self): + """Return first subnet from VPC. + + For now there should only be one. + + @return_value: Return subnet object + """ + return list(self.vpc.subnets.all())[0] + + def _get_security_group(self): + """Return default security group from VPC. + + For now there should only be one. + + @return_value: Return first security group object + """ + return list(self.vpc.security_groups.all())[0] + + def get_image(self, img_conf): + """Get image using specified image configuration. + + @param img_conf: configuration for image + @return_value: cloud_tests.images instance + """ + if img_conf['root-store'] == 'ebs': + image_type = 'hvm-ssd' + elif img_conf['root-store'] == 'instance-store': + image_type = 'hvm-instance' + else: + raise RuntimeError('Unknown root-store type: %s' % + (img_conf['root-store'])) + + image_filter = { + 'Name': 'name', + 'Values': ['ubuntu/images-testing/%s/ubuntu-%s-daily-%s-server-*' + % (image_type, img_conf['release'], img_conf['arch'])] + } + response = self.ec2_client.describe_images(Filters=[image_filter]) + images = sorted(response['Images'], key=lambda k: k['CreationDate']) + + try: + image_ami = images[-1]['ImageId'] + except IndexError: + raise RuntimeError('No images found for %s!' % img_conf['release']) + + image = EC2Image(self, img_conf, image_ami) + return image + + def create_instance(self, properties, config, features, + image_ami, user_data): + """Create an instance + + @param src_img_path: image path to launch from + @param properties: image properties + @param config: image configuration + @param features: image features + @param image_desc: description of image being launched + @return_value: cloud_tests.instances instance + """ + return EC2Instance(self, properties, config, features, + image_ami, user_data) + +# vi: ts=4 expandtab diff --git a/tests/cloud_tests/platforms/ec2/snapshot.py b/tests/cloud_tests/platforms/ec2/snapshot.py new file mode 100644 index 0000000..01ca22f --- /dev/null +++ b/tests/cloud_tests/platforms/ec2/snapshot.py @@ -0,0 +1,52 @@ +# This file is part of cloud-init. See LICENSE file for license information. + +"""Base EC2 snapshot.""" + +from ..snapshots import Snapshot + + +class EC2Snapshot(Snapshot): + """EC2 image copy backed snapshot.""" + + platform_name = 'ec2' + + def __init__(self, platform, properties, config, features, image_ami): + """Set up snapshot. + + @param platform: platform object + @param properties: image properties + @param config: image config + @param features: supported feature flags + @param image_ami: string of image ami ID + """ + super(EC2Snapshot, self).__init__( + platform, properties, config, features) + + self.image_ami = image_ami + + def launch(self, user_data, meta_data=None, block=True, start=True, + use_desc=None): + """Launch instance. + + @param user_data: user-data for the instance + @param instance_id: instance-id for the instance + @param block: wait until instance is created + @param start: start instance and wait until fully started + @param image_ami: string of image ami ID + @param use_desc: string of test name + @return_value: an Instance + """ + instance = self.platform.create_instance( + self.properties, self.config, self.features, + self.image_ami, user_data) + + if start: + instance.start() + + return instance + + def destroy(self): + """Clean up snapshot data.""" + super(EC2Snapshot, self).destroy() + +# vi: ts=4 expandtab diff --git a/tests/cloud_tests/platforms/instances.py b/tests/cloud_tests/platforms/instances.py index 8c59d62..d09f96b 100644 --- a/tests/cloud_tests/platforms/instances.py +++ b/tests/cloud_tests/platforms/instances.py @@ -1,14 +1,22 @@ # 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 util class Instance(TargetBase): """Base instance object.""" platform_name = None + _ssh_client = None def __init__(self, platform, name, properties, config, features): """Set up instance. @@ -26,6 +34,10 @@ class Instance(TargetBase): self.features = features self._tmp_count = 0 + self.ssh_ip = None + self.ssh_port = None + self.ssh_key_file = None + def console_log(self): """Instance console. @@ -49,6 +61,55 @@ class Instance(TargetBase): """Clean up instance.""" pass + def _ssh_connect(self): + """Connect via SSH.""" + if self._ssh_client: + return self._ssh_client + + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + private_key = paramiko.RSAKey.from_private_key_file(self.ssh_key_file) + + retries = 10 + while retries: + try: + client.connect(username='ubuntu', 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(1) + + ssh_cmd = 'Failed command to: ubuntu@%s:%s' % ( + self.ssh_ip, self.ssh_port + ) + raise util.InTargetExecuteError(b'', b'', 1, ssh_cmd, 'ssh') + + 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() + except SSHException as e: + raise util.InTargetExecuteError(b'', b'', 1, command, 'ssh', + reason=e) + + return (fp_out.read(), fp_err.read(), rc) + def _wait_for_system(self, wait_for_cloud_init): """Wait until system has fully booted and cloud-init has finished. diff --git a/tests/cloud_tests/platforms/nocloudkvm/image.py b/tests/cloud_tests/platforms/nocloudkvm/image.py index 09ff2a3..8fff422 100644 --- a/tests/cloud_tests/platforms/nocloudkvm/image.py +++ b/tests/cloud_tests/platforms/nocloudkvm/image.py @@ -24,6 +24,8 @@ class NoCloudKVMImage(Image): @param config: image configuration @param img_path: path to the image """ + super(NoCloudKVMImage, self).__init__(platform, config) + self.modified = False self._workd = tempfile.mkdtemp(prefix='NoCloudKVMImage') self._orig_img_path = orig_img_path @@ -33,8 +35,6 @@ class NoCloudKVMImage(Image): c_util.subp(['qemu-img', 'create', '-f', 'qcow2', '-b', orig_img_path, self._img_path]) - super(NoCloudKVMImage, self).__init__(platform, config) - @property def properties(self): """Dictionary containing: 'arch', 'os', 'version', 'release'.""" diff --git a/tests/cloud_tests/platforms/nocloudkvm/instance.py b/tests/cloud_tests/platforms/nocloudkvm/instance.py index 9bb2425..27d4716 100644 --- a/tests/cloud_tests/platforms/nocloudkvm/instance.py +++ b/tests/cloud_tests/platforms/nocloudkvm/instance.py @@ -4,7 +4,6 @@ import copy import os -import paramiko import socket import subprocess import time @@ -26,7 +25,6 @@ class NoCloudKVMInstance(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): @@ -39,6 +37,10 @@ class NoCloudKVMInstance(Instance): @param config: dictionary of configuration values @param features: dictionary of supported feature flags """ + super(NoCloudKVMInstance, self).__init__( + platform, name, properties, config, features + ) + self.user_data = user_data if meta_data: meta_data = copy.deepcopy(meta_data) @@ -56,7 +58,6 @@ class NoCloudKVMInstance(Instance): platform.config['data_dir'], platform.config['private_key']) self.ssh_pubkey_file = os.path.join( platform.config['data_dir'], platform.config['public_key']) - self.ssh_pubkey = None if self.ssh_pubkey_file: with open(self.ssh_pubkey_file, "r") as fp: @@ -66,6 +67,7 @@ class NoCloudKVMInstance(Instance): meta_data['public-keys'] = [] meta_data['public-keys'].append(self.ssh_pubkey) + self.ssh_ip = '127.0.0.1' self.ssh_port = None self.pid = None self.pid_file = None @@ -73,9 +75,6 @@ class NoCloudKVMInstance(Instance): self.disk = image_path self.meta_data = meta_data - super(NoCloudKVMInstance, self).__init__( - platform, name, properties, config, features) - def destroy(self): """Clean up instance.""" if self.pid: @@ -125,50 +124,6 @@ class NoCloudKVMInstance(Instance): 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'] diff --git a/tests/cloud_tests/platforms/nocloudkvm/platform.py b/tests/cloud_tests/platforms/nocloudkvm/platform.py index 8593346..f7c915f 100644 --- a/tests/cloud_tests/platforms/nocloudkvm/platform.py +++ b/tests/cloud_tests/platforms/nocloudkvm/platform.py @@ -21,6 +21,11 @@ class NoCloudKVMPlatform(Platform): platform_name = 'nocloud-kvm' + def __init__(self, config): + """Set up platform.""" + super(NoCloudKVMPlatform, self).__init__(config) + self._generate_ssh_keys(config['data_dir']) + def get_image(self, img_conf): """Get image using specified image configuration. diff --git a/tests/cloud_tests/platforms/nocloudkvm/snapshot.py b/tests/cloud_tests/platforms/nocloudkvm/snapshot.py index 2dae359..1070dff 100644 --- a/tests/cloud_tests/platforms/nocloudkvm/snapshot.py +++ b/tests/cloud_tests/platforms/nocloudkvm/snapshot.py @@ -22,14 +22,15 @@ class NoCloudKVMSnapshot(Snapshot): @param features: supported feature flags @param image_path: image file to snapshot. """ + super(NoCloudKVMSnapshot, self).__init__( + platform, properties, config, features + ) + self._workd = tempfile.mkdtemp(prefix='NoCloudKVMSnapshot') snapshot = os.path.join(self._workd, 'snapshot') shutil.copyfile(image_path, snapshot) self._image_path = snapshot - super(NoCloudKVMSnapshot, self).__init__( - platform, properties, config, features) - def launch(self, user_data, meta_data=None, block=True, start=True, use_desc=None): """Launch instance. diff --git a/tests/cloud_tests/platforms/platforms.py b/tests/cloud_tests/platforms/platforms.py index 2897536..2791287 100644 --- a/tests/cloud_tests/platforms/platforms.py +++ b/tests/cloud_tests/platforms/platforms.py @@ -1,6 +1,9 @@ # This file is part of cloud-init. See LICENSE file for license information. """Base platform class.""" +import os + +from cloudinit import util as c_util class Platform(object): @@ -24,4 +27,16 @@ class Platform(object): """Clean up platform data.""" pass + def _generate_ssh_keys(self, data_dir): + """Generate SSH keys to be used with image.""" + filename = os.path.join(data_dir, 'id_rsa') + + if os.path.exists(filename): + c_util.del_file(filename) + + c_util.subp(['ssh-keygen', '-t', 'rsa', '-b', '4096', + '-f', filename, '-P', '', + '-C', 'ubuntu@cloud_test'], + capture=True) + # vi: ts=4 expandtab diff --git a/tests/cloud_tests/releases.yaml b/tests/cloud_tests/releases.yaml index e593380..ea40750 100644 --- a/tests/cloud_tests/releases.yaml +++ b/tests/cloud_tests/releases.yaml @@ -27,6 +27,9 @@ default_release_config: # features groups and additional feature settings feature_groups: [] features: {} + ec2: + # Choose from: [ebs, instance-store] + root-store: ebs nocloud-kvm: mirror_url: https://cloud-images.ubuntu.com/daily mirror_dir: '/srv/citest/nocloud-kvm' diff --git a/tests/cloud_tests/setup_image.py b/tests/cloud_tests/setup_image.py index 179f40d..6d24211 100644 --- a/tests/cloud_tests/setup_image.py +++ b/tests/cloud_tests/setup_image.py @@ -5,7 +5,6 @@ from functools import partial import os -from cloudinit import util as c_util from tests.cloud_tests import LOG from tests.cloud_tests import stage, util @@ -192,20 +191,6 @@ def enable_repo(args, image): image.execute(cmd, description=msg) -def generate_ssh_keys(data_dir): - """Generate SSH keys to be used with image.""" - LOG.info('generating SSH keys') - filename = os.path.join(data_dir, 'id_rsa') - - if os.path.exists(filename): - c_util.del_file(filename) - - c_util.subp(['ssh-keygen', '-t', 'rsa', '-b', '4096', - '-f', filename, '-P', '', - '-C', 'ubuntu@cloud_test'], - capture=True) - - def setup_image(args, image): """Set up image as specified in args. @@ -239,9 +224,6 @@ def setup_image(args, image): LOG.info('setting up %s', image) res = stage.run_stage( 'set up for {}'.format(image), calls, continue_after_error=False) - LOG.debug('after setup complete, installed cloud-init version is: %s', - installed_package_version(image, 'cloud-init')) - generate_ssh_keys(args.data_dir) return res # vi: ts=4 expandtab |
