Import all the stacktester stuff as-is (s/stacktester/kong/, though).
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
from kong import exceptions
|
||||
|
||||
import httplib2
|
||||
import os
|
||||
import time
|
||||
|
||||
|
||||
class Client(object):
|
||||
|
||||
USER_AGENT = 'python-nova_test_client'
|
||||
|
||||
def __init__(self, host='localhost', port=80, base_url=''):
|
||||
#TODO: join these more robustly
|
||||
self.base_url = "http://%s:%s/%s" % (host, port, base_url)
|
||||
|
||||
def poll_request(self, method, url, check_response, **kwargs):
|
||||
|
||||
timeout = kwargs.pop('timeout', 180)
|
||||
interval = kwargs.pop('interval', 2)
|
||||
# Start timestamp
|
||||
start_ts = int(time.time())
|
||||
|
||||
while True:
|
||||
resp, body = self.request(method, url, **kwargs)
|
||||
if (check_response(resp, body)):
|
||||
break
|
||||
if (int(time.time()) - start_ts >= timeout):
|
||||
raise exceptions.TimeoutException
|
||||
time.sleep(interval)
|
||||
|
||||
def poll_request_status(self, method, url, status=200, **kwargs):
|
||||
|
||||
def check_response(resp, body):
|
||||
return resp['status'] == str(status)
|
||||
|
||||
self.poll_request(method, url, check_response, **kwargs)
|
||||
|
||||
|
||||
def request(self, method, url, **kwargs):
|
||||
# Default to management_url, but can be overridden here
|
||||
# (for auth requests)
|
||||
base_url = kwargs.get('base_url', self.management_url)
|
||||
|
||||
self.http_obj = httplib2.Http()
|
||||
|
||||
params = {}
|
||||
params['headers'] = {'User-Agent': self.USER_AGENT}
|
||||
params['headers'].update(kwargs.get('headers', {}))
|
||||
if 'Content-Type' not in params.get('headers',{}):
|
||||
params['headers']['Content-Type'] = 'application/json'
|
||||
|
||||
if 'body' in kwargs:
|
||||
params['body'] = kwargs.get('body')
|
||||
|
||||
req_url = os.path.join(base_url, url.strip('/'))
|
||||
resp, body = self.http_obj.request(req_url, method, **params)
|
||||
return resp, body
|
||||
@@ -0,0 +1,79 @@
|
||||
import time
|
||||
import socket
|
||||
import warnings
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
import paramiko
|
||||
|
||||
|
||||
class Client(object):
|
||||
|
||||
def __init__(self, host, username, password, timeout=300):
|
||||
self.host = host
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.timeout = timeout
|
||||
|
||||
def _get_ssh_connection(self):
|
||||
"""Returns an ssh connection to the specified host"""
|
||||
_timeout = True
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(
|
||||
paramiko.AutoAddPolicy())
|
||||
_start_time = time.time()
|
||||
|
||||
while not self._is_timed_out(self.timeout, _start_time):
|
||||
try:
|
||||
ssh.connect(self.host, username=self.username,
|
||||
password=self.password, look_for_keys=False,
|
||||
timeout=self.timeout)
|
||||
_timeout = False
|
||||
break
|
||||
except socket.error:
|
||||
continue
|
||||
except paramiko.AuthenticationException:
|
||||
time.sleep(15)
|
||||
continue
|
||||
if _timeout:
|
||||
raise socket.error("SSH connect timed out")
|
||||
return ssh
|
||||
|
||||
def _is_timed_out(self, timeout, start_time):
|
||||
return (time.time() - timeout) > start_time
|
||||
|
||||
def connect_until_closed(self):
|
||||
"""Connect to the server and wait until connection is lost"""
|
||||
try:
|
||||
ssh = self._get_ssh_connection()
|
||||
_transport = ssh.get_transport()
|
||||
_start_time = time.time()
|
||||
_timed_out = self._is_timed_out(self.timeout, _start_time)
|
||||
while _transport.is_active() and not _timed_out:
|
||||
time.sleep(5)
|
||||
_timed_out = self._is_timed_out(self.timeout, _start_time)
|
||||
ssh.close()
|
||||
except (EOFError, paramiko.AuthenticationException, socket.error):
|
||||
return
|
||||
|
||||
def exec_command(self, cmd):
|
||||
"""Execute the specified command on the server.
|
||||
|
||||
:returns: data read from standard output of the command
|
||||
|
||||
"""
|
||||
ssh = self._get_ssh_connection()
|
||||
stdin, stdout, stderr = ssh.exec_command(cmd)
|
||||
output = stdout.read()
|
||||
ssh.close()
|
||||
return output
|
||||
|
||||
def test_connection_auth(self):
|
||||
""" Returns true if ssh can connect to server"""
|
||||
try:
|
||||
connection = self._get_ssh_connection()
|
||||
connection.close()
|
||||
except paramiko.AuthenticationException:
|
||||
return False
|
||||
|
||||
return True
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
import ConfigParser
|
||||
|
||||
|
||||
class NovaConfig(object):
|
||||
"""Provides configuration information for connecting to Nova."""
|
||||
|
||||
def __init__(self, conf):
|
||||
"""Initialize a Nova-specific configuration object."""
|
||||
self.conf = conf
|
||||
|
||||
def get(self, item_name, default_value):
|
||||
try:
|
||||
return self.conf.get("nova", item_name)
|
||||
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
|
||||
return default_value
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
"""Host for the Nova HTTP API. Defaults to 127.0.0.1."""
|
||||
return self.get("host", "127.0.0.1")
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
"""Port for the Nova HTTP API. Defaults to 8774."""
|
||||
return int(self.get("port", 8774))
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
"""Username to use for Nova API requests. Defaults to 'admin'."""
|
||||
return self.get("user", "admin")
|
||||
|
||||
@property
|
||||
def base_url(self):
|
||||
"""Base of the HTTP API URL. Defaults to '/v1.1'."""
|
||||
return self.get("base_url", "/v1.1")
|
||||
|
||||
@property
|
||||
def project_id(self):
|
||||
"""Base of the HTTP API URL. Defaults to '/v1.1'."""
|
||||
return self.get("project_id", "admin")
|
||||
|
||||
@property
|
||||
def api_key(self):
|
||||
"""API key to use when authenticating. Defaults to 'admin_key'."""
|
||||
return self.get("api_key", "admin_key")
|
||||
|
||||
@property
|
||||
def ssh_timeout(self):
|
||||
"""Timeout in seconds to use when connecting via ssh."""
|
||||
return float(self.get("ssh_timeout", 300))
|
||||
|
||||
@property
|
||||
def build_timeout(self):
|
||||
"""Timeout in seconds to use when connecting via ssh."""
|
||||
return float(self.get("build_timeout", 300))
|
||||
|
||||
|
||||
|
||||
class EnvironmentConfig(object):
|
||||
def __init__(self, conf):
|
||||
"""Initialize a Environment-specific configuration object."""
|
||||
self.conf = conf
|
||||
|
||||
def get(self, item_name, default_value):
|
||||
try:
|
||||
return self.conf.get("environment", item_name)
|
||||
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
|
||||
return default_value
|
||||
|
||||
@property
|
||||
def image_ref(self):
|
||||
"""Valid imageRef to use """
|
||||
return self.get("image_ref", 3);
|
||||
|
||||
@property
|
||||
def image_ref_alt(self):
|
||||
"""Valid imageRef to rebuild images with"""
|
||||
return self.get("image_ref_alt", 3);
|
||||
|
||||
@property
|
||||
def flavor_ref(self):
|
||||
"""Valid flavorRef to use"""
|
||||
return self.get("flavor_ref", 1);
|
||||
|
||||
@property
|
||||
def flavor_ref_alt(self):
|
||||
"""Valid flavorRef to resize images with"""
|
||||
return self.get("flavor_ref_alt", 2);
|
||||
|
||||
@property
|
||||
def multi_node(self):
|
||||
""" Does the test environment have more than one compute node """
|
||||
return self.get("multi_node", 'false') != 'false'
|
||||
|
||||
|
||||
class StackConfig(object):
|
||||
"""Provides `kong` configuration information."""
|
||||
|
||||
_path = None
|
||||
|
||||
def __init__(self, path=None):
|
||||
"""Initialize a configuration from a path."""
|
||||
self._path = path or self._path
|
||||
self._conf = self.load_config(self._path)
|
||||
self.nova = NovaConfig(self._conf)
|
||||
self.env = EnvironmentConfig(self._conf)
|
||||
|
||||
def load_config(self, path=None):
|
||||
"""Read configuration from given path and return a config object."""
|
||||
config = ConfigParser.SafeConfigParser()
|
||||
config.read(path)
|
||||
return config
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
class TimeoutException(Exception):
|
||||
""" Exception on timeout """
|
||||
def __repr__(self):
|
||||
return "Request Timed Out"
|
||||
|
||||
|
||||
class ServerNotFound(KeyError):
|
||||
pass
|
||||
@@ -0,0 +1,25 @@
|
||||
import re
|
||||
|
||||
|
||||
class KnownIssuesFinder(object):
|
||||
|
||||
def __init__(self):
|
||||
self.count = 0
|
||||
self._pattern = re.compile('# *KNOWN-ISSUE')
|
||||
|
||||
def find_known_issues(self, package):
|
||||
for file in self._find_test_module_files(package):
|
||||
self._count_known_issues(file)
|
||||
|
||||
def _find_test_module_files(self, package):
|
||||
for name in dir(package):
|
||||
if name.startswith('test'):
|
||||
module = getattr(package, name)
|
||||
yield module.__file__
|
||||
|
||||
def _count_known_issues(self, file):
|
||||
if file.endswith('.pyc') or file.endswith('.pyo'):
|
||||
file = file[0:-1]
|
||||
for line in open(file):
|
||||
if self._pattern.search(line) is not None:
|
||||
self.count += 1
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
import json
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
import kong.common.http
|
||||
from kong import exceptions
|
||||
|
||||
|
||||
class API(kong.common.http.Client):
|
||||
"""Barebones Nova HTTP API client."""
|
||||
|
||||
def __init__(self, host, port, base_url, user, api_key, project_id=''):
|
||||
"""Initialize Nova HTTP API client.
|
||||
|
||||
:param host: Hostname/IP of the Nova API to test.
|
||||
:param port: Port of the Nova API to test.
|
||||
:param base_url: Version identifier (normally /v1.0 or /v1.1)
|
||||
:param user: The username to use for tests.
|
||||
:param api_key: The API key of the user.
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
super(API, self).__init__(host, port, base_url)
|
||||
self.user = user
|
||||
self.api_key = api_key
|
||||
self.project_id = project_id
|
||||
# Default to same as base_url, but will be change on auth
|
||||
self.management_url = self.base_url
|
||||
|
||||
def authenticate(self, user, api_key, project_id):
|
||||
"""Request and return an authentication token from Nova.
|
||||
|
||||
:param user: The username we're authenticating.
|
||||
:param api_key: The API key for the user we're authenticating.
|
||||
:returns: Authentication token (string)
|
||||
:raises: KeyError if authentication fails.
|
||||
|
||||
"""
|
||||
headers = {
|
||||
'X-Auth-User': user,
|
||||
'X-Auth-Key': api_key,
|
||||
'X-Auth-Project-Id': project_id,
|
||||
}
|
||||
resp, body = super(API, self).request('GET', '', headers=headers,
|
||||
base_url=self.base_url)
|
||||
|
||||
try:
|
||||
self.management_url = resp['x-server-management-url']
|
||||
return resp['x-auth-token']
|
||||
except KeyError:
|
||||
print "Failed to authenticate user"
|
||||
raise
|
||||
|
||||
def _wait_for_entity_status(self, url, entity_name, status, **kwargs):
|
||||
"""Poll the provided url until expected entity status is returned"""
|
||||
|
||||
def check_response(resp, body):
|
||||
try:
|
||||
data = json.loads(body)
|
||||
return data[entity_name]['status'] == status
|
||||
except (ValueError, KeyError):
|
||||
return False
|
||||
|
||||
try:
|
||||
self.poll_request('GET', url, check_response, **kwargs)
|
||||
except exceptions.TimeoutException:
|
||||
msg = "%s failed to reach status %s" % (entity_name, status)
|
||||
raise AssertionError(msg)
|
||||
|
||||
def wait_for_server_status(self, server_id, status='ACTIVE', **kwargs):
|
||||
"""Wait for the server status to be equal to the status passed in.
|
||||
|
||||
:param server_id: Server ID to query.
|
||||
:param status: The status string to look for.
|
||||
:returns: None
|
||||
:raises: AssertionError if request times out
|
||||
|
||||
"""
|
||||
url = '/servers/%s' % server_id
|
||||
return self._wait_for_entity_status(url, 'server', status, **kwargs)
|
||||
|
||||
def wait_for_image_status(self, image_id, status='ACTIVE', **kwargs):
|
||||
"""Wait for the image status to be equal to the status passed in.
|
||||
|
||||
:param image_id: Image ID to query.
|
||||
:param status: The status string to look for.
|
||||
:returns: None
|
||||
:raises: AssertionError if request times out
|
||||
|
||||
"""
|
||||
url = '/images/%s' % image_id
|
||||
return self._wait_for_entity_status(url, 'image', status, **kwargs)
|
||||
|
||||
def request(self, method, url, **kwargs):
|
||||
"""Generic HTTP request on the Nova API.
|
||||
|
||||
:param method: Request verb to use (GET, PUT, POST, etc.)
|
||||
:param url: The API resource to request.
|
||||
:param kwargs: Additional keyword arguments to pass to the request.
|
||||
:returns: HTTP response object.
|
||||
|
||||
"""
|
||||
headers = kwargs.get('headers', {})
|
||||
project_id = kwargs.get('project_id', self.project_id)
|
||||
|
||||
headers['X-Auth-Token'] = self.authenticate(self.user, self.api_key,
|
||||
self.project_id)
|
||||
kwargs['headers'] = headers
|
||||
return super(API, self).request(method, url, **kwargs)
|
||||
|
||||
def get_server(self, server_id):
|
||||
"""Fetch a server by id
|
||||
|
||||
:param server_id: dict of server attributes
|
||||
:returns: dict of server attributes
|
||||
:raises: ServerNotFound if server does not exist
|
||||
|
||||
"""
|
||||
resp, body = self.request('GET', '/servers/%s' % server_id)
|
||||
try:
|
||||
assert resp['status'] == '200'
|
||||
data = json.loads(body)
|
||||
return data['server']
|
||||
except (AssertionError, ValueError, TypeError, KeyError):
|
||||
raise exceptions.ServerNotFound(server_id)
|
||||
|
||||
def create_server(self, entity):
|
||||
"""Attempt to create a new server.
|
||||
|
||||
:param entity: dict of server attributes
|
||||
:returns: dict of server attributes after creation
|
||||
:raises: AssertionError if server creation fails
|
||||
|
||||
"""
|
||||
post_body = json.dumps({
|
||||
'server': entity,
|
||||
})
|
||||
|
||||
resp, body = self.request('POST', '/servers', body=post_body)
|
||||
try:
|
||||
assert resp['status'] == '202'
|
||||
data = json.loads(body)
|
||||
return data['server']
|
||||
except (AssertionError, ValueError, TypeError, KeyError):
|
||||
raise AssertionError("Failed to create server")
|
||||
|
||||
def delete_server(self, server_id):
|
||||
"""Attempt to delete a server.
|
||||
|
||||
:param server_id: server identifier
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
url = '/servers/%s' % server_id
|
||||
response, body = self.request('DELETE', url)
|
||||
@@ -0,0 +1,15 @@
|
||||
import kong.config
|
||||
import kong.nova
|
||||
|
||||
|
||||
class Manager(object):
|
||||
"""Top-level object to access OpenStack resources."""
|
||||
|
||||
def __init__(self):
|
||||
self.config = kong.config.StackConfig()
|
||||
self.nova = kong.nova.API(self.config.nova.host,
|
||||
self.config.nova.port,
|
||||
self.config.nova.base_url,
|
||||
self.config.nova.username,
|
||||
self.config.nova.api_key,
|
||||
self.config.nova.project_id)
|
||||
@@ -0,0 +1,102 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
import unittest2 as unittest
|
||||
|
||||
from kong import openstack
|
||||
|
||||
|
||||
class FlavorsTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.os = openstack.Manager()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def _index_flavors(self):
|
||||
url = '/flavors'
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
self.assertEqual(response['status'], '200')
|
||||
body_dict = json.loads(body)
|
||||
self.assertEqual(body_dict.keys(), ['flavors'])
|
||||
return body_dict['flavors']
|
||||
|
||||
def _show_flavor(self, flavor_id):
|
||||
url = '/flavors/%s' % flavor_id
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
self.assertEqual(response['status'], '200')
|
||||
body_dict = json.loads(body)
|
||||
self.assertEqual(body_dict.keys(), ['flavor'])
|
||||
return body_dict['flavor']
|
||||
|
||||
def _assert_flavor_entity_basic(self, flavor):
|
||||
actual_keys = set(flavor.keys())
|
||||
expected_keys = set(('id', 'name', 'links'))
|
||||
self.assertEqual(actual_keys, expected_keys)
|
||||
self._assert_flavor_links(flavor)
|
||||
|
||||
def _assert_flavor_entity_detailed(self, flavor):
|
||||
actual_keys = set(flavor.keys())
|
||||
expected_keys = set(('id', 'name', 'ram', 'disk', 'links'))
|
||||
self.assertEqual(actual_keys, expected_keys)
|
||||
self.assertEqual(type(flavor['ram']), int)
|
||||
self.assertEqual(type(flavor['disk']), int)
|
||||
self._assert_flavor_links(flavor)
|
||||
|
||||
def _assert_flavor_links(self, flavor):
|
||||
actual_links = flavor['links']
|
||||
|
||||
flavor_id = str(flavor['id'])
|
||||
host = self.os.config.nova.host
|
||||
port = self.os.config.nova.port
|
||||
api_url = '%s:%s' % (host, port)
|
||||
base_url = os.path.join(api_url, self.os.config.nova.base_url,
|
||||
self.os.config.nova.project_id)
|
||||
api_url = os.path.join(api_url, self.os.config.nova.project_id)
|
||||
|
||||
self_link = 'http://' + os.path.join(base_url, 'flavors', flavor_id)
|
||||
bookmark_link = 'http://' + os.path.join(api_url, 'flavors', flavor_id)
|
||||
|
||||
expected_links = [
|
||||
{
|
||||
'rel': 'self',
|
||||
'href': self_link,
|
||||
},
|
||||
{
|
||||
'rel': 'bookmark',
|
||||
'href': bookmark_link,
|
||||
},
|
||||
]
|
||||
|
||||
self.assertEqual(actual_links, expected_links)
|
||||
|
||||
def test_show_flavor(self):
|
||||
"""Retrieve a single flavor"""
|
||||
|
||||
flavors = self._index_flavors()
|
||||
|
||||
for flavor in flavors:
|
||||
detailed_flavor = self._show_flavor(flavor['id'])
|
||||
self._assert_flavor_entity_detailed(detailed_flavor)
|
||||
|
||||
def test_index_flavors_basic(self):
|
||||
"""List all flavors"""
|
||||
|
||||
flavors = self._index_flavors()
|
||||
|
||||
for flavor in flavors:
|
||||
self._assert_flavor_entity_basic(flavor)
|
||||
|
||||
def test_index_flavors_detailed(self):
|
||||
"""List all flavors in detail"""
|
||||
|
||||
url = '/flavors/detail'
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
self.assertEqual(response['status'], '200')
|
||||
body_dict = json.loads(body)
|
||||
self.assertEqual(body_dict.keys(), ['flavors'])
|
||||
flavors = body_dict['flavors']
|
||||
|
||||
for flavor in flavors:
|
||||
self._assert_flavor_entity_detailed(flavor)
|
||||
@@ -0,0 +1,97 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
import unittest2 as unittest
|
||||
|
||||
from kong import openstack
|
||||
|
||||
|
||||
class ImagesTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.os = openstack.Manager()
|
||||
|
||||
host = self.os.config.nova.host
|
||||
port = self.os.config.nova.port
|
||||
self.base_url = '%s:%s' % (host, port)
|
||||
self.api_url = os.path.join(self.base_url, self.os.config.nova.base_url)
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def _assert_image_links(self, image):
|
||||
image_id = str(image['id'])
|
||||
|
||||
self_link = 'http://' + os.path.join(self.api_url,
|
||||
self.os.config.nova.project_id,
|
||||
'images', image_id)
|
||||
bookmark_link = 'http://' + os.path.join(self.base_url,
|
||||
self.os.config.nova.project_id,
|
||||
'images', image_id)
|
||||
|
||||
expected_links = [
|
||||
{
|
||||
'rel': 'self',
|
||||
'href': self_link,
|
||||
},
|
||||
{
|
||||
'rel': 'bookmark',
|
||||
'href': bookmark_link,
|
||||
},
|
||||
]
|
||||
|
||||
self.assertEqual(image['links'], expected_links)
|
||||
|
||||
def _assert_image_entity_basic(self, image):
|
||||
actual_keys = set(image.keys())
|
||||
expected_keys = set((
|
||||
'id',
|
||||
'name',
|
||||
'links',
|
||||
))
|
||||
self.assertEqual(actual_keys, expected_keys)
|
||||
|
||||
self._assert_image_links(image)
|
||||
|
||||
def _assert_image_entity_detailed(self, image):
|
||||
keys = image.keys()
|
||||
if 'server' in keys:
|
||||
keys.remove('server')
|
||||
actual_keys = set(keys)
|
||||
expected_keys = set((
|
||||
'id',
|
||||
'name',
|
||||
'progress',
|
||||
'created',
|
||||
'updated',
|
||||
'status',
|
||||
'metadata',
|
||||
'links',
|
||||
))
|
||||
self.assertEqual(actual_keys, expected_keys)
|
||||
|
||||
self._assert_image_links(image)
|
||||
|
||||
def test_index(self):
|
||||
"""List all images"""
|
||||
|
||||
response, body = self.os.nova.request('GET', '/images')
|
||||
|
||||
self.assertEqual(response['status'], '200')
|
||||
resp_body = json.loads(body)
|
||||
self.assertEqual(resp_body.keys(), ['images'])
|
||||
|
||||
for image in resp_body['images']:
|
||||
self._assert_image_entity_basic(image)
|
||||
|
||||
def test_detail(self):
|
||||
"""List all images in detail"""
|
||||
|
||||
response, body = self.os.nova.request('GET', '/images/detail')
|
||||
|
||||
self.assertEqual(response['status'], '200')
|
||||
resp_body = json.loads(body)
|
||||
self.assertEqual(resp_body.keys(), ['images'])
|
||||
|
||||
for image in resp_body['images']:
|
||||
self._assert_image_entity_detailed(image)
|
||||
@@ -0,0 +1,354 @@
|
||||
|
||||
import json
|
||||
import time
|
||||
|
||||
from kong import exceptions
|
||||
from kong import openstack
|
||||
from kong.common import ssh
|
||||
|
||||
import unittest2 as unittest
|
||||
|
||||
|
||||
class ServerActionsTest(unittest.TestCase):
|
||||
|
||||
multi_node = openstack.Manager().config.env.multi_node
|
||||
|
||||
def setUp(self):
|
||||
self.os = openstack.Manager()
|
||||
|
||||
self.image_ref = self.os.config.env.image_ref
|
||||
self.image_ref_alt = self.os.config.env.image_ref_alt
|
||||
self.flavor_ref = self.os.config.env.flavor_ref
|
||||
self.flavor_ref_alt = self.os.config.env.flavor_ref_alt
|
||||
self.ssh_timeout = self.os.config.nova.ssh_timeout
|
||||
|
||||
self.server_password = 'testpwd'
|
||||
self.server_name = 'testserver'
|
||||
|
||||
expected_server = {
|
||||
'name' : self.server_name,
|
||||
'imageRef' : self.image_ref,
|
||||
'flavorRef' : self.flavor_ref,
|
||||
'adminPass' : self.server_password,
|
||||
}
|
||||
|
||||
created_server = self.os.nova.create_server(expected_server)
|
||||
|
||||
self.server_id = created_server['id']
|
||||
self._wait_for_status(self.server_id, 'ACTIVE')
|
||||
|
||||
server = self.os.nova.get_server(self.server_id)
|
||||
|
||||
# KNOWN-ISSUE lp?
|
||||
#self.access_ip = server['accessIPv4']
|
||||
self.access_ip = server['addresses']['public'][0]['addr']
|
||||
|
||||
# Ensure server came up
|
||||
self._assert_ssh_password()
|
||||
|
||||
def tearDown(self):
|
||||
self.os.nova.delete_server(self.server_id)
|
||||
|
||||
def _get_ssh_client(self, password):
|
||||
return ssh.Client(self.access_ip, 'root', password, self.ssh_timeout)
|
||||
|
||||
def _assert_ssh_password(self, password=None):
|
||||
_password = password or self.server_password
|
||||
client = self._get_ssh_client(_password)
|
||||
self.assertTrue(client.test_connection_auth())
|
||||
|
||||
def _wait_for_status(self, server_id, status):
|
||||
try:
|
||||
self.os.nova.wait_for_server_status(server_id, status)
|
||||
except exceptions.TimeoutException:
|
||||
self.fail("Server failed to change status to %s" % status)
|
||||
|
||||
def _get_boot_time(self):
|
||||
"""Return the time the server was started"""
|
||||
output = self._read_file("/proc/uptime")
|
||||
uptime = float(output.split().pop(0))
|
||||
return time.time() - uptime
|
||||
|
||||
def _write_file(self, filename, contents):
|
||||
return self._exec_command("echo -n %s > %s" % (contents, filename))
|
||||
|
||||
def _read_file(self, filename):
|
||||
return self._exec_command("cat %s" % filename)
|
||||
|
||||
def _exec_command(self, command):
|
||||
client = self._get_ssh_client(self.server_password)
|
||||
return client.exec_command(command)
|
||||
|
||||
def test_reboot_server_soft(self):
|
||||
"""Reboot a server (SOFT)"""
|
||||
|
||||
# SSH and get the uptime
|
||||
initial_time_started = self._get_boot_time()
|
||||
|
||||
# Make reboot request
|
||||
post_body = json.dumps({
|
||||
'reboot' : {
|
||||
'type' : 'SOFT',
|
||||
}
|
||||
})
|
||||
url = "/servers/%s/action" % self.server_id
|
||||
response, body = self.os.nova.request('POST', url, body=post_body)
|
||||
self.assertEqual(response['status'], '202')
|
||||
|
||||
# Assert status transition
|
||||
# KNOWN-ISSUE
|
||||
#self.os.nova.wait_for_server_status(self.server_id, 'REBOOT')
|
||||
ssh_client = self._get_ssh_client(self.server_password)
|
||||
ssh_client.connect_until_closed()
|
||||
self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
|
||||
|
||||
# SSH and verify uptime is less than before
|
||||
post_reboot_time_started = self._get_boot_time()
|
||||
self.assertTrue(initial_time_started < post_reboot_time_started)
|
||||
|
||||
def test_reboot_server_hard(self):
|
||||
"""Reboot a server (HARD)"""
|
||||
|
||||
# SSH and get the uptime
|
||||
initial_time_started = self._get_boot_time()
|
||||
|
||||
# Make reboot request
|
||||
post_body = json.dumps({
|
||||
'reboot' : {
|
||||
'type' : 'HARD',
|
||||
}
|
||||
})
|
||||
url = "/servers/%s/action" % self.server_id
|
||||
response, body = self.os.nova.request('POST', url, body=post_body)
|
||||
self.assertEqual(response['status'], '202')
|
||||
|
||||
# Assert status transition
|
||||
# KNOWN-ISSUE
|
||||
#self.os.nova.wait_for_server_status(self.server_id, 'HARD_REBOOT')
|
||||
ssh_client = self._get_ssh_client(self.server_password)
|
||||
ssh_client.connect_until_closed()
|
||||
self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
|
||||
|
||||
# SSH and verify uptime is less than before
|
||||
post_reboot_time_started = self._get_boot_time()
|
||||
self.assertTrue(initial_time_started < post_reboot_time_started)
|
||||
|
||||
def test_change_server_password(self):
|
||||
"""Change root password of a server"""
|
||||
|
||||
# SSH into server using original password
|
||||
self._assert_ssh_password()
|
||||
|
||||
# Change server password
|
||||
post_body = json.dumps({
|
||||
'changePassword' : {
|
||||
'adminPass' : 'test123',
|
||||
}
|
||||
})
|
||||
url = '/servers/%s/action' % self.server_id
|
||||
response, body = self.os.nova.request('POST', url, body=post_body)
|
||||
|
||||
# Assert status transition
|
||||
self.assertEqual('202', response['status'])
|
||||
# KNOWN-ISSUE
|
||||
#self.os.nova.wait_for_server_status(self.server_id, 'PASSWORD')
|
||||
self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
|
||||
|
||||
# SSH into server using new password
|
||||
self._assert_ssh_password('test123')
|
||||
|
||||
def test_rebuild_server(self):
|
||||
"""Rebuild a server"""
|
||||
|
||||
filename = '/tmp/testfile'
|
||||
contents = 'WORDS'
|
||||
self._write_file(filename, contents)
|
||||
self.assertEqual(self._read_file(filename), contents)
|
||||
|
||||
# Make rebuild request
|
||||
post_body = json.dumps({
|
||||
'rebuild' : {
|
||||
'imageRef' : self.image_ref_alt,
|
||||
}
|
||||
})
|
||||
url = '/servers/%s/action' % self.server_id
|
||||
response, body = self.os.nova.request('POST', url, body=post_body)
|
||||
|
||||
# Ensure correct status transition
|
||||
self.assertEqual('202', response['status'])
|
||||
# KNOWN-ISSUE
|
||||
#self.os.nova.wait_for_server_status(self.server_id, 'REBUILD')
|
||||
self.os.nova.wait_for_server_status(self.server_id, 'BUILD')
|
||||
self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
|
||||
|
||||
# Treats an issue where we ssh'd in too soon after rebuild
|
||||
time.sleep(30)
|
||||
|
||||
# Check that the instance's imageRef matches the new imageRef
|
||||
server = self.os.nova.get_server(self.server_id)
|
||||
ref_match = self.image_ref_alt == server['image']['links'][0]['href']
|
||||
id_match = self.image_ref_alt == server['image']['id']
|
||||
self.assertTrue(ref_match or id_match)
|
||||
|
||||
# SSH into the server to ensure it came back up
|
||||
self._assert_ssh_password()
|
||||
|
||||
# make sure file is gone
|
||||
self.assertEqual(self._read_file(filename), '')
|
||||
|
||||
@unittest.skipIf(not multi_node, 'Multiple compute nodes required')
|
||||
def test_resize_server_confirm(self):
|
||||
"""Resize a server"""
|
||||
# Make resize request
|
||||
post_body = json.dumps({
|
||||
'resize' : {
|
||||
'flavorRef': self.flavor_ref_alt,
|
||||
}
|
||||
})
|
||||
url = '/servers/%s/action' % self.server_id
|
||||
response, body = self.os.nova.request('POST', url, body=post_body)
|
||||
|
||||
# Wait for status transition
|
||||
self.assertEqual('202', response['status'])
|
||||
# KNOWN-ISSUE
|
||||
#self.os.nova.wait_for_server_status(self.server_id, 'VERIFY_RESIZE')
|
||||
self.os.nova.wait_for_server_status(self.server_id, 'RESIZE-CONFIRM')
|
||||
|
||||
# Ensure API reports new flavor
|
||||
server = self.os.nova.get_server(self.server_id)
|
||||
self.assertEqual(self.flavor_ref_alt, server['flavor']['id'])
|
||||
|
||||
#SSH into the server to ensure it came back up
|
||||
self._assert_ssh_password()
|
||||
|
||||
# Make confirmResize request
|
||||
post_body = json.dumps({
|
||||
'confirmResize' : 'null'
|
||||
})
|
||||
url = '/servers/%s/action' % self.server_id
|
||||
response, body = self.os.nova.request('POST', url, body=post_body)
|
||||
|
||||
# Wait for status transition
|
||||
self.assertEqual('204', response['status'])
|
||||
self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
|
||||
|
||||
# Ensure API still reports new flavor
|
||||
server = self.os.nova.get_server(self.server_id)
|
||||
self.assertEqual(self.flavor_ref_alt, server['flavor']['id'])
|
||||
|
||||
@unittest.skipIf(not multi_node, 'Multiple compute nodes required')
|
||||
def test_resize_server_revert(self):
|
||||
"""Resize a server, then revert"""
|
||||
|
||||
# Make resize request
|
||||
post_body = json.dumps({
|
||||
'resize' : {
|
||||
'flavorRef': self.flavor_ref_alt,
|
||||
}
|
||||
})
|
||||
url = '/servers/%s/action' % self.server_id
|
||||
response, body = self.os.nova.request('POST', url, body=post_body)
|
||||
|
||||
# Wait for status transition
|
||||
self.assertEqual('202', response['status'])
|
||||
# KNOWN-ISSUE
|
||||
#self.os.nova.wait_for_server_status(self.server_id, 'VERIFY_RESIZE')
|
||||
self.os.nova.wait_for_server_status(self.server_id, 'RESIZE-CONFIRM')
|
||||
|
||||
# SSH into the server to ensure it came back up
|
||||
self._assert_ssh_password()
|
||||
|
||||
# Ensure API reports new flavor
|
||||
server = self.os.nova.get_server(self.server_id)
|
||||
self.assertEqual(self.flavor_ref_alt, server['flavor']['id'])
|
||||
|
||||
# Make revertResize request
|
||||
post_body = json.dumps({
|
||||
'revertResize' : 'null'
|
||||
})
|
||||
url = '/servers/%s/action' % self.server_id
|
||||
response, body = self.os.nova.request('POST', url, body=post_body)
|
||||
|
||||
# Assert status transition
|
||||
self.assertEqual('202', response['status'])
|
||||
self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
|
||||
|
||||
# Ensure flavor ref was reverted to original
|
||||
server = self.os.nova.get_server(self.server_id)
|
||||
self.assertEqual(self.flavor_ref, server['flavor']['id'])
|
||||
|
||||
|
||||
class SnapshotTests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.os = openstack.Manager()
|
||||
|
||||
self.image_ref = self.os.config.env.image_ref
|
||||
self.flavor_ref = self.os.config.env.flavor_ref
|
||||
self.ssh_timeout = self.os.config.nova.ssh_timeout
|
||||
|
||||
self.server_name = 'testserver'
|
||||
|
||||
expected_server = {
|
||||
'name' : self.server_name,
|
||||
'imageRef' : self.image_ref,
|
||||
'flavorRef' : self.flavor_ref,
|
||||
}
|
||||
|
||||
created_server = self.os.nova.create_server(expected_server)
|
||||
self.server_id = created_server['id']
|
||||
|
||||
def tearDown(self):
|
||||
self.os.nova.delete_server(self.server_id)
|
||||
|
||||
def _wait_for_status(self, server_id, status):
|
||||
try:
|
||||
self.os.nova.wait_for_server_status(server_id, status)
|
||||
except exceptions.TimeoutException:
|
||||
self.fail("Server failed to change status to %s" % status)
|
||||
|
||||
def test_snapshot_server_active(self):
|
||||
"""Create image from an existing server"""
|
||||
|
||||
# Wait for server to come up before running this test
|
||||
self._wait_for_status(self.server_id, 'ACTIVE')
|
||||
|
||||
# Create snapshot
|
||||
image_data = {'name' : 'backup'}
|
||||
req_body = json.dumps({'createImage': image_data})
|
||||
url = '/servers/%s/action' % self.server_id
|
||||
response, body = self.os.nova.request('POST', url, body=req_body)
|
||||
print response
|
||||
print body
|
||||
|
||||
self.assertEqual(response['status'], '202')
|
||||
image_ref = response['location']
|
||||
snapshot_id = image_ref.rsplit('/',1)[1]
|
||||
|
||||
# Get snapshot and check its attributes
|
||||
resp, body = self.os.nova.request('GET', '/images/%s' % snapshot_id)
|
||||
snapshot = json.loads(body)['image']
|
||||
self.assertEqual(snapshot['name'], image_data['name'])
|
||||
server_ref = snapshot['server']['links'][0]['href']
|
||||
self.assertTrue(server_ref.endswith('/%s' % self.server_id))
|
||||
|
||||
# Ensure image is actually created
|
||||
self.os.nova.wait_for_image_status(snapshot['id'], 'ACTIVE')
|
||||
|
||||
# Cleaning up
|
||||
self.os.nova.request('DELETE', '/images/%s' % snapshot_id)
|
||||
|
||||
def test_snapshot_server_inactive(self):
|
||||
"""Ensure inability to snapshot server in BUILD state"""
|
||||
|
||||
# Create snapshot
|
||||
req_body = json.dumps({'createImage': {'name' : 'backup'}})
|
||||
url = '/servers/%s/action' % self.server_id
|
||||
response, body = self.os.nova.request('POST', url, body=req_body)
|
||||
|
||||
# KNOWN-ISSUE - we shouldn't be able to snapshot a building server
|
||||
#self.assertEqual(response['status'], '400') # what status code?
|
||||
self.assertEqual(response['status'], '202')
|
||||
snapshot_id = response['location'].rsplit('/', 1)[1]
|
||||
# Delete image for now, won't need this once correct status code is in
|
||||
self.os.nova.request('DELETE', '/images/%s' % snapshot_id)
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
import unittest2 as unittest
|
||||
|
||||
from kong import openstack
|
||||
from kong import exceptions
|
||||
|
||||
|
||||
class ServerAddressesTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
self.os = openstack.Manager()
|
||||
self.image_ref = self.os.config.env.image_ref
|
||||
self.flavor_ref = self.os.config.env.flavor_ref
|
||||
|
||||
def setUp(self):
|
||||
server = {
|
||||
'name' : 'testserver',
|
||||
'imageRef' : self.image_ref,
|
||||
'flavorRef' : self.flavor_ref,
|
||||
}
|
||||
|
||||
created_server = self.os.nova.create_server(server)
|
||||
self.server_id = created_server['id']
|
||||
self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
|
||||
|
||||
def tearDown(self):
|
||||
self.os.nova.delete_server(self.server_id)
|
||||
|
||||
def test_server_addresses(self):
|
||||
"""Retrieve server addresses information"""
|
||||
url = '/servers/%s' % self.server_id
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
self.assertEqual(response.status, 200)
|
||||
body = json.loads(body)
|
||||
self.assertTrue('addresses' in body['server'].keys())
|
||||
server_addresses = body['server']['addresses']
|
||||
|
||||
url = '/servers/%s/ips' % self.server_id
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
self.assertEqual(response.status, 200)
|
||||
body = json.loads(body)
|
||||
self.assertEqual(body.keys(), ['addresses'])
|
||||
ips_addresses = body['addresses']
|
||||
|
||||
self.assertEqual(server_addresses, ips_addresses)
|
||||
|
||||
# Now validate entities within addresses containers if available
|
||||
for (network, network_data) in ips_addresses.items():
|
||||
# Ensure we can query for each particular network
|
||||
url = '/servers/%s/ips/%s' % (self.server_id, network)
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
self.assertEqual(response.status, 200)
|
||||
body = json.loads(body)
|
||||
self.assertEqual(body.keys(), [network])
|
||||
self.assertEqual(body[network], network_data)
|
||||
|
||||
for ip_data in network_data:
|
||||
self.assertEqual(set(ip_data.keys()),
|
||||
set(['addr', 'version']))
|
||||
@@ -0,0 +1,173 @@
|
||||
|
||||
import json
|
||||
|
||||
import unittest2 as unittest
|
||||
|
||||
from kong import openstack
|
||||
|
||||
|
||||
class ServersMetadataTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
self.os = openstack.Manager()
|
||||
self.image_ref = self.os.config.env.image_ref
|
||||
self.flavor_ref = self.os.config.env.flavor_ref
|
||||
|
||||
def setUp(self):
|
||||
server = {
|
||||
'name' : 'testserver',
|
||||
'imageRef' : self.image_ref,
|
||||
'flavorRef' : self.flavor_ref,
|
||||
'metadata' : {
|
||||
'testEntry' : 'testValue',
|
||||
},
|
||||
}
|
||||
|
||||
created_server = self.os.nova.create_server(server)
|
||||
self.server_id = created_server['id']
|
||||
|
||||
def tearDown(self):
|
||||
self.os.nova.delete_server(self.server_id)
|
||||
|
||||
|
||||
def test_get_server_metadata(self):
|
||||
"""Retrieve metadata for a server"""
|
||||
|
||||
url = '/servers/%s/metadata' % self.server_id
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
self.assertEqual(200, response.status)
|
||||
|
||||
result = json.loads(body)
|
||||
expected = {
|
||||
'metadata' : {
|
||||
'testEntry' : 'testValue',
|
||||
},
|
||||
}
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_post_server_metadata(self):
|
||||
"""Create or update metadata for a server"""
|
||||
|
||||
post_metadata = {
|
||||
'metadata' : {
|
||||
'new_entry1' : 'new_value1',
|
||||
'new_entry2' : 'new_value2',
|
||||
},
|
||||
}
|
||||
post_body = json.dumps(post_metadata)
|
||||
|
||||
url = '/servers/%s/metadata' % self.server_id
|
||||
response, body = self.os.nova.request('POST', url, body=post_body)
|
||||
self.assertEqual(200, response.status)
|
||||
|
||||
url = '/servers/%s/metadata' % self.server_id
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
self.assertEqual(200, response.status)
|
||||
|
||||
result = json.loads(body)
|
||||
expected = post_metadata
|
||||
expected['metadata']['testEntry'] = 'testValue'
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_put_server_metadata(self):
|
||||
"""Overwrite all metadata for a server"""
|
||||
|
||||
expected = {
|
||||
'metadata' : {
|
||||
'new_entry1' : 'new_value1',
|
||||
'new_entry2' : 'new_value2',
|
||||
},
|
||||
}
|
||||
|
||||
url = '/servers/%s/metadata' % self.server_id
|
||||
post_body = json.dumps(expected)
|
||||
response, body = self.os.nova.request('PUT', url, body=post_body)
|
||||
self.assertEqual(200, response.status)
|
||||
|
||||
url = '/servers/%s/metadata' % self.server_id
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
self.assertEqual(200, response.status)
|
||||
|
||||
result = json.loads(body)
|
||||
# We want to make sure 'testEntry' was removed
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_get_server_metadata_key(self):
|
||||
"""Retrieve specific metadata key for a server"""
|
||||
|
||||
url = '/servers/%s/metadata/testEntry' % self.server_id
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
self.assertEqual(200, response.status)
|
||||
|
||||
result = json.loads(body)
|
||||
expected = {
|
||||
'meta':{
|
||||
'testEntry':'testValue',
|
||||
},
|
||||
}
|
||||
|
||||
self.assertDictEqual(expected, result)
|
||||
|
||||
def test_add_server_metadata_key(self):
|
||||
"""Set specific metadata key on a server"""
|
||||
|
||||
expected_meta = {
|
||||
'meta' : {
|
||||
'new_meta1' : 'new_value1',
|
||||
},
|
||||
}
|
||||
|
||||
put_body = json.dumps(expected_meta)
|
||||
|
||||
url = '/servers/%s/metadata/new_meta1' % self.server_id
|
||||
response, body = self.os.nova.request('PUT', url, body=put_body)
|
||||
self.assertEqual(200, response.status)
|
||||
result = json.loads(body)
|
||||
self.assertDictEqual(expected_meta, result)
|
||||
|
||||
expected_metadata = {
|
||||
'metadata' : {
|
||||
'testEntry' : 'testValue',
|
||||
'new_meta1' : 'new_value1',
|
||||
},
|
||||
}
|
||||
|
||||
# Now check all metadata to make sure the other values are there
|
||||
url = '/servers/%s/metadata' % self.server_id
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
result = json.loads(body)
|
||||
self.assertDictEqual(expected_metadata, result)
|
||||
|
||||
def test_update_server_metadata_key(self):
|
||||
"""Update specific metadata key for a server"""
|
||||
|
||||
expected_meta = {
|
||||
'meta' : {
|
||||
'testEntry' : 'testValue2',
|
||||
},
|
||||
}
|
||||
put_body = json.dumps(expected_meta)
|
||||
|
||||
url = '/servers/%s/metadata/testEntry' % self.server_id
|
||||
response, body = self.os.nova.request('PUT', url, body=put_body)
|
||||
self.assertEqual(200, response.status)
|
||||
result = json.loads(body)
|
||||
self.assertEqual(expected_meta, result)
|
||||
|
||||
def test_delete_server_metadata_key(self):
|
||||
"""Delete metadata for a server"""
|
||||
|
||||
url = '/servers/%s/metadata/testEntry' % self.server_id
|
||||
response, body = self.os.nova.request('DELETE', url)
|
||||
self.assertEquals(204, response.status)
|
||||
|
||||
url = '/servers/%s/metadata/testEntry' % self.server_id
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
self.assertEquals(404, response.status)
|
||||
|
||||
url = '/servers/%s/metadata' % self.server_id
|
||||
response, body = self.os.nova.request('GET', url)
|
||||
self.assertEquals(200, response.status)
|
||||
result = json.loads(body)
|
||||
self.assertDictEqual({'metadata':{}}, result)
|
||||
@@ -0,0 +1,373 @@
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
|
||||
import unittest2 as unittest
|
||||
|
||||
from kong import openstack
|
||||
from kong import exceptions
|
||||
from kong.common import ssh
|
||||
|
||||
|
||||
class ServersTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
self.os = openstack.Manager()
|
||||
self.image_ref = self.os.config.env.image_ref
|
||||
self.flavor_ref = self.os.config.env.flavor_ref
|
||||
self.ssh_timeout = self.os.config.nova.ssh_timeout
|
||||
self.build_timeout = self.os.config.nova.build_timeout
|
||||
|
||||
def _assert_server_entity(self, server):
|
||||
actual_keys = set(server.keys())
|
||||
expected_keys = set((
|
||||
'id',
|
||||
'name',
|
||||
'hostId',
|
||||
'status',
|
||||
'metadata',
|
||||
'addresses',
|
||||
'links',
|
||||
'progress',
|
||||
'image',
|
||||
'flavor',
|
||||
'created',
|
||||
'updated',
|
||||
'accessIPv4',
|
||||
'accessIPv6',
|
||||
|
||||
#KNOWN-ISSUE lp804093
|
||||
'uuid',
|
||||
|
||||
))
|
||||
self.assertTrue(expected_keys <= actual_keys)
|
||||
|
||||
server_id = str(server['id'])
|
||||
host = self.os.config.nova.host
|
||||
port = self.os.config.nova.port
|
||||
api_url = '%s:%s' % (host, port)
|
||||
base_url = os.path.join(api_url, self.os.config.nova.base_url)
|
||||
|
||||
self_link = 'http://' + os.path.join(base_url,
|
||||
self.os.config.nova.project_id,
|
||||
'servers', server_id)
|
||||
bookmark_link = 'http://' + os.path.join(api_url,
|
||||
self.os.config.nova.project_id,
|
||||
'servers', server_id)
|
||||
|
||||
expected_links = [
|
||||
{
|
||||
'rel': 'self',
|
||||
'href': self_link,
|
||||
},
|
||||
{
|
||||
'rel': 'bookmark',
|
||||
'href': bookmark_link,
|
||||
},
|
||||
]
|
||||
|
||||
self.assertEqual(server['links'], expected_links)
|
||||
|
||||
def test_build_server(self):
|
||||
"""Build a server"""
|
||||
|
||||
expected_server = {
|
||||
'name': 'testserver',
|
||||
'metadata': {
|
||||
'key1': 'value1',
|
||||
'key2': 'value2',
|
||||
},
|
||||
'imageRef': self.image_ref,
|
||||
'flavorRef': self.flavor_ref,
|
||||
}
|
||||
|
||||
post_body = json.dumps({'server': expected_server})
|
||||
response, body = self.os.nova.request('POST',
|
||||
'/servers',
|
||||
body=post_body)
|
||||
|
||||
self.assertEqual(response.status, 202)
|
||||
|
||||
_body = json.loads(body)
|
||||
self.assertEqual(_body.keys(), ['server'])
|
||||
created_server = _body['server']
|
||||
|
||||
admin_pass = created_server.pop('adminPass')
|
||||
self._assert_server_entity(created_server)
|
||||
self.assertEqual(expected_server['name'], created_server['name'])
|
||||
self.assertEqual(expected_server['metadata'],
|
||||
created_server['metadata'])
|
||||
|
||||
self.os.nova.wait_for_server_status(created_server['id'],
|
||||
'ACTIVE',
|
||||
timeout=self.build_timeout)
|
||||
|
||||
server = self.os.nova.get_server(created_server['id'])
|
||||
|
||||
# Find IP of server
|
||||
try:
|
||||
(_, network) = server['addresses'].popitem()
|
||||
ip = network[0]['addr']
|
||||
except KeyError:
|
||||
self.fail("Failed to retrieve IP address from server entity")
|
||||
|
||||
# Assert password works
|
||||
client = ssh.Client(ip, 'root', admin_pass, self.ssh_timeout)
|
||||
self.assertTrue(client.test_connection_auth())
|
||||
|
||||
self.os.nova.delete_server(server['id'])
|
||||
|
||||
def test_build_server_with_file(self):
|
||||
"""Build a server with an injected file"""
|
||||
|
||||
file_contents = 'testing'
|
||||
|
||||
expected_server = {
|
||||
'name': 'testserver',
|
||||
'metadata': {
|
||||
'key1': 'value1',
|
||||
'key2': 'value2',
|
||||
},
|
||||
'personality': [
|
||||
{
|
||||
'path': '/etc/test.txt',
|
||||
'contents': base64.b64encode(file_contents),
|
||||
},
|
||||
],
|
||||
'imageRef': self.image_ref,
|
||||
'flavorRef': self.flavor_ref,
|
||||
}
|
||||
|
||||
post_body = json.dumps({'server': expected_server})
|
||||
response, body = self.os.nova.request('POST',
|
||||
'/servers',
|
||||
body=post_body)
|
||||
|
||||
self.assertEqual(response.status, 202)
|
||||
|
||||
_body = json.loads(body)
|
||||
self.assertEqual(_body.keys(), ['server'])
|
||||
created_server = _body['server']
|
||||
|
||||
admin_pass = created_server.pop('adminPass', None)
|
||||
self._assert_server_entity(created_server)
|
||||
self.assertEqual(expected_server['name'], created_server['name'])
|
||||
self.assertEqual(expected_server['metadata'],
|
||||
created_server['metadata'])
|
||||
|
||||
self.os.nova.wait_for_server_status(created_server['id'],
|
||||
'ACTIVE',
|
||||
timeout=self.build_timeout)
|
||||
|
||||
server = self.os.nova.get_server(created_server['id'])
|
||||
|
||||
# Find IP of server
|
||||
try:
|
||||
(_, network) = server['addresses'].popitem()
|
||||
ip = network[0]['addr']
|
||||
except KeyError:
|
||||
self.fail("Failed to retrieve IP address from server entity")
|
||||
|
||||
# Assert injected file is on instance, also verifying password works
|
||||
client = ssh.Client(ip, 'root', admin_pass, self.ssh_timeout)
|
||||
injected_file = client.exec_command('cat /etc/test.txt')
|
||||
self.assertEqual(injected_file, file_contents)
|
||||
|
||||
self.os.nova.delete_server(server['id'])
|
||||
|
||||
def test_build_server_with_password(self):
|
||||
"""Build a server with a password"""
|
||||
|
||||
server_password = 'testpwd'
|
||||
|
||||
expected_server = {
|
||||
'name': 'testserver',
|
||||
'metadata': {
|
||||
'key1': 'value1',
|
||||
'key2': 'value2',
|
||||
},
|
||||
'adminPass': server_password,
|
||||
'imageRef': self.image_ref,
|
||||
'flavorRef': self.flavor_ref,
|
||||
}
|
||||
|
||||
post_body = json.dumps({'server': expected_server})
|
||||
response, body = self.os.nova.request('POST',
|
||||
'/servers',
|
||||
body=post_body)
|
||||
|
||||
self.assertEqual(response.status, 202)
|
||||
|
||||
_body = json.loads(body)
|
||||
self.assertEqual(_body.keys(), ['server'])
|
||||
created_server = _body['server']
|
||||
|
||||
admin_pass = created_server.pop('adminPass', None)
|
||||
self._assert_server_entity(created_server)
|
||||
self.assertEqual(expected_server['name'], created_server['name'])
|
||||
self.assertEqual(expected_server['adminPass'], admin_pass)
|
||||
self.assertEqual(expected_server['metadata'],
|
||||
created_server['metadata'])
|
||||
|
||||
self.os.nova.wait_for_server_status(created_server['id'],
|
||||
'ACTIVE',
|
||||
timeout=self.build_timeout)
|
||||
|
||||
server = self.os.nova.get_server(created_server['id'])
|
||||
|
||||
# Find IP of server
|
||||
try:
|
||||
(_, network) = server['addresses'].popitem()
|
||||
ip = network[0]['addr']
|
||||
except KeyError:
|
||||
self.fail("Failed to retrieve IP address from server entity")
|
||||
|
||||
# Assert password was set to that in request
|
||||
client = ssh.Client(ip, 'root', server_password, self.ssh_timeout)
|
||||
self.assertTrue(client.test_connection_auth())
|
||||
|
||||
self.os.nova.delete_server(server['id'])
|
||||
|
||||
def test_delete_server_building(self):
|
||||
"""Delete a server while building"""
|
||||
|
||||
# Make create server request
|
||||
server = {
|
||||
'name' : 'testserver',
|
||||
'imageRef' : self.image_ref,
|
||||
'flavorRef' : self.flavor_ref,
|
||||
}
|
||||
created_server = self.os.nova.create_server(server)
|
||||
|
||||
# Server should immediately be accessible, but in have building status
|
||||
server = self.os.nova.get_server(created_server['id'])
|
||||
self.assertEqual(server['status'], 'BUILD')
|
||||
|
||||
self.os.nova.delete_server(created_server['id'])
|
||||
|
||||
# Poll server until deleted
|
||||
try:
|
||||
url = '/servers/%s' % created_server['id']
|
||||
self.os.nova.poll_request_status('GET', url, 404)
|
||||
except exceptions.TimeoutException:
|
||||
self.fail("Server deletion timed out")
|
||||
|
||||
def test_delete_server_active(self):
|
||||
"""Delete a server after fully built"""
|
||||
|
||||
expected_server = {
|
||||
'name' : 'testserver',
|
||||
'imageRef' : self.image_ref,
|
||||
'flavorRef' : self.flavor_ref,
|
||||
}
|
||||
|
||||
created_server = self.os.nova.create_server(expected_server)
|
||||
server_id = created_server['id']
|
||||
|
||||
self.os.nova.wait_for_server_status(server_id,
|
||||
'ACTIVE',
|
||||
timeout=self.build_timeout)
|
||||
|
||||
self.os.nova.delete_server(server_id)
|
||||
|
||||
# Poll server until deleted
|
||||
try:
|
||||
url = '/servers/%s' % server_id
|
||||
self.os.nova.poll_request_status('GET', url, 404)
|
||||
except exceptions.TimeoutException:
|
||||
self.fail("Server deletion timed out")
|
||||
|
||||
def test_update_server_name(self):
|
||||
"""Change the name of a server"""
|
||||
|
||||
expected_server = {
|
||||
'name' : 'testserver',
|
||||
'imageRef' : self.image_ref,
|
||||
'flavorRef' : self.flavor_ref,
|
||||
}
|
||||
|
||||
created_server = self.os.nova.create_server(expected_server)
|
||||
|
||||
self.assertTrue(expected_server['name'], created_server['name'])
|
||||
server_id = created_server['id']
|
||||
|
||||
# Wait for it to be built
|
||||
self.os.nova.wait_for_server_status(server_id,
|
||||
'ACTIVE',
|
||||
timeout=self.build_timeout)
|
||||
|
||||
# Update name
|
||||
new_server = {'name': 'updatedtestserver'}
|
||||
put_body = json.dumps({
|
||||
'server': new_server,
|
||||
})
|
||||
url = '/servers/%s' % server_id
|
||||
resp, body = self.os.nova.request('PUT', url, body=put_body)
|
||||
|
||||
self.assertEqual(resp.status, 200)
|
||||
data = json.loads(body)
|
||||
self.assertEqual(data.keys(), ['server'])
|
||||
self._assert_server_entity(data['server'])
|
||||
self.assertEqual('updatedtestserver', data['server']['name'])
|
||||
|
||||
# Get Server information
|
||||
resp, body = self.os.nova.request('GET', '/servers/%s' % server_id)
|
||||
self.assertEqual(200, resp.status)
|
||||
data = json.loads(body)
|
||||
self.assertEqual(data.keys(), ['server'])
|
||||
self._assert_server_entity(data['server'])
|
||||
self.assertEqual('updatedtestserver', data['server']['name'])
|
||||
|
||||
self.os.nova.delete_server(server_id)
|
||||
|
||||
def test_create_server_invalid_image(self):
|
||||
"""Create a server with an unknown image"""
|
||||
|
||||
post_body = json.dumps({
|
||||
'server' : {
|
||||
'name' : 'testserver',
|
||||
'imageRef' : -1,
|
||||
'flavorRef' : self.flavor_ref,
|
||||
}
|
||||
})
|
||||
|
||||
resp, body = self.os.nova.request('POST', '/servers', body=post_body)
|
||||
|
||||
self.assertEqual(400, resp.status)
|
||||
|
||||
fault = json.loads(body)
|
||||
expected_fault = {
|
||||
"badRequest": {
|
||||
"message": "Cannot find requested image",
|
||||
"code": 400,
|
||||
},
|
||||
}
|
||||
# KNOWN-ISSUE - The error message is confusing and should be improved
|
||||
#self.assertEqual(fault, expected_fault)
|
||||
|
||||
def test_create_server_invalid_flavor(self):
|
||||
"""Create a server with an unknown flavor"""
|
||||
|
||||
post_body = json.dumps({
|
||||
'server' : {
|
||||
'name' : 'testserver',
|
||||
'imageRef' : self.image_ref,
|
||||
'flavorRef' : -1,
|
||||
}
|
||||
})
|
||||
|
||||
resp, body = self.os.nova.request('POST', '/servers', body=post_body)
|
||||
|
||||
self.assertEqual(400, resp.status)
|
||||
|
||||
fault = json.loads(body)
|
||||
expected_fault = {
|
||||
"badRequest": {
|
||||
"message": "Cannot find requested flavor",
|
||||
"code": 400,
|
||||
},
|
||||
}
|
||||
# KNOWN-ISSUE lp804084
|
||||
#self.assertEqual(fault, expected_fault)
|
||||
Reference in New Issue
Block a user