From 242ad6477cb25f5d92ef979527231dc83777bccf Mon Sep 17 00:00:00 2001 From: Joe Gordon Date: Mon, 16 Dec 2013 12:00:59 -0800 Subject: [PATCH] Remove smoketests We don't use them anymore. Also remove boto_v6 since only smoketests have was using it. Change-Id: Iee206fd4ba6cd502ded794f2de302062604d4186 --- contrib/boto_v6/__init__.py | 37 --- contrib/boto_v6/ec2/__init__.py | 0 contrib/boto_v6/ec2/connection.py | 141 ---------- contrib/boto_v6/ec2/instance.py | 34 --- smoketests/__init__.py | 25 -- smoketests/base.py | 201 -------------- smoketests/flags.py | 39 --- smoketests/proxy.sh | 29 -- smoketests/public_network_smoketests.py | 180 ------------- smoketests/random.image | Bin 65536 -> 0 bytes smoketests/random.kernel | Bin 16384 -> 0 bytes smoketests/requirements.txt | 1 - smoketests/run_tests.py | 316 ---------------------- smoketests/test_netadmin.py | 203 -------------- smoketests/test_sysadmin.py | 337 ------------------------ 15 files changed, 1543 deletions(-) delete mode 100644 contrib/boto_v6/__init__.py delete mode 100644 contrib/boto_v6/ec2/__init__.py delete mode 100644 contrib/boto_v6/ec2/connection.py delete mode 100644 contrib/boto_v6/ec2/instance.py delete mode 100644 smoketests/__init__.py delete mode 100644 smoketests/base.py delete mode 100644 smoketests/flags.py delete mode 100755 smoketests/proxy.sh delete mode 100644 smoketests/public_network_smoketests.py delete mode 100644 smoketests/random.image delete mode 100644 smoketests/random.kernel delete mode 100644 smoketests/requirements.txt delete mode 100644 smoketests/run_tests.py delete mode 100644 smoketests/test_netadmin.py delete mode 100644 smoketests/test_sysadmin.py diff --git a/contrib/boto_v6/__init__.py b/contrib/boto_v6/__init__.py deleted file mode 100644 index 9fec157f178d..000000000000 --- a/contrib/boto_v6/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/ -# Copyright (c) 2010, Eucalyptus Systems, Inc. -# All rights reserved. -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, dis- -# tribute, sublicense, and/or sell copies of the Software, and to permit -# persons to whom the Software is furnished to do so, subject to the fol- -# lowing conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- -# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - - -def connect_ec2(aws_access_key_id=None, aws_secret_access_key=None, **kwargs): - """ - :type aws_access_key_id: string - :param aws_access_key_id: Your AWS Access Key ID - - :type aws_secret_access_key: string - :param aws_secret_access_key: Your AWS Secret Access Key - - :rtype: :class:`boto.ec2.connection.EC2Connection` - :return: A connection to Amazon's EC2 - """ - from boto_v6.ec2.connection import EC2ConnectionV6 - return EC2ConnectionV6(aws_access_key_id, aws_secret_access_key, **kwargs) diff --git a/contrib/boto_v6/ec2/__init__.py b/contrib/boto_v6/ec2/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/contrib/boto_v6/ec2/connection.py b/contrib/boto_v6/ec2/connection.py deleted file mode 100644 index 4cec65ad8368..000000000000 --- a/contrib/boto_v6/ec2/connection.py +++ /dev/null @@ -1,141 +0,0 @@ -''' -Created on 2010/12/20 - -@author: Nachi Ueno -''' -import base64 -import boto -import boto.ec2 -import boto.ec2.securitygroup as securitygroup -from boto_v6.ec2.instance import ReservationV6 - - -class EC2ConnectionV6(boto.ec2.EC2Connection): - ''' - EC2Connection for OpenStack IPV6 mode - ''' - def get_all_instances(self, instance_ids=None, filters=None): - """ - Retrieve all the instances associated with your account. - - :type instance_ids: list - :param instance_ids: A list of strings of instance IDs - - :type filters: dict - :param filters: Optional filters that can be used to limit - the results returned. Filters are provided - in the form of a dictionary consisting of - filter names as the key and filter values - as the value. The set of allowable filter - names/values is dependent on the request - being performed. Check the EC2 API guide - for details. - - :rtype: list - :return: A list of :class:`boto.ec2.instance.Reservation` - """ - params = {} - if instance_ids: - self.build_list_params(params, instance_ids, 'InstanceId') - if filters: - self.build_filter_params(params, filters) - return self.get_list('DescribeInstancesV6', params, - [('item', ReservationV6)]) - - def run_instances(self, image_id, min_count=1, max_count=1, - key_name=None, security_groups=None, - user_data=None, addressing_type=None, - instance_type='m1.small', placement=None, - kernel_id=None, ramdisk_id=None, - monitoring_enabled=False, subnet_id=None, - block_device_map=None): - """ - Runs an image on EC2. - - :type image_id: string - :param image_id: The ID of the image to run - - :type min_count: int - :param min_count: The minimum number of instances to launch - - :type max_count: int - :param max_count: The maximum number of instances to launch - - :type key_name: string - :param key_name: The name of the key pair with which to - launch instances - - :type security_groups: list of strings - :param security_groups: The names of the security groups with - which to associate instances - - :type user_data: string - :param user_data: The user data passed to the launched instances - - :type instance_type: string - :param instance_type: The type of instance to run - (m1.small, m1.large, m1.xlarge) - - :type placement: string - :param placement: The availability zone in which to launch - the instances - - :type kernel_id: string - :param kernel_id: The ID of the kernel with which to - launch the instances - - :type ramdisk_id: string - :param ramdisk_id: The ID of the RAM disk with which to - launch the instances - - :type monitoring_enabled: bool - :param monitoring_enabled: Enable CloudWatch monitoring - on the instance. - - :type subnet_id: string - :param subnet_id: The subnet ID within which to launch - the instances for VPC. - - :type block_device_map: - :class:`boto.ec2.blockdevicemapping.BlockDeviceMapping` - :param block_device_map: A BlockDeviceMapping data structure - describing the EBS volumes associated - with the Image. - - :rtype: Reservation - :return: The :class:`boto.ec2.instance.ReservationV6` - associated with the request for machines - """ - params = {'ImageId': image_id, - 'MinCount': min_count, - 'MaxCount': max_count} - if key_name: - params['KeyName'] = key_name - if security_groups: - l = [] - for group in security_groups: - if isinstance(group, securitygroup.SecurityGroup): - l.append(group.name) - else: - l.append(group) - self.build_list_params(params, l, 'SecurityGroup') - if user_data: - params['UserData'] = base64.b64encode(user_data) - if addressing_type: - params['AddressingType'] = addressing_type - if instance_type: - params['InstanceType'] = instance_type - if placement: - params['Placement.AvailabilityZone'] = placement - if kernel_id: - params['KernelId'] = kernel_id - if ramdisk_id: - params['RamdiskId'] = ramdisk_id - if monitoring_enabled: - params['Monitoring.Enabled'] = 'true' - if subnet_id: - params['SubnetId'] = subnet_id - if block_device_map: - block_device_map.build_list_params(params) - return self.get_object('RunInstances', params, - ReservationV6, verb='POST') diff --git a/contrib/boto_v6/ec2/instance.py b/contrib/boto_v6/ec2/instance.py deleted file mode 100644 index 6f088c67e73a..000000000000 --- a/contrib/boto_v6/ec2/instance.py +++ /dev/null @@ -1,34 +0,0 @@ -''' -Created on 2010/12/20 - -@author: Nachi Ueno -''' -from boto.ec2 import instance -from boto import resultset - - -class ReservationV6(instance.Reservation): - def startElement(self, name, attrs, connection): - if name == 'instancesSet': - self.instances = resultset.ResultSet([('item', InstanceV6)]) - return self.instances - elif name == 'groupSet': - self.groups = resultset.ResultSet([('item', instance.Group)]) - return self.groups - else: - return None - - -class InstanceV6(instance.Instance): - def __init__(self, connection=None): - instance.Instance.__init__(self, connection) - self.dns_name_v6 = None - - def endElement(self, name, value, connection): - instance.Instance.endElement(self, name, value, connection) - if name == 'dnsNameV6': - self.dns_name_v6 = value - - def _update(self, updated): - self.__dict__.update(updated.__dict__) - self.dns_name_v6 = updated.dns_name_v6 diff --git a/smoketests/__init__.py b/smoketests/__init__.py deleted file mode 100644 index 67609d4249f0..000000000000 --- a/smoketests/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -:mod:`smoketests` -- Nova Integration "Smoke" Tests -===================================================== - -.. automodule:: nova.volume - :platform: Unix -""" diff --git a/smoketests/base.py b/smoketests/base.py deleted file mode 100644 index c3b2be131181..000000000000 --- a/smoketests/base.py +++ /dev/null @@ -1,201 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import boto -from boto.ec2 import regioninfo -import commands -import httplib -import os -import paramiko -import sys -import time -import unittest - -from smoketests import flags - -SUITE_NAMES = '[image, instance, volume]' -FLAGS = flags.FLAGS -flags.DEFINE_string('suite', None, 'Specific test suite to run ' + SUITE_NAMES) -flags.DEFINE_integer('ssh_tries', 3, 'Numer of times to try ssh') - - -class SmokeTestCase(unittest.TestCase): - def connect_ssh(self, ip, key_name): - key = paramiko.RSAKey.from_private_key_file('/tmp/%s.pem' % key_name) - tries = 0 - while(True): - try: - client = paramiko.SSHClient() - client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - client.connect(ip, username='root', pkey=key, timeout=5) - return client - except (paramiko.AuthenticationException, paramiko.SSHException): - tries += 1 - if tries == FLAGS.ssh_tries: - raise - - def can_ping(self, ip, command="ping"): - """Attempt to ping the specified IP, and give up after 1 second.""" - - # NOTE(devcamcar): ping timeout flag is different in OSX. - if sys.platform == 'darwin': - timeout_flag = 't' - else: - timeout_flag = 'w' - - status, output = commands.getstatusoutput('%s -c1 -%s1 %s' % - (command, timeout_flag, ip)) - return status == 0 - - def wait_for_running(self, instance, tries=60, wait=1): - """Wait for instance to be running.""" - for x in xrange(tries): - instance.update() - if instance.state.startswith('running'): - return True - time.sleep(wait) - else: - return False - - def wait_for_deleted(self, instance, tries=60, wait=1): - """Wait for instance to be deleted.""" - for x in xrange(tries): - try: - #NOTE(dprince): raises exception when instance id disappears - instance.update(validate=True) - except ValueError: - return True - time.sleep(wait) - else: - return False - - def wait_for_ping(self, ip, command="ping", tries=120): - """Wait for ip to be pingable.""" - for x in xrange(tries): - if self.can_ping(ip, command): - return True - else: - return False - - def wait_for_ssh(self, ip, key_name, tries=30, wait=5): - """Wait for ip to be sshable.""" - for x in xrange(tries): - try: - conn = self.connect_ssh(ip, key_name) - conn.close() - except Exception: - time.sleep(wait) - else: - return True - else: - return False - - def connection_for_env(self, **kwargs): - """ - Returns a boto ec2 connection for the current environment. - """ - access_key = os.getenv('EC2_ACCESS_KEY') - secret_key = os.getenv('EC2_SECRET_KEY') - clc_url = os.getenv('EC2_URL') - - if not access_key or not secret_key or not clc_url: - raise Exception('Missing EC2 environment variables. Please source ' - 'the appropriate novarc file before running this ' - 'test.') - - parts = self.split_clc_url(clc_url) - if FLAGS.use_ipv6: - return boto_v6.connect_ec2(aws_access_key_id=access_key, - aws_secret_access_key=secret_key, - is_secure=parts['is_secure'], - region=regioninfo.RegionInfo(None, - 'nova', - parts['ip']), - port=parts['port'], - path='/services/Cloud', - **kwargs) - - return boto.connect_ec2(aws_access_key_id=access_key, - aws_secret_access_key=secret_key, - is_secure=parts['is_secure'], - region=regioninfo.RegionInfo(None, - 'nova', - parts['ip']), - port=parts['port'], - path='/services/Cloud', - **kwargs) - - def split_clc_url(self, clc_url): - """Splits a cloud controller endpoint url.""" - parts = httplib.urlsplit(clc_url) - is_secure = parts.scheme == 'https' - ip, port = parts.netloc.split(':') - return {'ip': ip, 'port': int(port), 'is_secure': is_secure} - - def create_key_pair(self, conn, key_name): - try: - os.remove('/tmp/%s.pem' % key_name) - except Exception: - pass - key = conn.create_key_pair(key_name) - key.save('/tmp/') - return key - - def delete_key_pair(self, conn, key_name): - conn.delete_key_pair(key_name) - try: - os.remove('/tmp/%s.pem' % key_name) - except Exception: - pass - - def bundle_image(self, image, tempdir='/tmp', kernel=False): - cmd = 'euca-bundle-image -i %s -d %s' % (image, tempdir) - if kernel: - cmd += ' --kernel true' - status, output = commands.getstatusoutput(cmd) - if status != 0: - raise Exception(output) - return True - - def upload_image(self, bucket_name, image, tempdir='/tmp'): - cmd = 'euca-upload-bundle -b ' - cmd += '%s -m %s/%s.manifest.xml' % (bucket_name, tempdir, image) - status, output = commands.getstatusoutput(cmd) - if status != 0: - raise Exception(output) - return True - - def delete_bundle_bucket(self, bucket_name): - cmd = 'euca-delete-bundle --clear -b %s' % (bucket_name) - status, output = commands.getstatusoutput(cmd) - if status != 0: - raise Exception(output) - return True - - -TEST_DATA = {} -if FLAGS.use_ipv6: - global boto_v6 - boto_v6 = __import__('boto_v6') - - -class UserSmokeTestCase(SmokeTestCase): - def setUp(self): - global TEST_DATA - self.conn = self.connection_for_env() - self.data = TEST_DATA diff --git a/smoketests/flags.py b/smoketests/flags.py deleted file mode 100644 index 472e76409015..000000000000 --- a/smoketests/flags.py +++ /dev/null @@ -1,39 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Package-level global flags are defined here, the rest are defined -where they're used. -""" - - -from gflags import * # noqa - -# This keeps pylint from barfing on the imports -FLAGS = FLAGS -DEFINE_string = DEFINE_string -DEFINE_integer = DEFINE_integer -DEFINE_bool = DEFINE_bool - -# __GLOBAL FLAGS ONLY__ -# Define any app-specific flags in their own files, docs at: -# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#39 - -DEFINE_string('region', 'nova', 'Region to use') -DEFINE_string('test_image', 'ami-tty', 'Image to use for launch tests') -DEFINE_bool('use_ipv6', False, 'use the ipv6 or not') diff --git a/smoketests/proxy.sh b/smoketests/proxy.sh deleted file mode 100755 index b9057fe9dec6..000000000000 --- a/smoketests/proxy.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -# This is a simple shell script that uses netcat to set up a proxy to the -# metadata server on port 80 and to a google ip on port 8080. This is meant -# to be passed in by a script to an instance via user data, so that -# automatic testing of network connectivity can be performed. - -# Example usage: -# euca-run-instances -t m1.tiny -f proxy.sh ami-tty - -mkfifo backpipe1 -mkfifo backpipe2 - -if nc -h 2>&1 | grep -i openbsd -then - NC_LISTEN="nc -l" -else - NC_LISTEN="nc -l -p" -fi - -# NOTE(vish): proxy metadata on port 80 -while true; do - $NC_LISTEN 80 0backpipe1 -done & - -# NOTE(vish): proxy google on port 8080 -while true; do - $NC_LISTEN 8080 0backpipe2 -done & diff --git a/smoketests/public_network_smoketests.py b/smoketests/public_network_smoketests.py deleted file mode 100644 index ef6582770438..000000000000 --- a/smoketests/public_network_smoketests.py +++ /dev/null @@ -1,180 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import commands -import os -import random -import sys -import time - -# If ../nova/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): - sys.path.insert(0, possible_topdir) - -from smoketests import base -from smoketests import flags - -#Note that this test should run from -#public network (outside of private network segments) -#Please set EC2_URL correctly -#You should use admin account in this test - -FLAGS = flags.FLAGS - -TEST_PREFIX = 'test%s' % int(random.random() * 1000000) -TEST_BUCKET = '%s_bucket' % TEST_PREFIX -TEST_KEY = '%s_key' % TEST_PREFIX -TEST_KEY2 = '%s_key2' % TEST_PREFIX -TEST_DATA = {} - - -class InstanceTestsFromPublic(base.UserSmokeTestCase): - def test_001_can_create_keypair(self): - key = self.create_key_pair(self.conn, TEST_KEY) - self.assertEqual(key.name, TEST_KEY) - - def test_002_security_group(self): - security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd") - for x in range(random.randint(4, 8))) - group = self.conn.create_security_group(security_group_name, - 'test group') - group.connection = self.conn - group.authorize('tcp', 22, 22, '0.0.0.0/0') - if FLAGS.use_ipv6: - group.authorize('tcp', 22, 22, '::/0') - - reservation = self.conn.run_instances(FLAGS.test_image, - key_name=TEST_KEY, - security_groups=[security_group_name], - instance_type='m1.tiny') - self.data['security_group_name'] = security_group_name - self.data['group'] = group - self.data['instance_id'] = reservation.instances[0].id - - def test_003_instance_with_group_runs_within_60_seconds(self): - reservations = self.conn.get_all_instances([self.data['instance_id']]) - instance = reservations[0].instances[0] - # allow 60 seconds to exit pending with IP - for x in xrange(60): - instance.update() - if instance.state == u'running': - break - time.sleep(1) - else: - self.fail('instance failed to start') - ip = reservations[0].instances[0].private_ip_address - self.assertFalse(ip == '0.0.0.0') - self.data['private_ip'] = ip - if FLAGS.use_ipv6: - ipv6 = reservations[0].instances[0].dns_name_v6 - self.assertFalse(ipv6 is None) - self.data['ip_v6'] = ipv6 - - def test_004_can_ssh_to_ipv6(self): - if FLAGS.use_ipv6: - for x in xrange(20): - try: - conn = self.connect_ssh( - self.data['ip_v6'], TEST_KEY) - conn.close() - except Exception: - time.sleep(1) - else: - break - else: - self.fail('could not ssh to instance') - - def test_012_can_create_instance_with_keypair(self): - if 'instance_id' in self.data: - self.conn.terminate_instances([self.data['instance_id']]) - reservation = self.conn.run_instances(FLAGS.test_image, - key_name=TEST_KEY, - instance_type='m1.tiny') - self.assertEqual(len(reservation.instances), 1) - self.data['instance_id'] = reservation.instances[0].id - - def test_013_instance_runs_within_60_seconds(self): - reservations = self.conn.get_all_instances([self.data['instance_id']]) - instance = reservations[0].instances[0] - # allow 60 seconds to exit pending with IP - for x in xrange(60): - instance.update() - if instance.state == u'running': - break - time.sleep(1) - else: - self.fail('instance failed to start') - ip = reservations[0].instances[0].private_ip_address - self.assertFalse(ip == '0.0.0.0') - self.data['private_ip'] = ip - if FLAGS.use_ipv6: - ipv6 = reservations[0].instances[0].dns_name_v6 - self.assertFalse(ipv6 is None) - self.data['ip_v6'] = ipv6 - - def test_014_can_not_ping_private_ip(self): - for x in xrange(4): - # ping waits for 1 second - status, output = commands.getstatusoutput( - 'ping -c1 %s' % self.data['private_ip']) - if status == 0: - self.fail('can ping private ip from public network') - if FLAGS.use_ipv6: - status, output = commands.getstatusoutput( - 'ping6 -c1 %s' % self.data['ip_v6']) - if status == 0: - self.fail('can ping ipv6 from public network') - else: - pass - - def test_015_can_not_ssh_to_private_ip(self): - for x in xrange(1): - try: - conn = self.connect_ssh(self.data['private_ip'], TEST_KEY) - conn.close() - except Exception: - time.sleep(1) - else: - self.fail('can ssh for ipv4 address from public network') - - if FLAGS.use_ipv6: - for x in xrange(1): - try: - conn = self.connect_ssh( - self.data['ip_v6'], TEST_KEY) - conn.close() - except Exception: - time.sleep(1) - else: - self.fail('can ssh for ipv6 address from public network') - - def test_999_tearDown(self): - self.delete_key_pair(self.conn, TEST_KEY) - security_group_name = self.data['security_group_name'] - group = self.data['group'] - if group: - group.revoke('tcp', 22, 22, '0.0.0.0/0') - if FLAGS.use_ipv6: - group.revoke('tcp', 22, 22, '::/0') - self.conn.delete_security_group(security_group_name) - if 'instance_id' in self.data: - self.conn.terminate_instances([self.data['instance_id']]) diff --git a/smoketests/random.image b/smoketests/random.image deleted file mode 100644 index f2c0c30bbc73beaf5f13aff01709cb6a20f9e198..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65536 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⪻KX$Gi_s6->& zMNS&&j|z9?uj!Pb}HdtIUSrQ~?=T`UUOXb)khL_+^3-Z$;ccK|!Xj zFK4fgj=xx_f2&mZl$5bkcZa=CY`g)>03gL`Z}{^Dl5?7-+HKsXd zbajjCanU!l@#yK45b`6}yGNE4FA-H>phHJMMC_nK8&H;BsKL-A_jWq9C`z)X-U|7rzQH6-3uyQJegzYKNTBw)!rmlaJmSzv z-dRgdKr3%8{fSF5`1Wc!YtJ}AZ@^dAUy$29E8blH6stIhl7lL0ro{S9(S`Bj*(y?h zsKW6z#}WX?vk4szf^on!I;zk;%mzn^^MHn{=hPZNmG0Wp zeZ?H#+7gcZ5kG|nQBn_^MyRV=V|LS@0i`k*Oa@-TB}M+_IRpNnozz*{+4sSsrus^1 zSqG_RlbfsJcol)N`fHqut?7Fqv1lzw2ZpHCt5HgVCOwG#ZR5(VOw%v-D)RZ|9@U27 zSF$dm65vI3mH1Tz^o-AVkB4qY#aNXVvB{RbqP1j&=U&4t4VO$_MxF|zNb7L~yD9!_ zl`oaH6wx%?yHr7wz)1AFt-?_qjm=w(I7#}N^?WMMa(10MF{N~osl#O%XEJbRmO4yV zwBmn5?Cc;u%?SZ4S5}=b@Y9Z+7V&e(>@_5Sak(zdERCV`$(+}ss+#43-+FGpSzgKE7{>k* zWxV#ug}3SB(tMLd;NIRpZqqh#E9}M{5%b_6o?UG)@If<$lotYfqBn(q!V3*##5DsT zN?fEXxA`2i@Q=CoQ^Rp2M7D~6-T469P$XIhe1hx=`@s0Qk>K@0O$aY(I;L|DBe`X| z#FeSma0%h>9^#G}yZEIO{|KgD^T1L=S0@uUEJbB}A?$}`stK+rmpz8j5FDm)sZaU{ zu_^Tkcgc_3YfDZOG6z`(8glLuv$5*il@k<|bdAc&gS^Xk?0U&Z=2y#87f3k{8gKZr zKjw>yLA(g-_zc`Uk3=SDpGqv<>hA%xC5erm${X~P!uV+fmsDEIk>A~hAJTKDw^nge zQRwLh`?D+HVUDv6IH_pK(;mOc`@doJO^tT|pkH3moD8t?3TDUP-w?tFz4JVr_yXM3mw@;-jt)(~^B6Qm2t0}}4Yz|RMhCRI^3<(8E)N}Q8 zk>>eUt-qsg{gbRKv_iap^W;OceqWMl1O`5C+t=@yZfM3sN#cW17_Rv|6Mx~~DNRn0gLdw0n+zE+a1%yIIrp+} zHQAK;63P4aA))MnQMYzZiKGosMFk$C1JqI@?t{g2NLv2P&O-yI!?!=Xcr}C$DbVBb zBiX)wCl(!fU(7GthmkJ2FDKAi%-^akm4u;t93eKcHLBl#hhNP!g6i;m4Pv2`=N525 z*APN`#~!U+<=)c|=nI3zX>$Py73l!A(GebT5P074(l4LJ%BumZ=#YTJ~)7(Kx1jL#7cHVBmKqxKTBDDBA*GhH0RnU*{De7vjOo3Q5GKxSM4Nk-!+_WgPq2%jx2MOQyns}G6hiboiY zHAEQfQzfwAIzRY9U2ntHfr*?resw%*k3ebN4HTxW>~qiCTgCqGxdonl3kWv!@b48a z)4U0qExKe6sp_s?dhOQXlOp+66{V% z<6BeQcr%lwG%R)@zE z)lF}0@U%_aXnOd-uDrwc7A2+wvG_;G(x9WnPh=?bhtvKdQ{rtDW$1W!tng1OQY&~2(#hxcoYFSI4~|v1 zPj}oNAHe9MA5A!qlCP37W3DQ$roxGMvKZbZrQB}L*WdZ789l9!cSiLG-yM&7FhvF& z{s~idI0VHRPvO0;D5c9xlh$^acBn`E#B-Yyf(HSmXWpUzrvOBh18i&>lANFWVts+> zCppB+cHsCPK7-#v8(8Tvtg5Px*f=kqD|=KE3uRYit&8F@5SgTV!mAg6PA=jyVYn}c z4cD*+?7_)sS+mNXb#W>>9Q%ZCGk-a7x&tx%&SypuarZ)TGqGX5j*u6ecbnkF!yR~1 z;8kz@NK!c6a$k&1sJ!v#}29m z7w<8GveC*+_?jej;9vWj+zo`greeGPaF_wMpjLgnJ~8s_Ii`94*xWbkJPOPs1ige9(rRWzPvNMKFskFcVjhauuYk8{N>#!Hi|nGS@AH#Pof6x_l7NT+>;FA z2==#uPG(PV{Ma690oN4j$j-afW7^(@c767b;XJ1Vl$>6T>t@87G?c{v!u79Zu=gY5 zkE`~RRIlB99t28eg2%4l*%1ywze;$?18MM2U=TBV&wY?J@3?v0WY}fj?kt(7-jBn| zIE_&@_U8;NjF5&7lw$L8Gc2j;OZ@=DTcJ322kDy21Z7$0sr7#$tJigQ91$t6Rw|6> zi0oC2v8nva7~FC-*O-rcCJEZO_8)r+{==4rg;NwG$NXniN}?O-XKUq;!3cBG5YxRS z)mzW^IRUfP{om7Sg9CO#^xVg33h}@4?1EX=frzr4hm+XX|NnU|3==!Amx-WXS6!teDrFO_43_9`u z#QNBuw>(5g`ijHi28htCz(sTd(o`tj(Mpk)pdA|eonzr$MIAfp<_fH6XnV9a{HCnt zI0&3HD%_mfx}>BdddNTEh@wzyvzUWRz(LXbq?i`wJYWuH{yz>&EVj7XUnPbS)G0Jz zq5!mu{rpatW zjznLW6N^?4V1iyRweP-&{Li@>FUU)*ffiP))A*{JqPI%9#$8UWEQ)@txR8|sfXZ9; z&DO?!ML=CT6h7oy-KGw}o6alf<8V<5-W-`XLqX@7I_?mSC=msT+3}bYJ5Q&n5;|o& zEs3^U&Kz?I@pbXoD13od0e=czro@f;?vy)kSprFpJ@6K>IcGj8LqVD#kB0K-t zK&&CGIw?~=zItemkTFY_4Cdx>(3q=K+h5Cl3&NT>TJq27pXvpUJkITx1dO*hA1fXk zyO^R%{i^daKiz2gqWxAxC?-r|tL>fCPH(*;*&_Aw$wEnaLm@YGcPB7hiwBd)I-nmF zPxT(CB6>uvN7J?@?v&I`G*(b4wp|u!cR)sNg#)e}<6VQ&Vs{_cGqnpbz){GS(^Dn` z7l!nnY9+Y6?#~7m3N-^wU7ZNGp_tagz4q*@w3-^Hq^X6eLJf`?p?4@$Nuti zmTfuXcC5uActDxK3YAZNs`wYGBwQAcLdnh{Q`OY;KIDMHB-4e_vK-(gs}V)6p%Nw3rjmTmGQ&mhC6O&24$O zx-K;KtWo*<%JB|AxL^JHl0cW~T6|;_zd@@GUszmK=3czP?`@1Afp}@SLR_u6?92rj zU>8CYGF}&qIn&e=9z;E5LJWhHW9c;HU)q&`yxx161(ceMUT>&HINl7;XTJOF0g=v^ zgE^~7Rwc6Z@VV=KMri@5-Dd%^v5|hw8tZekAJ}mx!|@OqYyfRfpx5m09>6r&w-~)c z8ne^RS$9%h*nn%rYiV^HrvxTlzKy79v!}E|`CPtxyOw=}}b;9#s8FlCn69}&P; zDUFvtZtYOv40q66pcuBrdpuVC^@rm!{L|R?3;l(+8GX1ldl{Ssic37vuIa>cfZ(l1pgaYcC>dG%CRM#!);P(0YHY+5Wf_+qO zPk-q_;s;TeOY0p^Gv0vf;izyI`XwDj4DhjP&$dWE&zP zV-aJDLm%~C(^;@vhnC*&llXD3r4Q^#s?27bN2$}u*Mag$^ z$Z9KPZ}@j zKs;BW-d1Fc`R)1;4EDep2y7ftjT(CiDw*6tSV(Wex)I?*5pRo3Z-BwxcZZ2J)`&R7 zA+vo1N$u`HTzkRCa%gZn7{KRd1r==K#z6K4LieX$SA+ddI--r#|z|K+t3n-<8qw-e~B z=L;ssAMQ#CYe}E>0*{#wd%p;w?F^vh-sqIonEobr|K2D#`|$y1Xwp8_kjNLo6Hu|p z+Qo$r3pj!Xw4sR9Q#LA4;M7c@g~cE)@u=dvs!~s*JEHou@yp72`*QeVD5?X-OrY&A z?Q!P{uij|`ns3;jZiBqMy|3)WUZ9<$LIm4~dQ-|N*xz1TuD3JMfSc%oyHSeyp>zUA zR}7Iy>=5|Cx>mW`&EkC|$H0}w!xihf0w=Dqmb|gu5YjBG(d`w!u8&wm$<%utS**P` zjAV*JSc7{3Y|bU!kpbYny>0v1Vi56ue&+9$fM{9|$jN?BK=_fgT}r}3yRSy!1_6Z2TT)sL@vrvs9jBmNPD{cNLIQlTyG_vZ?AYw_t;3&goeEi5)2pg@Z zN;&Wz@$3}rRJuEltl+UzjJ_J?hEio2;r`cdwJw1c&^WJ03!KD&3c&wb z9V5=i@Xy(-$FZy%(5lpI=>Q9Z(xmJcL`N;^p8#ahsW?zzw6-K-$UF5klS0M;&HrBo z>q`&cc4OSV#|DcZQIu|W8Q|pyVzupgsglYKMozExI*|U&n{yVCwT1LG|3o@g+Y)#g z?R`mcts^cq7`r|te&}_;NK7pj`B%-P=gd@=(m|rU3%VzvsMi=cQ$5A>#@RA73ZmhM zBV*KK=}MhL`3jzwhoDUSnqBsrU-gmawh{h!e!8%wcv$h)}ZuaAR2v>!)mDkWl@24W2T z_B+C$N0M@`E*y8+T@C4|fK+0oVn5t`+J7L}*UFEePR%Z1x96^UyRG!^{{39hka;uF$Svw?f=43Dnoby8 z(NPQRoUBCbEAUcvZN4F3gz62Fiol5=-8?V7x(akkvd~J?>lOz&W))7*PoSc=2yvWbO-T?W+ZROP_H_H%%DAOF-U^7vQpJVIGFS0XZs;vvj ziyvJGqfmn?6%4_*M85M@i1?dmsPt>rN{1kkir$F|mkz2p14N5>w&~Xdfl|qWuwz97 zPr9M6N$H$QQ!T6^HV5UG z=#{ZRH0w)!{8B1PNhtb}L1J<7(u1yGE>R(#u38HdhOa6HZs^JzMptbUZH)FJl&5Nz z!s|or>jF`o8a7sFjO=B1gs#L?Mw5>^_h6hcpJG(tz;}KOOu8<(9gB5mgw0RIEM0fb zxM-6G!ax*e>EiMU@>tf&=1?o(B-FwgTI(`$K&U9_+TDYq|e>zcHPV77590arQJ z1~~>R^prjDO8(6?zhqI}gG*=sseWUo!vt?aCHB*r%7*P=f1TNe>YH{0$BWhj2)8s) zSILZ3K4OqNH&GEVD5-jdHjGRHs8~{FDsX861t>w0LJzy#m!#7v3SDto#mLmQQN4;B zeGgLKBb;{n){h|}_X!uvt@Sl^d;x12IeC02e+wj5xoaeO#S@Rk5XGp>WW>ZQ3*Qv= zVJ9QQ;sn;x2a&XB(E;86-cb|tbxyD>XaHvZ9NX%f^l2~jVa-p~bsFQ3--w^424{*w z1IaKrxIu|G90F?J(~X~0Mtcx14qfU;>pfe}NfX1H0SJ90!aczaAGnH=?;d?u zpo1ERtzYSE>}vkAry{I(4^iiahmA~lI!#e0EiIvKB1DBlq4SzjE~#8`#6xfn+c#<9vJeQa=YY3fok~Gr1H~=19vf*7 zTXY{_<@pJDS=)mLZMY7TOl{@{PG< z%($)Z1JjqHU`^YWfmQd*)OD!QI*+=Uc5asW*6=})ihneM5OeNk;j)fNFvejxe5+XW z(cV29hUncyk&t(NfcqL6$FoQ<@^m0n=^`f#|2v(s(D*63C9^*+;dz&aa+ntogNOmiA&c^GF9;*7cfRzhVL$!NkkPT#(Hq$z29D-jc;P)fC*@ds z*02bFlNWc27%e8jNoEzjBYi@tqv+J-PsMiPXZLIt%0pt4tjc8)u8 zj_`jjD>7q1p;^11@;mJLUkz(n9m$26p)ZumRnS<}AXS%OL}6&H2bySafR0JP$1I5g z=X{D|3veeu5h?_#iaRdyu8O~7WigyJ=*vbUo6(JyDf0kZUF&h0u$r4)h1tR42l}mA zBz~zfYWjt;-^LKPRE^|ef8A`FsA$#)){BpRHL>K+A+db=5YqzT6C@?TGaVN4ZNkel z&J_~bIc!paMFn53`KZDz6mITN z=v|0(!0cUIV*g;4X{`!cE3On7e*_CR!^f7eiTVN?elYx3C>&%@XTjl=qZQS8=}tot zDNNw3al__Xc6KbWXvPiaNRM2?Bc%EX8DP|Ky34nJ&2{#K3R+t|1lD91Hu>~gBl>`- zC}#uYog9%yv#}wRA~CP1JpaVN%*UxqQf>bYXI%EI$J`61#sXxNe|8i(jvlK6d63J17tCd9r$JZ$cYPz3R!iG`U5kgNFjO@vZjO$@W;S zk#L=y7ddj}_0FShrJLDMq}C{0e&Rfbqallyvi*9ga$Ac&!CGT<9TeV|Szfg`+2hLI z1~}4GrX|p(xA}V`?Yiz}KSv{qgb3Kx25UfB0{W7rSAK6`#TkJmh!0ZO&ETC)hmhdR zG<{ZFxsEgz->0Uef$${4)U&5HHn1!$LW+mt_uyZM(wC1f*eqQdaFJ}VA%_i+=eT?0 ze(@o!QO_P)jL<1XATpeumu2>rC$GFY3}IB3^fQ-*I=2Jz?jC$FD~$y*`Zr13@rLb|#l7gZ zOugTx;C@#I5{#kU!b*nh_4v&|KXNmSHwZG6g!O7j&)BWsM9LLNalaLQu2iky_W)(V zu`*tqK0=Dk8@d;wX@fbNTXA2Lzl=M%!>QN+I?Ic?*6l6or zZ^&0|yKxz?ty0%6o*;DG<`I+9AputCNMJR>WmVI@ce#0nOu zz2IH86L#0uYSY0NvX>OiDD4c@MgBx8|%rhH04E0BkgH@jXV8N;$2xRa;{IAyBF0Xsh1{BA-a}243vRRpT+Dxepet2?yh%kXVGi(WRt;4rEy_!2Zr(FxOd!g?=`6i{h3r z+sM~TfG*B~2IenuD;XhPd5Z<>I@bZV^mB0&*6A2~U)stQNQSj&Anr8vOj8?VWwuAa ziz<(TklLaO&MR;&f=@9qJQB@6+|=pnO}It7)p%xrP+%pfeG>Uc**Do5!|YaozH->hZU6rea+rj{b$wyY@le}Fd6G>m zr+$%Jw;jM7Gp34JwqQD+e_AP_2$i!0`N&-%~<||9fEk`{Ub0cx#%UxU?a6GK}(A>srwZ;va zH-0Cl%{&xd`$!}??QPjE5#96Rb0+?A)a)(A;g9ka41?3Ui|9JYh$v8_6#Y>}rBv$KQdjuAPU#%7*%V}RVd*=L#JF^E|y~W!ykIH>_ z@Q34V%v z4W%?r>gL&w?$g)$KJS{s2G|^3fR07x=ql?0EculPk(9d1zU<%$d<8MHy!BxMPa)i_ z*TDm3R6B<-I^C%LsSe&14{oH`KL$AxXPws=3XF_#LDq( z^4ya7W~eV&4VN?F0>01)U|tnk<%F~MC!WP@{`6)k^T}iZ-)*hcLD(aEf+#{s*Fjos zR*H6SGCJ3B0SqqCAuQ}0FfHjioazzl%u@3{)v|#DdAFC_E`+Q3XL~rWn92gi-TN(B zfho8<^QgYz9QAj4mdpwzRw{7o-!I2v2sw)vqDlop%6d#o)NuJ}tVWHP*)bcaAD9S( zfF}cOCrzm&hC1p)rkJKx-m$igl;xf!M#UAq*{ikQF2xl#4izIT*4CInS>p*4d!_mf%`p#i?G|Bx?$_U~s_pH@{lVW##qWZPn5QK@5){4h%VWot_?r0UyL?PX|)U9#6*>g<-=k!6u4m;tP{ph z_l_$=Zlh7Ph)QCOug3F*if5S{A*`~TWJwa3Er&@CRijYz88M?6&fYV>%law_`#o&! zZC*2gz~ZZ8ns{1=wc8o-XC>0q#oLI61-ZO5O3bn=+=x0WAT=1eQ%&vlLy7 z3*&yi?y98g1EJxr$p{E%H<=lXPtk421W}}@I)zv66m27ND~VSZT$VWZOK5_yhJ$P> zAW%xXL+ujn8=ZGN;OR}=-DDm)7bzODi!ap6Yzhm0{w$d80EyuG`qseE=OP|zHW9o> zd5<$Uj8%QAV7S`f0Ghk4c}2IYSq`dTiIXm1icte`?k`1MI{oLpv9HNqB<74HQ$U>Q z`g$O2`wM7tciP$1c-do(#md~=o1QDQqnSUWX?#d?<})?do~sLzY`h6(&sduLPv)ZW zgW}ysc0LdX?=kK9ldf7Z#lNg=kc$TBe$7odUG(Z#LM*M24c)r?4?3pMRW5Pf(O1M2 zzIJ!d3F>3`1GbIAEqX_LSdtnyBd+8=3H(4TL3z6k4xTV*udLoYPO}>BAdgGfL8g6> z)2@E?<+}g(29b#cz**3NJL$IrMOXqXIn7Sp%71DTy&aKyybWhNzBmvJij|e0T>I#! zMl2ry5;uW8b6$thW2hulE#JDf&)Pr19(9b{J?0h;AK=}v6r@cLT$u_PHUn0m^+@A( zcHpm$_m97E|9tf7kd>D@6Ur8e0(7jc2!|{P$MV8EPKTss1Xo~JPFyoPnea@zg>g!z zRBDLM!q8Bpy2b(`@hq~+0gA)P1x zx3$sJ`y)`nEuk>S68oTpocqUei8%G}=IN*^42j_&ZPia{l4=xF04$Nog!732;$`Jw z2^AkNgjkLf%@9MCTGd_O2`PLy{b`0Uoa1B6_I!j%E~t!Ghc>?{7+sOKiGk6A{$t=rY2o|n1(t~$<;s;CmPPp6}3p$0Eox@MF)K7+u z9LZNL15r3GmpMaB$tQWc+fbTb2c!^sGtN9zs-g4#@E{Co88#l5`e0nFmQyCUx-P_y zw}TI-(AbY+v;sk{|M>=Bd@(Z^zS#zh%`mTE z*6Zrl$Q;L`Uowp~*vQro_x&SwEi~)@M{N(rr=@PUO-guHY9IU2xkpEtkUS(LBF*|H zelfNtT|CO`8AP_vkSAl*Wht>BpVL^{#05K7QJ>QxqrCC)<^rJq8Sh7TTHq!+jrPd5KtfM7`q7Xq?u z$Z#w@gnN39aRB5-^9ZcJWwNdKRubK4$#r(}nX;`h>>7P)#4O3M;=ArP?OW8sWBt1E zvXDK{%8~o%t1E(gXg5PyM889_?uEj4?4lanP>A!F;H>s{=vvKL1R)`1XJW=K6pmza zlP|_h!J}l+Iy*DZFqQqp7=NoIS5?La4|`twD6&S$4c4dn#R`er}pRj zuK#X}2OcTd<1)ZS@7z)sE%A$&F*W|dqo?>25Z-dR>TGA?32*18ajOUNYNrC?0`D ztwa>t&>WAgLrE%^KaA{7=FvY+AZUTF=fDk_%muNNsg|Fr6>a15P&!Bi=-~G4&S~K? zcQ)z2m?(j~n`yNbYfdzF2n#ChpuH2g)$69CMV(Zak2 zl+A$lUh>zs$AH{GulJNF90_j_VH2_r=BCN&8JLO0K=L!j9@&TsEVSX)y&6I5=jbR$ zBK3(1$2$Yh4MOrt+`H(K>v*d%k^%zI2>ra6RpIYX>Yz4dGN9Dhx@M*PC?W2SRS#RLuCsrO zdNpS9J)#9)a6`Jc5w;oic$p1Cz(vI5j(w6~&38);s32I6D@=td&K=Jo% z4Fm26Ul0`Nv92%A{07L{VeD9Rhp(U?Xdh0o84ew!ad84o$d-vx`$?}hhOI#nrbe22 zDyufu;g_6{9v@FZi!!#L6T?F;?+#d{sk}~ooQHgl^!xA9zukPRo)L~=2X}PCXt)R7 zfD@sIO}P;DJG?|sV{s}Lv&8hRj4o#l`YNyUe%M0D{#qzv&^-f@;LC0;!1;Sg7W~vp z#wc!)5}Q3UHoa}4;_0P~3NDG$Lm7Ag#~DCzzd(UVKL&SIY5(fJIiE)ipcyma$!6k< zfG%MHem{Z0RJ&$-gWDIR39^V8*A0bNx>`*inxCFNa+rMls&9L(-+JU}r8<#Bnmrn%*4mDQ&)!N; zG@JU-&Wqnm+dYY{#L)YLSDV60XAHKR9y5bmT%FXxr|6~ka2@pja4y97J2n$2Wl1}F5chn~ra&ZYilnV>X1MnSqVvyVqKi1iniQP2Y^;@#1QjE>>SWfS@ zdGg*8JFkM`JEc-V#W)`trl+K(>tw)U2@aV>7nHObG;%&n#AQ{C(Fh%U&-u z?uF8`PpJ5GS}CHm7`;p<$)ooGi+;~Ox$jyzHu zLX+#GRRMExSx#0%0Rxjp*q&L=J`BypF~j2-yNDv4d$Nb}Q)#dR0Nnow8ZE0U+vxfj z%LiirR*3)y8yLc2ZpQ%o$lc8YKo(}Utsn1qfuezxw-x!EY~tGdWFcQUng@ZrPnA*% zcXs7(1s~s0Ae!y=*&9y<_-vfZS%~gx3%}~s53V4P|JBD}RzS~#>w95K2q&A~r?kYo zX1M&RMJSUiQe4-B+6iYX=hF?xMSst z#iB*g>flUDftb`OdyHXi3c`=9q;sM6jvt+kfoD?gh-~EDcg(2xcRim)YsE>21D9oA zEM9c@&kui1;0wSCBAr_ncw6)WPFk3D6ZZ5QyA{UFgVQkAWn&*Pv6T%DnF)cO$x7eu zJVWCH4?fcc48xF`H`VQpe{@HpC;uY@g(c}$;Y>uLNUWWZVpqibIlTg#S07t#Q?ND}w4E z*P{ycNn#$5HG~n7L{mse#z`62@8AbBKnng2iqlY5B;=hX!EsFcX6RK0JHw;LISbI* zp$0YD4mx~fF;2KZJv>{Z&|R);2a{^p*e6tD1b$}_6Er6vtQbwVB4O;1;gNxaR> zPyPm}N-8{3wD%Cn%GbY}l3j&<+Vszj{j;_{i*D{lzZhDFepym!1{=HvB{=Ft*<6~9 zSIbP`_l5^{%&5A(;}U(mV79=Y!`%g$%3d{%$%P5*#=+qaVf&@u*t0hdH^G^CNINeG zk!wN~>R)6Ar&+@I#C_o)?Q^)%Hl?ShlH1ag^vMu|B5Etzm0C0CTbHR@r4Q<(hlZ{x z`oYaYnThxqHyXq1j0JHbkJanVOn)2SVKMc+cN`BjhXf3!X$Rq%any}j2UPW0S%@>m zYJjZB=7Qdy*YikUhNu=%{}SqyjCjBn-L;voN%;~Ue2cf15Vgu7xd|eDD+!JJ zBn$&aN0?iw3fP>_j5m^}_DG zgDd-iX-JQa$&C05&_yb+b#FsYk0*$mrlW_5SfNFQn{>V?aA6X<5n$VRzw=IxTA_&E z>jd{t?D|HwlSe^7-EB$#wW5bcCpx9prESr*K(|RtPW#D18V&{%U-}-?%|Q{rHu}I| zn&JI&IDW^j-6td#?nh~XdRHh3*Rjy2s@w*;*N+x?!mVyZA|q`!1qXu)o?3w>0ezaf z>mO}As@9_aJMrcx?l5!wiNgRQKX|}B2P*(3i!N+fLwvc0Z(h2>%VFT%`lS{E-!W-S zRkT$>hRa^QRdL%)vXIKWJ>j%zq)VzP8cvE?3T+KyQ>LEsm4&Q7AI%~@x%A=}{fs`? z?7_>nMybW&Q-rX4QBSGpDxbd4%-B}$7F`aAlJ!O?8R=6!%YV)Ju(k&n|7h-MMs9&> zv-E%N&5~T`3&?}*Ty#_063hQy^|=kEo{2%WAsG!_LSaP-20JldjF3>I)}Ztux&-Yx zHl|W&8=*Vh=Xze=mu3rcrn?wAg`sie`Q;Mf88iavRGIQ|-T2;@#g5gIBBIR(qHRz> z;c=DXlz8s!&k-zETT{3Yiay?js{>!3{QpDIF+HjkcY&x49Kv8&(d`L7nEC>j6y}YY zO~n%2Lnk0hg>`GA zC5_v%4Zm^du^ds_cF z@DxdDOFq^xL{vQ5wrGYcNft7+rU@t1VY|>ft;~>p_{tz&^yR-krJ*cw82mRQZKnlgEeFzeoUDzINV{iG-oh8E{Kuv4PMICe3YK5s#{bB6|D#V#x z>VjPFsq=MmSi|gyO(Vl7plEVLRMfCwYY8tTLsG`yUW%Utif;ybxRwmXVpPa`q4>8OW6lBISY%;ac~mY<*k%PZI@cCga_*U}cv z@6^!+z0`jNkacIZkLz-T2M+PqDp@~jt9Cj0-{B8E9cmYm#i_(`^e&oXaWN_=_Iqm= zXX--i{w{ZOIL34~wzeiJrHBt^Xa~_O$eTRoNtOM zb?}Q4hAMCD`M<-^i#ZZX#!DDfF1Zw@7Z*?viXTJVU!H^kWhkoF9Z;>yr`s}@&fSu%bn zK4e>zrWn;&le_(-!BP**z~(Q%1F+!{=iOi=0- z9vdQ=9b`mBfuOTtTQ4r%LNAFNfehpI3{XdkHv;;B&eg^dl2zF37N*$U`B1x%B(}Kw z>lqS6PrUnI4?T7sJ{?Ncu5#bciBHxLuyg6wnI5W)Na4+1a7|drT3JH)B9kt)2(s6& zVZNEK`3PgjUAIn-(M8wUjE z!0NAJTvw~7_tUbHVdZ24SjR{%AtV7%O5VUPC`%pui`GPCIOxJ4%li2UH*Gck%vwT` zMHEUVBJPEUSI-3kING9(K*^?BT)Kkv><_w zj3J_#0T_H{YNFUhvr{f(4ir*FQ-i=~sXk0O#T0xL>?nRr>Bsz21soQmt;wsR}G5yZsiZ ztaL~Nf6jymsLm1H5L;a!ypFvsNt--t%7%0)@N2NkW#%&5-`52bBM;g#-*vVXl|dkc z%+27o+V^wP(xgiFgPr_xhhvG-(5H*8Q@;UR$)(tvNXN?*bl!Dg>O ze;8=vs=Nb60XJrR(|gV1O@je=Vuc}-0ELjdp^6K5`(Vhwwpl`g5Lw>aE@l?X}Yd=>%<#(q{xc+J3s_T1h-F zY*S(LSPra0lCm+gTw3Bb%$I_KE1ux*#h4UZ(n~GKimX+kJ9_6KuiaD_$JEhDG5p(Y zc})JzGpofNnQ=5rL3AVO`d6MyqN9o(O4y#cFM9kb-g*l>eM<|Dra-wXp;3l(IZrsZu$AtLP6goJftL3I){I)}e8 z=Tv{X;-GGG^lrJO54!zE2%DTHMB>GrJ&1-gQW0`zz4f=Xm;5f#2l zt8FRH>?J}L;OYXBdM+I7WT5c}U)g*$50Y=`KgO>b`UVMeTUl&DQ_7l++MECN7n_pv zHfCi$A4z2pEYF!0hSF+KnIk-pd+W`FO@^wuscz{2W2csqZ$OmhimO?L?h=tbbYKP7 z`NsJP>>}r=PTp)4!Ib!HST7@}(-s8B-wA`!M~-@)rXgRllU(+jWtV&6g+Wl^QB%u3tN5^c=GOQNn&sF?9?2 zof|*)6-ZKG=uZusF$ga}e{nr|oyWRIx{<6)t!OJ@%aT6qle>V#den+svy$kkZ3 zp=-qy+(cLe#Lz1nM*g&br#GU^zjvgYUIWQ&JM0>H^IZ==KIQ+?Y>Fn!DGQ}pr6hTq zBB-XD4aWSzgR)+z6;7nu>&GJjG>N10o14S(T>1^;W^>0$CvyIvfrXOse!ygUnI4%q zb$+tlh9+P}du=ubp3p0oLaeYShbxIzcJS;5hir!@@>8f2zZULr^}%8EU|5b&I-ia7 zm81Tygl~eovwJNWvkFL8oNJJAI4)a zLpmz}VIBZvD3$?a1@5X=teMVR8~93jEl((x2&f=GYgX8#L9eIP@_j(KdHz(UzOcW^ z^beDG6*=P6h?`WI7T(Q(Com;cgfQcFQ;7^Wht!CFMfP9L6JqQ)+BmDi?T*KsU&sd1x(KiWKl<|Xg_ZJbqKCj8NyJE%ldx~|GNJ$vxC=+mo$BF;AHDn~02*<2~ z+Dc_n7Myn>>&`FBbo7*9^G01Q)QtKIl|6*MlVQp!C__~`0~J)Sd>RVsKP;NP z>m*e`+Z0h(H*2^)UHh26I#jQ4$Gah5j`QP#N&jY9j#^3dkvogcqSxQPwD7$=! z#Rw!bDyR#RpWw%(bwY@C5=?p&Ya2_UTD#TzoCDVEf|O_|Ke%d#qeQ8y$s3aIi!~hP=W3)O2^K9|CavODlYd0 zPWcuXV_&j0ZvVQq@DggNEatn6=2*Yz_~Ct5Rb@>eYbex6MI}R;&fu>{KIml$!*>C+ zuMkv;O8UB;eG8qLLr&BuI>-sDq6#5qTJa|Wg)di|NHLL5;W`Rf-b3wwTG(DYniLy8 z>pjM_MHCcn23_AVL-muVj@_lvmgo}RL)6Zfo)g4RpfLCV}$5?Wc>+v2K=6rS(qIcKv2fbDiU!9n? ztTQ6@QHW!BtB5#0PC!<}Ew^czEUJAi2%ga{x_gc?r0l)!8M3`gpKrJ77@?LXSA3c_ zipNd&Blc&IL?g$G04Fe#&Fi&Z10*Xowp`QB`lf;Q7zd))4qwHI{J&%y=}OB0`Nph+ z@Sg%b7sdHgC!dE(T+$WcJSG%Tu+YcZGD7Y`RuQZM6>u!$v}%e@tumpydo zni&v)t$S>mtBv?T;f!S;=a>zs_8r^!vzl?fH47z9NI7@CEXR|-;_C20wD)Y$M5wuuS*+4+aE#HOuvVFuo$D-Rd#R+VAUuy7lW0IZAhqk5x?)=_vgt;k6~lAyRc!X}5hJsm(lA;($&QO9AczCX9bx~YV<8nWxRmd~7C zS4`qcB@7*Tjti5(5v(n-uoJ1vyX^uk`;7J`f1A0{G6->gJOI@_12kE$r?7{gnkc7I z5bZ*F8lqj6Wm9I)J(<@N3(_!y0R-VT>!|4cRO875`}5qY;Z7pQingQ1_~VC4?BV5^ zy0JI$1YFLOl6N40WZ+~dYrN;jX3}UrTa{s!PuoVMkx4KVabv3@a{$!M`O+bt+2k8i zrZUimWRw+)r4yduEKf-bf)^Dc<>XF081u)lw0%inSbUH%1x1WG|EI=tz-7vXWFhi6 zsoF8g|By>&c*1>8y9aJ}h87W*>jx;{0Bfz!?hA{zcFcV&ckta$W7oDJ7QE!-&L^16! zdakOnQ%x!qY1URG+zsplYr+bO@c|`Yt_V8oafXjTLvHz}y7-J~*3R)4u;{-Ay?ksRifGE*k4#91ZnFOkO{|vY^DB5vy8G;D>RJ0Cv$3 zG>HBVt~)F}&owb!hR@?|T%2dl)Y~3^fwZo`ScfUPo;Hw$NJ!zf8#n03L$-`I59^J0 z*M=D^v27BpT{?kIeV)6GCIi2U9dd|c0~~i!G~niHRn8v`=Y7Qz1bzss3*K>I;mv`= zCnpE|4=t{IURH>k7Uy!=S$8TY)d0uL>0Q2g`{EuA;0ydfUaQnKa3o-C3G^fk?%yK=o5m8dE1oP_HNve2_o5W5%6^y@$8LwGa zl%`Z)HkWCPiT>x|#f3`Ou^!Kr;9W*oJRi%PdbnjfAAQ-biy`TFabQS+{izbB%Dz<%6RitPo%-5T~2C`A~tXWhh#ww3zTzO+< zRAOfkyr}6}0VvcZ^GGx}h1>mQzs)!MUlWP2dI5cWk}ZzDVSuj~ew<|ZEs+fZ^7#vX zP}!VIBPkLpb5AWu&9_<7`R1<_)Nj*&;AsxkRD498I>6b1B(CutB#?qSp|TeGFw)he zQ&VqRG-bKKJZ4y_j%g_WMGkh(Z75t+4xx`jD`serIr51aQ=1=y`DyZX`(GGOHv7h? zKy^Ra$>&UbNg#;dxZtg=_8%=CbG`1%?@#mtwg9fNJq(gO4lZ12XkKHGg!C$CP#2pT zF`Ww40KGBmHv`l=0)>G!8kijXcy4M;jW_v6A-q}4NQIiIJL0X+a+ZN7iFR|lU9fYL ztlV*^amu1bZRHdDu6HZv7^4R~!OI`~9@)=~L&U9zVpiVkAmPoAOkY(*vgySRrGIn< zoi)KF_7nueyMXTFaUxI7pOF^y>>L+@lfAy`PN`nwwG>lt2#F~87hafZ@7Qtqj%+?F zJDd_!;k)Zdv*_q=8a5)wV6a*&v$CiGnhAu%qHRQZba-ur*Low;C43XfOYvhrLXSH# zjqkyIwEUhI0KbNkl3xtnfh162g886^wnm}NKcBL8v%Uq$4u=B;X;uTwVNfg{o|(sf zec#OQKn8qPo6@|s#?bbz^n=+D>1M0uOugO-7YSPmak1*$Y=$cYtdxfF=N`NEB zzZY;`$&!W%xKxO;i5T|MwBYNb6SHIzVASRD-dkA!AUW5+L~1$&Oag0OFPIX09={}f z^M{|g-myd=ql5!|J7DexdT6k^%EXf9f`DZ|h~~IUgbR^6eQ!e_PYYj*;0p#IScKhD z$gKrik_Z^0OCwghq-`n?@65WZ))H~M4NsT}ach`ygST`U^Bwa-gX^;0W^fZ-akn)U z0b6>PkbBY%UFl$0%p{{J`yv><{-SB|bxwB}n?#6GD|Xz}QpN=6E7aHpv6<)$`Ljio z^f`!z-FSxsVzNKcQ7!ly0^mb(k#c`CcktTdSla%355736gt_~jM`)fTA^tf~F<=`& z>#l&F%KC1DzS$^;z0OF9MhH>48tS|VedUo*8e=wyy&zvaZ`YB$_%Zijd%S zt96w9sx+OrsPaox2M0pZr- zY0F=Wc5zX4eT18OqoU{z5<2RO1G<*AD~&cP!Ir>M_@mTA93!@Pm&Tlw7nxe|aN$J;Mx0K3LXm>28B8HjA6?@oT-;H8 z$=NmyThrpWkbCZ}{#*Lx5_zr(fz`-KYV4n6e#SNo2#}+5_=Vt)@crsGUeI?*IDQ#2rK;cBVzUGeK{cm*9M{(%1c8BD+QLGYf83%y<{qW_?{?*fq$LWJ zxUY%13 z`WuVrex~|-&_LNb-(=nKe$ibLBjz=0Yf*iSX^Q-Mf$n2b949`lnZ3GNFgx+|yY!jY5)T%Il`g*W5dNeneyBZ;=Ba`~CVsFN7x_=~5t z`~L(1@JRdHT|iuUpS2Z44F(c3s29ALw098SnZ&)YSsxh_hDY{lEguDEo`*SI>H(uF z>Q?)SFYZrmq&c8n6f7aQ5RiCd4labhIN!-F4@-XY0p0YON)g28JV;?w$38QpFg!zK z2doG{?G1tzsRr-Y9~t9|;6-twvrccxUJdXJ&h=^Unm6txH(`OC4rfIo6^?j@PeKU? z*Hoz^+0QC9@h5o)CZ#ze?FNf0xC^xj)0;b7q_n%w$P}BQ8HV#53*?-4UeapIybeon z&YZwa%EAKhzzmM3sGdmX)0r;jT0IA4N_{Z1`Svvlg<}C*k4tb(V^my7FZ7EWS~&FD;QxIv}l|(ATg1mW@@$EeSV_eR2Ea5iULa{Z4sviKOj#z1`g}BQd(E9Dfxw zO^mCybacD20_o#W^Wz`vR%DorqiyrK;dfXXHOOPjFxys~^YlMHuBNhMeao#Hul)U_ zoIttUN^FkWj&k3HnA8iM_9%9WuMMn{3fFGoGjIuKUY-e9d6k?71)} zhIgAy|Hy8ZC;DveW~>$QUa+o-d~{u%0}TpuUu4E&TN_Dpufyc*RHSqKsJ0F zI$zxHVrk`%a;M>QubB!vKpgFmQRgf#FFKeWQSMPM1iELYOj;}}4p@b6KrjZGC`h@^Pv$##iV&ej`k*m)gd zwc&O~j@`WdQ8rx5q5Zpl*>}&>GaJ{P%G}ZZL_k;OFX;kB) zit)qre5>%nndobmz|06%{|XyV`h_e8O|7KWnV|(?;0S5v30xq5)`-I{AUsTcn!Mmr zqa&a}xAIHjoVC}_fNiAnDrF-999Yh&u{Ri9A7vj2L_d!Oi9`Oo1pYgy_4b%aV{s&& zOX38j$%bL$NiURoy4T6mur?f zLOT3Li|X!4UHQwfPCeSLhQqup!Z*-21xN5-MGgs0@$1Ai7V^I;2Wj|=b#M3N7-(BH z|1R_-3t3giNx_C3=`)bO^V z$8MgiwWi8Z;g7vWMHZWmv89)OIpoV)#|6ox4?c(C`cf#n-XB?cFcYtvJKM*WTVs$2 zA%jv30CV{Ormo(8V8iG#41ov$I}P)t0@e-qq z4AP=?9s0u;7i)@&4AW4 zK7Zat^hlsa5)H(8kgUR-;Cc`Fx)<_|rZaYzXM(#9&B31URf(6C+dN~DqPwcv0wQzw zsy~M!)?o|$u^dJy18s55v!(!&40Z1Vx8cWMUSD=>nDwtyv32k-LRjfbTW{+R9le6a znopsBp^6%-zr{dRA1rGC0ysVu-B7tu*6ch-Z@^dE19j8aonJ?_x0 zU!d{LG^Ud^y-o~LK&dY2t1D2u6kM?1M`JwPn1*tN2Gi#xrCp-jQitSz%O=}Q)sSMn z0Cm!KZ3lRE9*8-iCdlEOamO%Zoo9CLFl8712YRyp)xX=r%9gCCGNV!wG!E>FQ%<$QgofUO1&=bYq?OulYr3TYtQYkC%m;vL9uqH=$i-KpG6Cq_5KA!`^7 zE|v#Wq~H(~bpp_(apw^cV_qL#fU-`GvumBnZnq+C9%JH^82mMx`YZ8iOv&afGX8Ml zO|Z?nAa?+=ig+f6fZ|gUwJy9|K%|?^^wl726hx?0M*{&>TS0MCdu3XJF4fuO=EF7Tp z5C-JjjPM(OOzY%=^KH=3V@E5SFJ4nkc0{-{U9MdT*JG`}Ejhk1(kGV#$fOS-1GptG z)HCLa4BW(G1fwb%#6Wqs9D_OV6Ts_9qm*zrD)PqMc=S&~@8}qGj$pe0x)T^1TpEh2 zz2qIaq6nKsO`wl3Ir%gZXr$T~ZQ+BhVYPj%abkZ*;ol>X=Mooyb2%26*@Z}2>?XX0$%e8g6;KSG)MX63&1P8wKoj&IVe(U*dc5AZaH z$mhNP^RgNK4B~O0-5^tyVrSYtnkQp|-Wb+;amA>2FE8YzD~uN1ohocrl}FLxw216k&R4nj7?{MM*_e-Fiu-<*%_ zOuWTVOzI{AsZ$WC^8OZ3D(%Ju!+F|w2Qdepu5-*zg=6RMUQJS83AqPUkqWB`?sB4{ zIa~hhS|q~y<$KjZH0e5nVqLX*Dw=csSD)-FNQ zP4*g`>EH@-L328?*O(4Xq*}_Grn0AvuwAX|i+-aTQfZ_eMMOc}S>ujH4L$84NJv35 zVbrO{Gp;UJ-`ne}2tp#+TB!!Ii;Sndr@~nf&@hk9WVz;t1OKvlgQ3^aGv(?5_Bo%U z*;TpvX{Xi94H?NFH%M8SqSKDri!eY>Az4-v4H(d0!2(f3GjYLTQjrEYVk~M7TIVu6 zh=5q_vt--#KVJPDH}r<{^F~#Zfk0eGC08 z>9e!2HtWK^7LACwxBs>ZQ=FI=y9GXT`=2Xppffq%JmKl12?C_g2Q1eylX$2&ON#~~ zb|-9jxdrE-6U;{dn}H}~eIP?IhCBd-^nZuS-blReYE~e(3)Y9S3=WjAz@?cB;x#hE zg(g~mJsh=yo%iB1wTkgq?AtlJ0P8WItpOxpo(%)4+WQ~neLijj=gT{+zX$|;S2)j0 zD1fk^WskkZNGztB|4YWs=s|qKQH;{sEJ7b5~*P)Fs&h4__gZa=ka>|DafqF`Oy9GpxMSv#2pY?+$nf7tJy_ ziPfCX?1MLCTi)x5-oHTt^n9x?>REDc0WXuL7qrqo96BGoqnArNzN`=>tQAn6JqZN@ zWZNttJtvp658|dAUwy#@`*p|gD3EcC-fC6{hso350r~aIWS-FMelUo#!t@ndks?50 zo~D|9xU4i$K7gtR_NP+iX9IEO*88!fTZ`?LDIF1}-9EHFJEZntj8 zjzKi6f+MyOAwOPx)I>i&oka(qydF9c{NYwBkoHu}Z&&lhyj<-dq|17?pQUJORV{0e zj9i491NgI51AhE)V~GYXB`1zC%}ukk0S4MjbHdTb`s5Oa7`t)oynZZFp}jvvOXkQg1n!#}Mawy_+Q?64Q=tMZlXabmWbH+5AsF`{ zy|h`DTh9SWqs_9^ZwhXVvfqy!n%aQZS)zKY+WOL&ueX`6UJiPU=2f;ryOH?oEMH2O z`G8w@bSdarX&EIQQ!H+|>XiZ`u%E}@>75t0uu8J4M_i0d<7PHhZSOg}4D~bWLbJp8f970(=J&hlJFibh1rC#E!_|8eK5Lj+Yc}$%%BrU>>#v?XOtf9B5Yxj z`S!gKPMJmTwuWl57t+bk6QkbVC{C6$gYag61 zp_YX6CT1#ob3}ozv~I?&6@$pHE@fH+I=?-1Op=|L<}yrCJ@Nm2#7SOP-0@TRRZ#Ry z!$2Ax@Pv!(--AuI9Fax`Z4YCrbb0i*$)+NVGTv)pnm!-yR@D z6SqL3(g0rz*7CKl{7IptUo8Ab@jYRkwhjoWUmVTrXHj3~12DkX6KR%ZnEMMenU4^s zHN@5ImaY1RKpovprQOkaB~zfS4Dow^6AR^(X^l&xMx#=CA^^pu6%EL z^hjGI;z#a#BhgLhTi6BHk$NTM1)g{>w_IPG%brb>{9<;;lXtv= zWQEd;oVK07*%M&1+;xjZqFo6rwIPe0l2c34ixn`^MC;c3jZ!>s=@_GnvEguCET_oP zn|NI{{pqz!=3fLk3pqvaw%opPjA@UGaT)#sP@R% zhM|6?+^#mh#7Kgma@mF&bV?BeVC|AzUB&+c)7#k%W3MJ37Q{k-on7MX-2Nvi|e zlH@E~aUhv9%PP<${NKWk5>$btRrG0;=oYPROR_mS*=}YPqPGI8B=;k72rK2aqsRt~ zP;75W6vX4#%2FG5P8~-N7V@V34lv}ZsJTGm{sJf9{$15 z@{vCMKZtsLdedaE{%GBkkQNAVhn_^n`-q3%Mdf=dAHv1&AGMVFVRer4Xm3#6tA_APP?Az_w*srQLiS!~k`7C|fQa?yWXbTCobm@C64EG;p-%w?sJ;fH(>)eT|&sAtbX?^aI=_ zjjnn=XUkCfcM#BILHY+AYoz$=T-f|rL)J0FE&Fcb=u|L13FIOaB&4^RxqHN_5vj08 zFfJfBWSi?zOA(U+|C)0&X+Hst+}=>VQ+5KjhZ7y=!vwmW&-=x`ri&7?J%VBA6*g?* z)`}ylD-p7rG=Jjhx!$@5f#8ul+AaA(YY}L2vO~`3cPU~6f-}!@j9)w0N}E@#%#=u& z1c9h6u&LdEoeK`>srIIyV>@^=^?kvPNr4hMiHUhM=6kMya8`H1kN{GQ8!Imd1^M=N zqDI2#7x#f!JV5AvLwf)K`9bT!y+%wBx9m&Mqf`z&he|Wsnt&E?b{G2+*?2%1bV=Pp ztSCvBiFCp6%h|bIONn3ebO0-``9{~+6C*6yALz#j5pCbs@lp|Kx)&+{+*VAJ=AV$l zvh?fQwKCC(Z8k=<@w?z{Cal(t$hig*Kfq+AcPN(2olUDIYvyvjuG$z7u;ynUhMrfK zPU=o-Sn#^DQJjY{uZIaH`(HuQZ>E>JCwb4;7DD8 zHKs~aU7}>@f@fj{HA#oi&-+Z=H>w#ID^vmOTcI#YtpSHz*T{b!HG}n&wQyj^B2(&B z+otFP68wZt-dh|wZU5g0O0|GZJ0OhoDipVcDUnEHyLrNdV6WQ9~7m9B^*K%y?JAf5q?(|v(Nf5y2 zJy}{61~H9WJEBsxG|v?v{rx1(8_ei%ZeEt_UI?)HXz(>y`=6^b5-Jui$kp%P2i&Z* zpV!&x^sxoa9Zx-+R1#qMds(P{?>u0QJ_yVguy8Btvib>V6YQct*G+Iqz z>3Y~05bZXyN2j`DD;1h| zrgcH-evGOW+>W$T1q0>Q5ih`oXAl>#metF~#JJ>gO_wZulI3LG*#9Og%Ha+~x$`cVJIEq&+PLG!>Aj^pOKDAt7A$sojQ6tgX@B!+O=fuk3ZZ?R*K z3xX#cqg_q|SNuQv9?``)1>1HX!=T>9-~Rg!o?I^Sjv!ie?^jN>&`Q?p#FYJRK}Pb& zRKR;mxo89aM_p-=S+88w-JZ-`xIgeX*CVTOHp7PZp9sRNh-XR^yz5aJ^q0p#moY zf{Z3;c-Hx!W@dGh_$Ass=FkXTNSKP%=M|AjIx%+sJXyhPXv{;!Cv5l2sc|5wEuhC~ zsJYvR&g<1liE~nfL0w5$S~iFBuv~0c3j{?Dn|bs;pHX;MGtt%1n+=5(Tf-ptDbGn5 zL8d5wRUCaA<{yGb3h$Y<ZRY}7MC?-ANNsI*L7 z;t?k_wI(gnUysul$ky$7OYHpbiC7P21sn<7m1@PnG`5 z6^my7{pdTF4b20n$gS?9Z0Mq{8zk7{yp3ecTd6cN9Jop&F*`nH&PGz7kgjNS_1~UG zcauEqB+5d9^DF^75u$AuPV@vzx08{Y>3K2^75FY#>7l>t?9~SasK~p|-6?Mu00)oC zIkCC`wP&03QEvvqga1EX!6WzYg>M#d72z?y(Kc_M>sdak2A0S~@qnf$>Fhr&%AW4J zg46tlXp0FO5wr+f?0uezo-eb5a(4u;guM2Ybj+~_r|d+=`};&FKuuU>p3>U7-ksrC z<49{IHQ5Z!wr-rf6Vh_nH8^34TsNp)C}+?riZ}Nv8it%wi}}A1U7it7wD?fNUZl~= zEXImrFTDo$h+B^6;etKtPw;p_-=(x#h`u;+iMg0JmCnV=uIINXcRc@(5RgV-;C{@# z{{TmQ69#$t`X6*VTCvupbhX#AR18Xz=4O@inU>?o#uaZ9d#jr5gyA03 z6ZC>kEksUk#8E0ww;uW-Bf_$G_*Y$L-}KAA_Est^o%=p(%zzR#WNlMjbJ*_sXH|(E z^qGh%zDuL?uo1EnIeQ2%GObVQ+#BIa z7K%)2^;JwA{Jv6Xr^f|-44m%Tbtt8R5}g8LYG<_jT|kglBowKZ&W&k$kwL-o)A)Fm z5FMRRJKYAkkZ6ZNtc08@&fz^d*)A5G>lqWhiaKN(oc=?n~cKo$T0~cSAUbutr-~G z(CNUM+3>IwxjY;3al8G?l-WuEfrPeo!$CSalbzTbsrTTkZWIOQ%6War;fxH=<3lrx zS<=CzgQp4F+Ab)@mu#&p)7@07!#oH`!1V9TV?4_uW23%`*@-RhOq{bV#77H1K5(MO zD{HhFXbS*m4X%V0UsYEkO@h5M7ZBLv-O@3Pzs%W<7%~i5C>8_5RD6nG8OnuN>V2wS zlNQR(E(aZ*u-pb)_w*no48`eV1DGtO>ghK@VIhgvX_?@Kl-#k)yD9mmnUqT8qPjUJ zml4VL$WZpR7)hF|PtMS(cQ?y+i*`Cq8m@z5KnGwXush<^H)LndowGo@Z;|b@fTZHw zb3=T-{kya;l`lfz4#$Y^&aUzPiUiBFQ8N^<-+QN1GT4B9I;PmLs*2v0mwLXyrB?{q zdrCOH<^2M}f zr~?B5SyUK8mU#Q*EMOMCOAIvOOeZf39C$KxB;VUz5{dMWD1fw6Qv`!!B7Jd>WOAUb zj-AQ>8On)Ybop_T@-V`onrROzN=jwJKmf&~CmTV(#X&wJC%MBu^3Y9U?9p7@uYvow z>JTbOBO%MNKvardTf-kqeE(1yQod)YKZ*{Yh9K}kOK?hL7t&@Mons-4*-eXN8_o-cy z;m_Bm2upy+gK4Ghi!sh_(Q6W898T$vW|@-4x*dP2iZGmhiv*aCIoRSChXjt6gIt=6 zh=pW;q$|KRuwPQ)UMRbzzM2WpT6W7Ir)C*z8*0W2@kyw%ZVQg6iGa(ZO|I{e2)A6rm1Z;!W(A^}}d=-2I%JFM0V88|j$8Vwr>vXxaec)OiY-5BtZW+``$oxif zmAE$gI$Cc?^T-*BfPn%=p#ia{r}-B2_n@x>I5J^{?+UZ^raV^KFvGpV+T1#%Sbf)O z5tnSB)DUe>YZuv(SF^@ZV#;YW@+^KImrkU_2lkr(jBW+ykU{4Q*W>FuWk(Kftr@ z4rG=;wiwK(LjfyB#=4vWYEEhc;~yy{CJQr!b>dfIh`68eYuQ!L z5d2PJ%n)1(LN=TdPsXeD<6MJNX=U5zT&D!2Q0!KJ)LL^d5l8P4XvzSidsIgsDb$kmhr2vK*bp2X8^Zpo)u(lr+e6nN=VLro zx+TUUrr2MwID^{xoP_Nsm9Ad+S4C|cPm%F#=6TnEw+K+LGmitt1SdFq9Xtby*PSf) z7?x!02Re9G&9lpbhjRHuAd)vQ`Rd&H?(5+pohSXwI!>d%muuVQE5S{E&dR9P`NA3J zU*}wT9K2Tli75QP+}HJvAE3N(Hsyl~80pm{A22%=rEAk)_(?0mUVuJE$uP8toB-2` z#C;68;|q2#db9mJ!xXL;pVwj*A~T4=R|{t%31v{pLD9q<-SOyf!Vx}*L3c zM9~+MwJyd)7~SQ#7HY?=M=8<0c8N5vLgpm(8wVa+6=c5rm;YY z-STzq2AzW_OV*7>z{R&F3nu6}c)))M^0mq%kdW~AxPF54a=ZLEkqTnJA(G5S99SX6 zpqPHusP|7}(4KKT4JwS#Qm|RljC%p|J!D?FQ|^rx6)>w8ROQ`Ab^V z`LI4QY0(ujNUESlxBj^#%HVelQ5}HJQCH2dKZ>OX%;zi-gk2+!QQOR*0s;f5jtYq~ zJzz?y3+c0$pS5DS``gYc`KfuJcIvvI@Gh}uspQ_Dz1pep?XF4}Y3ovk78i%)UvAUz zQ`f#Bgq;0@fu$4+oX#tL+j@_J3W^Cv*C?$B@Qn4uGP>BPL?EO@sd zD#iIUlNu!)r76!1s2qSzTOp3!DH*xUe;#xxqLyX{@Rd2Iv4F0W$PsSxQ&--t;u8VQ z;(~{j)tvB*<*PgHuLc+H-gH1STe+2&9mZFtE9vAD&ps4q0B3a^7!iJSbG5AyQMewB z?7~)_4sx8JEb}~T*M5^L%~}VSuhyr&>gHJvj8G0)`w3a(#RAd5e)w#ip=nth!mz3e zAe3gSU&NMDoz5lVWUAu?tX9Inet}Hp#?r*oOO~+oasH7-VDDmoGKGu0i_hX$Q5(4j z*>4eppNxkA3ZaGyK^CvOxslKCC+yQWMT5DHJ%4RvN3se+=kxou{>CKu7>r|8xysRV zd8Svev8p)zM%V$w8>cr->q&InrmKVQvbBTOhoPP4p2}6(N*7w;DB^e(9;3Ag`Uq}2 zK5ecPkX|#rzt(^1n-#chjCyM~?!-2@D=VOBz}gdNpSPLw_SA-{82RL4qEDl~S7KNn zKn!Myd(=CSViyA3Rl9RgY~^sH=rZ`VrFF{p5xQ{?>cfz|E(>5 zXVS+-{cQ#e%!~ui$(o>(YrfZiH@D%te>I2%edE0ZDqWXhQvQry5`afSx5z#v*3f#g z&z_EMPA@K`p|MgH_}wpIq;B3fF}^SL_5#eQ8?Y!gd}IASv2OMGfBA@@hm}F!HQUMz z_pveomH_1ZIb|Y*2eyBncuRMUqp+w&v?r->Sa6#(bcr}Jc>waQPLqpM)Cd!a(}F~r z0I-^?sGrQ##v|TJ;xv-pF%LzcjhaZg@`l4@eE(_AW@hUA*4Tf%Io{8AX=sQ=#SF6a zew?o`z44U_J#n~nINuczpBtCt65_1Av_DsE2qsB7iB@7UOviz*H0MTtCRpI%zF=G~`x*y25Z!tI7mUJ5 zK?P$Ct)HIUu!tjFKA=ctYKC_ee-J(kB;R`{`@-*3;Hru0j-J*%38>%{Ql)%?tAv~w0tTtK+ zt21^_Y%|lSbU$gyyQAx|>eN!8kP5WJV1~)M{X?KAtdx91`SkOi(VVAXW~XILPitlA&fY<+Og(;URjXaJ1QszeQ0)T zOm16UiA$`w8#M8ITcht=D@%n9A7!srik`@#Lp-4zzf;@7E91a%Q6(v!0(2GjdHILL z+;xT_df`qKqPNSHw>(SYa{9t4 z!H$xp$jQH;-snY{+9OJUJO^y|KHHR%Rjmx)w<+-?4HSKRz8#u{_$sh`6FN5n^56+@@+NHVjK^rfp~%)k#Ntt7ANAxb{q#M!3BrI{du42&V;~ zW#-I?{8QD%3;T(M)GU@gv8kq25loZwD}K8>HptbT;bKrPkjyEgS#+Hu&9lA^TDxy% zH-zZ|JS3_|Rnq<8F66ZA$>PherZzn{twM5{1%8Xtm;M3rN$%qSbPS1-ITmmDYt~ zyw=8IfNFVj?2vgWYh!!cW8@*gLI*=L6*xMKnwbx2eFyxBkE`S%vXuT zMIkghqUDp=&?3h!rkL*Ue&N2=syJ*C zPC#oujQ7TU)Z}M|)AKnoe5eJeKDY4|^b(6D^2EbQI!lTI&3Tq`fJlalxzcAjFqHS}666jk;F!9-`AiR;&GCHzEMzywH*rlh z9nz|wK>4AEWyAf7&M$Q(fDhmLn#ZR8^ARRYTsnT}Yhbs9-zkV5Zem}0TJ8ZrMr~4y zi{7M$Y*lTaAkIV#u9?7KyET8v+->UNvFiJ1USU+@@~HoC9%$IWxc?6{>*`njv3xhk zw$r`a2qIxFx|rG`p^TwvV24)K1u&r&bzZFNPu7+d`<$=cRRAZCES?Zml^mJe0w$6w z7Z}P$2(p5#08d)URfhBA#!y_V2gaEf(*Mx9`F|Z&&ejO4=A}l>d=l5my`05{t~XEc z)+laAhM=5AiV=O2a1*6h1gg*zt|GNM>-XaPto&sV`s|~MhzuIm5gl+lp>bhrjYzNM z&5V$??;QLYMI8OU@Fn`c2TE2bN3>_AzW~8xYsI^i7B+qI`83}epSCc$wpfcFFUqrt z5|gB9z0Yu-zKNGkNa!J6E{RhJ9ALmOUIEBDep!f;d|Jub!3vxUI*rwWf#D^~z9dHt zpo&c#lq%BIh_oKBl*<1$f=ieneaf>?Y#Wu>^c^kMa$qq4=2rwioYa7>I8qQMNyX}Y z-(*2fdRN9vXu2Y3*<)v}oHcaIWK>cu&{yfDU2cbG!OUJALrT2*<&_JI8^Sxkx1e!* zG=Ga%mUHU-OYpca%tgFCx+eq(6Xr8XHyQ2Ad?GnGi1^}RY&=*DE_@UL#gJX`e=_tY zkIxHwEe;)dJ7(j&1rcp7P6-Y*ldwQ6pyW!^LnmYwkf56)rs6h!ZFySzON^b?4>R~Sr^;BG7B%K&&=md4COi

