Import all the stacktester stuff as-is (s/stacktester/kong/, though).
diff --git a/kong/tests/test_servers.py b/kong/tests/test_servers.py
new file mode 100644
index 0000000..c16b514
--- /dev/null
+++ b/kong/tests/test_servers.py
@@ -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)