`50T+(!R{qV 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 Or`H9xQmpg!A<=*u-tgZ5 diff --git a/smoketests/requirements.txt b/smoketests/requirements.txt deleted file mode 100644 index b942b507ceb2..000000000000 --- a/smoketests/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -python-gflags diff --git a/smoketests/run_tests.py b/smoketests/run_tests.py deleted file mode 100644 index e8caff4c77d1..000000000000 --- a/smoketests/run_tests.py +++ /dev/null @@ -1,316 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Colorizer Code is borrowed from Twisted: -# Copyright (c) 2001-2010 Twisted Matrix Laboratories. -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -"""Unittest runner for Nova. - -To run all tests - python run_tests.py - -To run a single test: - python run_tests.py test_compute:ComputeTestCase.test_run_terminate - -To run a single test module: - python run_tests.py test_compute - - or - - python run_tests.py api.test_wsgi - -""" - -from __future__ import print_function - -import os -import sys -import unittest - -# If ../nova/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): - sys.path.insert(0, possible_topdir) - - -from nose import config -from nose import core -from nose import result - -from smoketests import flags - -from nova.openstack.common.gettextutils import _ - -FLAGS = flags.FLAGS - - -class _AnsiColorizer(object): - """ - A colorizer is an object that loosely wraps around a stream, allowing - callers to write text to the stream in a particular color. - - Colorizer classes must implement C{supported()} and C{write(text, color)}. - """ - _colors = dict(black=30, red=31, green=32, yellow=33, - blue=34, magenta=35, cyan=36, white=37) - - def __init__(self, stream): - self.stream = stream - - def supported(cls, stream=sys.stdout): - """ - A class method that returns True if the current platform supports - coloring terminal output using this method. Returns False otherwise. - """ - if not stream.isatty(): - return False # auto color only on TTYs - try: - import curses - except ImportError: - return False - else: - try: - try: - return curses.tigetnum("colors") > 2 - except curses.error: - curses.setupterm() - return curses.tigetnum("colors") > 2 - except Exception: - raise - # guess false in case of error - return False - supported = classmethod(supported) - - def write(self, text, color): - """ - Write the given text to the stream in the given color. - - @param text: Text to be written to the stream. - - @param color: A string label for a color. e.g. 'red', 'white'. - """ - color = self._colors[color] - self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text)) - - -class _Win32Colorizer(object): - """ - See _AnsiColorizer docstring. - """ - def __init__(self, stream): - from win32console import FOREGROUND_BLUE - from win32console import FOREGROUND_GREEN - from win32console import FOREGROUND_INTENSITY - from win32console import FOREGROUND_RED - from win32console import GetStdHandle - from win32console import STD_OUTPUT_HANDLE - - red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN, - FOREGROUND_BLUE, FOREGROUND_INTENSITY) - self.stream = stream - self.screenBuffer = GetStdHandle(STD_OUTPUT_HANDLE) - self._colors = { - 'normal': red | green | blue, - 'red': red | bold, - 'green': green | bold, - 'blue': blue | bold, - 'yellow': red | green | bold, - 'magenta': red | blue | bold, - 'cyan': green | blue | bold, - 'white': red | green | blue | bold - } - - def supported(cls, stream=sys.stdout): - try: - import win32console - screenBuffer = win32console.GetStdHandle( - win32console.STD_OUTPUT_HANDLE) - except ImportError: - return False - import pywintypes - try: - screenBuffer.SetConsoleTextAttribute( - win32console.FOREGROUND_RED | - win32console.FOREGROUND_GREEN | - win32console.FOREGROUND_BLUE) - except pywintypes.error: - return False - else: - return True - supported = classmethod(supported) - - def write(self, text, color): - color = self._colors[color] - self.screenBuffer.SetConsoleTextAttribute(color) - self.stream.write(text) - self.screenBuffer.SetConsoleTextAttribute(self._colors['normal']) - - -class _NullColorizer(object): - """ - See _AnsiColorizer docstring. - """ - def __init__(self, stream): - self.stream = stream - - def supported(cls, stream=sys.stdout): - return True - supported = classmethod(supported) - - def write(self, text, color): - self.stream.write(text) - - -class NovaTestResult(result.TextTestResult): - def __init__(self, *args, **kw): - result.TextTestResult.__init__(self, *args, **kw) - self._last_case = None - self.colorizer = None - # NOTE(vish): reset stdout for the terminal check - stdout = sys.stdout - sys.stdout = sys.__stdout__ - for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]: - if colorizer.supported(): - self.colorizer = colorizer(self.stream) - break - sys.stdout = stdout - - def getDescription(self, test): - return str(test) - - # NOTE(vish): copied from unittest with edit to add color - def addSuccess(self, test): - unittest.TestResult.addSuccess(self, test) - if self.showAll: - self.colorizer.write("OK", 'green') - self.stream.writeln() - elif self.dots: - self.stream.write('.') - self.stream.flush() - - # NOTE(vish): copied from unittest with edit to add color - def addFailure(self, test, err): - unittest.TestResult.addFailure(self, test, err) - if self.showAll: - self.colorizer.write("FAIL", 'red') - self.stream.writeln() - elif self.dots: - self.stream.write('F') - self.stream.flush() - - # NOTE(vish): copied from nose with edit to add color - def addError(self, test, err): - """Overrides normal addError to add support for - errorClasses. If the exception is a registered class, the - error will be added to the list for that class, not errors. - """ - stream = getattr(self, 'stream', None) - ec, ev, tb = err - try: - exc_info = self._exc_info_to_string(err, test) - except TypeError: - # 2.3 compat - exc_info = self._exc_info_to_string(err) - for cls, (storage, label, isfail) in self.errorClasses.items(): - if result.isclass(ec) and issubclass(ec, cls): - if isfail: - test.passed = False - storage.append((test, exc_info)) - # Might get patched into a streamless result - if stream is not None: - if self.showAll: - message = [label] - detail = result._exception_detail(err[1]) - if detail: - message.append(detail) - stream.writeln(": ".join(message)) - elif self.dots: - stream.write(label[:1]) - return - self.errors.append((test, exc_info)) - test.passed = False - if stream is not None: - if self.showAll: - self.colorizer.write("ERROR", 'red') - self.stream.writeln() - elif self.dots: - stream.write('E') - - def startTest(self, test): - unittest.TestResult.startTest(self, test) - current_case = test.test.__class__.__name__ - - if self.showAll: - if current_case != self._last_case: - self.stream.writeln(current_case) - self._last_case = current_case - - self.stream.write( - ' %s' % str(test.test._testMethodName).ljust(60)) - self.stream.flush() - - -class NovaTestRunner(core.TextTestRunner): - def _makeResult(self): - return NovaTestResult(self.stream, - self.descriptions, - self.verbosity, - self.config) - - -if __name__ == '__main__': - if not os.getenv('EC2_ACCESS_KEY'): - print(_('Missing EC2 environment variables. Please ' - 'source the appropriate novarc file before ' - 'running this test.')) - sys.exit(1) - - argv = FLAGS(sys.argv) - testdir = os.path.abspath("./") - c = config.Config(stream=sys.stdout, - env=os.environ, - verbosity=3, - workingDir=testdir, - plugins=core.DefaultPluginManager()) - - runner = NovaTestRunner(stream=c.stream, - verbosity=c.verbosity, - config=c) - sys.exit(not core.run(config=c, testRunner=runner, argv=argv)) diff --git a/smoketests/test_netadmin.py b/smoketests/test_netadmin.py deleted file mode 100644 index d1254234e345..000000000000 --- a/smoketests/test_netadmin.py +++ /dev/null @@ -1,203 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import commands -import os -import random -import sys -import time - -# If ../nova/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): - sys.path.insert(0, possible_topdir) - -from smoketests import base -from smoketests import flags - - -FLAGS = flags.FLAGS - -TEST_PREFIX = 'test%s' % int(random.random() * 1000000) -TEST_BUCKET = '%s_bucket' % TEST_PREFIX -TEST_KEY = '%s_key' % TEST_PREFIX -TEST_GROUP = '%s_group' % TEST_PREFIX - - -class AddressTests(base.UserSmokeTestCase): - def test_000_setUp(self): - self.create_key_pair(self.conn, TEST_KEY) - reservation = self.conn.run_instances(FLAGS.test_image, - instance_type='m1.tiny', - key_name=TEST_KEY) - self.data['instance'] = reservation.instances[0] - if not self.wait_for_running(self.data['instance']): - self.fail('instance failed to start') - self.data['instance'].update() - if not self.wait_for_ping(self.data['instance'].private_ip_address): - self.fail('could not ping instance') - if not self.wait_for_ssh(self.data['instance'].private_ip_address, - TEST_KEY): - self.fail('could not ssh to instance') - - def test_001_can_allocate_floating_ip(self): - result = self.conn.allocate_address() - self.assertTrue(hasattr(result, 'public_ip')) - self.data['public_ip'] = result.public_ip - - def test_002_can_associate_ip_with_instance(self): - result = self.conn.associate_address(self.data['instance'].id, - self.data['public_ip']) - self.assertTrue(result) - - def test_003_can_ssh_with_public_ip(self): - ssh_authorized = False - groups = self.conn.get_all_security_groups(['default']) - for rule in groups[0].rules: - if (rule.ip_protocol == 'tcp' and - int(rule.from_port) <= 22 and - int(rule.to_port) >= 22): - ssh_authorized = True - break - if not ssh_authorized: - self.conn.authorize_security_group('default', - ip_protocol='tcp', - from_port=22, - to_port=22) - try: - if not self.wait_for_ssh(self.data['public_ip'], TEST_KEY): - self.fail('could not ssh to public ip') - finally: - if not ssh_authorized: - self.conn.revoke_security_group('default', - ip_protocol='tcp', - from_port=22, - to_port=22) - - def test_004_can_disassociate_ip_from_instance(self): - result = self.conn.disassociate_address(self.data['public_ip']) - self.assertTrue(result) - - def test_005_can_deallocate_floating_ip(self): - result = self.conn.release_address(self.data['public_ip']) - self.assertTrue(result) - - def test_999_tearDown(self): - self.delete_key_pair(self.conn, TEST_KEY) - self.conn.terminate_instances([self.data['instance'].id]) - - -class SecurityGroupTests(base.UserSmokeTestCase): - - def __get_metadata_item(self, category): - id_url = "latest/meta-data/%s" % category - options = "-f -s --max-time 1" - command = "curl %s %s/%s" % (options, self.data['public_ip'], id_url) - status, output = commands.getstatusoutput(command) - value = output.strip() - if status > 0: - return False - return value - - def __public_instance_is_accessible(self): - instance_id = self.__get_metadata_item('instance-id') - if not instance_id: - return False - if instance_id != self.data['instance'].id: - raise Exception("Wrong instance id. Expected: %s, Got: %s" % - (self.data['instance'].id, instance_id)) - return True - - def test_001_can_create_security_group(self): - self.conn.create_security_group(TEST_GROUP, description='test') - - groups = self.conn.get_all_security_groups() - self.assertTrue(TEST_GROUP in [group.name for group in groups]) - - def test_002_can_launch_instance_in_security_group(self): - with open("proxy.sh") as f: - user_data = f.read() - self.create_key_pair(self.conn, TEST_KEY) - reservation = self.conn.run_instances(FLAGS.test_image, - key_name=TEST_KEY, - security_groups=[TEST_GROUP], - user_data=user_data, - instance_type='m1.tiny') - - self.data['instance'] = reservation.instances[0] - if not self.wait_for_running(self.data['instance']): - self.fail('instance failed to start') - self.data['instance'].update() - - def test_003_can_authorize_security_group_ingress(self): - self.assertTrue(self.conn.authorize_security_group(TEST_GROUP, - ip_protocol='tcp', - from_port=80, - to_port=80)) - - def test_004_can_access_metadata_over_public_ip(self): - result = self.conn.allocate_address() - self.assertTrue(hasattr(result, 'public_ip')) - self.data['public_ip'] = result.public_ip - - result = self.conn.associate_address(self.data['instance'].id, - self.data['public_ip']) - start_time = time.time() - while not self.__public_instance_is_accessible(): - # 1 minute to launch - if time.time() - start_time > 60: - raise Exception("Timeout") - time.sleep(1) - - def test_005_validate_metadata(self): - instance = self.data['instance'] - start_time = time.time() - instance_type = False - while not instance_type: - instance_type = self.__get_metadata_item('instance-type') - # 10 seconds to restart proxy - if time.time() - start_time > 10: - raise Exception("Timeout") - time.sleep(1) - self.assertEqual(instance.instance_type, instance_type) - #FIXME(dprince): validate more metadata here - - def test_006_can_revoke_security_group_ingress(self): - self.assertTrue(self.conn.revoke_security_group(TEST_GROUP, - ip_protocol='tcp', - from_port=80, - to_port=80)) - start_time = time.time() - while self.__public_instance_is_accessible(): - # 1 minute to teardown - if time.time() - start_time > 60: - raise Exception("Timeout") - time.sleep(1) - - def test_999_tearDown(self): - self.conn.disassociate_address(self.data['public_ip']) - self.conn.delete_key_pair(TEST_KEY) - self.conn.terminate_instances([self.data['instance'].id]) - self.wait_for_deleted(self.data['instance']) - self.conn.delete_security_group(TEST_GROUP) - groups = self.conn.get_all_security_groups() - self.assertFalse(TEST_GROUP in [group.name for group in groups]) - self.assertTrue(self.conn.release_address(self.data['public_ip'])) diff --git a/smoketests/test_sysadmin.py b/smoketests/test_sysadmin.py deleted file mode 100644 index e3c58ea31a1a..000000000000 --- a/smoketests/test_sysadmin.py +++ /dev/null @@ -1,337 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import os -import random -import shutil -import sys -import tempfile -import time - -# If ../nova/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): - sys.path.insert(0, possible_topdir) - -from smoketests import base -from smoketests import flags - -FLAGS = flags.FLAGS -flags.DEFINE_string('bundle_kernel', 'random.kernel', - 'Local kernel file to use for bundling tests') -flags.DEFINE_string('bundle_image', 'random.image', - 'Local image file to use for bundling tests') - -TEST_PREFIX = 'test%s' % int(random.random() * 1000000) -TEST_BUCKET = '%s_bucket' % TEST_PREFIX -TEST_KEY = '%s_key' % TEST_PREFIX -TEST_GROUP = '%s_group' % TEST_PREFIX - - -class ImageTests(base.UserSmokeTestCase): - def test_001_can_bundle_image(self): - self.data['tempdir'] = tempfile.mkdtemp() - self.assertTrue(self.bundle_image(FLAGS.bundle_image, - self.data['tempdir'])) - - def test_002_can_upload_image(self): - try: - self.assertTrue(self.upload_image(TEST_BUCKET, - FLAGS.bundle_image, - self.data['tempdir'])) - finally: - if os.path.exists(self.data['tempdir']): - shutil.rmtree(self.data['tempdir']) - - def test_003_can_register_image(self): - image_id = self.conn.register_image('%s/%s.manifest.xml' % - (TEST_BUCKET, FLAGS.bundle_image)) - self.assertTrue(image_id is not None) - self.data['image_id'] = image_id - - def test_004_can_bundle_kernel(self): - self.assertTrue(self.bundle_image(FLAGS.bundle_kernel, kernel=True)) - - def test_005_can_upload_kernel(self): - self.assertTrue(self.upload_image(TEST_BUCKET, FLAGS.bundle_kernel)) - - def test_006_can_register_kernel(self): - kernel_id = self.conn.register_image('%s/%s.manifest.xml' % - (TEST_BUCKET, FLAGS.bundle_kernel)) - self.assertTrue(kernel_id is not None) - self.data['kernel_id'] = kernel_id - - def test_007_images_are_available_within_10_seconds(self): - for i in xrange(10): - image = self.conn.get_image(self.data['image_id']) - if image and image.state == 'available': - break - time.sleep(1) - else: - self.assertTrue(False) # wasn't available within 10 seconds - self.assertTrue(image.type == 'machine') - - for i in xrange(10): - kernel = self.conn.get_image(self.data['kernel_id']) - if kernel and kernel.state == 'available': - break - time.sleep(1) - else: - self.assertTrue(False) # wasn't available within 10 seconds - self.assertTrue(kernel.type == 'kernel') - - def test_008_can_describe_image_attribute(self): - attrs = self.conn.get_image_attribute(self.data['image_id'], - 'launchPermission') - self.assertTrue(attrs.name, 'launch_permission') - - def test_009_can_add_image_launch_permission(self): - image = self.conn.get_image(self.data['image_id']) - self.assertEqual(image.id, self.data['image_id']) - self.assertEqual(image.is_public, False) - self.conn.modify_image_attribute(image_id=self.data['image_id'], - operation='add', - attribute='launchPermission', - groups='all') - image = self.conn.get_image(self.data['image_id']) - self.assertEqual(image.id, self.data['image_id']) - self.assertEqual(image.is_public, True) - - def test_010_can_see_launch_permission(self): - attrs = self.conn.get_image_attribute(self.data['image_id'], - 'launchPermission') - self.assertEqual(attrs.name, 'launch_permission') - self.assertEqual(attrs.attrs['groups'][0], 'all') - - def test_011_can_remove_image_launch_permission(self): - image = self.conn.get_image(self.data['image_id']) - self.assertEqual(image.id, self.data['image_id']) - self.assertEqual(image.is_public, True) - self.conn.modify_image_attribute(image_id=self.data['image_id'], - operation='remove', - attribute='launchPermission', - groups='all') - image = self.conn.get_image(self.data['image_id']) - self.assertEqual(image.id, self.data['image_id']) - self.assertEqual(image.is_public, False) - - def test_012_private_image_shows_in_list(self): - images = self.conn.get_all_images() - image_ids = [image.id for image in images] - self.assertTrue(self.data['image_id'] in image_ids) - - def test_013_user_can_deregister_kernel(self): - self.assertTrue(self.conn.deregister_image(self.data['kernel_id'])) - - def test_014_can_deregister_image(self): - self.assertTrue(self.conn.deregister_image(self.data['image_id'])) - - def test_015_can_delete_bundle(self): - self.assertTrue(self.delete_bundle_bucket(TEST_BUCKET)) - - -class InstanceTests(base.UserSmokeTestCase): - def test_001_can_create_keypair(self): - key = self.create_key_pair(self.conn, TEST_KEY) - self.assertEqual(key.name, TEST_KEY) - - def test_002_can_create_instance_with_keypair(self): - reservation = self.conn.run_instances(FLAGS.test_image, - key_name=TEST_KEY, - instance_type='m1.tiny') - self.assertEqual(len(reservation.instances), 1) - self.data['instance'] = reservation.instances[0] - - def test_003_instance_runs_within_60_seconds(self): - # allow 60 seconds to exit pending with IP - if not self.wait_for_running(self.data['instance']): - self.fail('instance failed to start') - self.data['instance'].update() - ip = self.data['instance'].private_ip_address - self.assertFalse(ip == '0.0.0.0') - if FLAGS.use_ipv6: - ipv6 = self.data['instance'].dns_name_v6 - self.assertFalse(ipv6 is None) - - def test_004_can_ping_private_ip(self): - if not self.wait_for_ping(self.data['instance'].private_ip_address): - self.fail('could not ping instance') - - if FLAGS.use_ipv6: - if not self.wait_for_ping(self.data['instance'].dns_name_v6, - "ping6"): - self.fail('could not ping instance v6') - - def test_005_can_ssh_to_private_ip(self): - if not self.wait_for_ssh(self.data['instance'].private_ip_address, - TEST_KEY): - self.fail('could not ssh to instance') - - if FLAGS.use_ipv6: - if not self.wait_for_ssh(self.data['instance'].dns_name_v6, - TEST_KEY): - self.fail('could not ssh to instance v6') - - def test_999_tearDown(self): - self.delete_key_pair(self.conn, TEST_KEY) - self.conn.terminate_instances([self.data['instance'].id]) - - -class VolumeTests(base.UserSmokeTestCase): - def setUp(self): - super(VolumeTests, self).setUp() - self.device = '/dev/vdb' - - def test_000_setUp(self): - self.create_key_pair(self.conn, TEST_KEY) - reservation = self.conn.run_instances(FLAGS.test_image, - instance_type='m1.tiny', - key_name=TEST_KEY) - self.data['instance'] = reservation.instances[0] - if not self.wait_for_running(self.data['instance']): - self.fail('instance failed to start') - self.data['instance'].update() - if not self.wait_for_ping(self.data['instance'].private_ip_address): - self.fail('could not ping instance') - if not self.wait_for_ssh(self.data['instance'].private_ip_address, - TEST_KEY): - self.fail('could not ssh to instance') - - def test_001_can_create_volume(self): - volume = self.conn.create_volume(1, 'nova') - self.assertEqual(volume.size, 1) - self.data['volume'] = volume - # Give network time to find volume. - time.sleep(5) - - def test_002_can_attach_volume(self): - volume = self.data['volume'] - - for x in xrange(10): - volume.update() - if volume.status.startswith('available'): - break - time.sleep(1) - else: - self.fail('cannot attach volume with state %s' % volume.status) - - # Give volume some time to be ready. - time.sleep(5) - volume.attach(self.data['instance'].id, self.device) - - # wait - for x in xrange(10): - volume.update() - if volume.status.startswith('in-use'): - break - time.sleep(1) - else: - self.fail('volume never got to in use') - - self.assertTrue(volume.status.startswith('in-use')) - - def test_003_can_mount_volume(self): - ip = self.data['instance'].private_ip_address - conn = self.connect_ssh(ip, TEST_KEY) - - # NOTE(dprince): give some time for volume to show up in partitions - stdin, stdout, stderr = conn.exec_command( - 'COUNT="0";' - 'until cat /proc/partitions | grep "%s\\$"; do ' - '[ "$COUNT" -eq "60" ] && exit 1;' - 'COUNT=$(( $COUNT + 1 ));' - 'sleep 1; ' - 'done' - % self.device.rpartition('/')[2]) - out = stdout.read() - if not out.strip().endswith(self.device.rpartition('/')[2]): - self.fail('Timeout waiting for volume partition in instance. %s %s' - % (out, stderr.read())) - - # NOTE(vish): this will create a dev for images that don't have - # udev rules - stdin, stdout, stderr = conn.exec_command( - 'grep %s /proc/partitions | ' - '`awk \'{print "mknod /dev/"\\$4" b "\\$1" "\\$2}\'`' - % self.device.rpartition('/')[2]) - exec_list = [] - exec_list.append('mkdir -p /mnt/vol') - exec_list.append('/sbin/mke2fs %s' % self.device) - exec_list.append('mount %s /mnt/vol' % self.device) - exec_list.append('echo success') - stdin, stdout, stderr = conn.exec_command(' && '.join(exec_list)) - out = stdout.read() - conn.close() - if not out.strip().endswith('success'): - self.fail('Unable to mount: %s %s' % (out, stderr.read())) - - def test_004_can_write_to_volume(self): - ip = self.data['instance'].private_ip_address - conn = self.connect_ssh(ip, TEST_KEY) - # FIXME(devcamcar): This doesn't fail if the volume hasn't been mounted - stdin, stdout, stderr = conn.exec_command( - 'echo hello > /mnt/vol/test.txt') - err = stderr.read() - conn.close() - if len(err) > 0: - self.fail('Unable to write to mount: %s' % (err)) - - def test_005_volume_is_correct_size(self): - ip = self.data['instance'].private_ip_address - conn = self.connect_ssh(ip, TEST_KEY) - stdin, stdout, stderr = conn.exec_command( - "cat /sys/class/block/%s/size" % self.device.rpartition('/')[2]) - out = stdout.read().strip() - conn.close() - # NOTE(vish): 1G bytes / 512 bytes per block - expected_size = 1024 * 1024 * 1024 / 512 - self.assertEqual('%s' % (expected_size,), out, - 'Volume is not the right size: %s %s. Expected: %s' % - (out, stderr.read(), expected_size)) - - def test_006_me_can_umount_volume(self): - ip = self.data['instance'].private_ip_address - conn = self.connect_ssh(ip, TEST_KEY) - stdin, stdout, stderr = conn.exec_command('umount /mnt/vol') - err = stderr.read() - conn.close() - if len(err) > 0: - self.fail('Unable to unmount: %s' % (err)) - - def test_007_me_can_detach_volume(self): - result = self.conn.detach_volume(volume_id=self.data['volume'].id) - self.assertTrue(result) - volume = self.data['volume'] - for x in xrange(10): - volume.update() - if volume.status.startswith('available'): - break - time.sleep(1) - else: - self.fail('cannot detach volume. state %s' % volume.status) - - def test_008_me_can_delete_volume(self): - result = self.conn.delete_volume(self.data['volume'].id) - self.assertTrue(result) - - def test_999_tearDown(self): - self.conn.terminate_instances([self.data['instance'].id]) - self.conn.delete_key_pair(TEST_KEY)