diff --git a/kong/tests/test_servers.py b/kong/tests/test_servers.py index f81f6a2ccf..0957327744 100644 --- a/kong/tests/test_servers.py +++ b/kong/tests/test_servers.py @@ -1,5 +1,6 @@ import base64 +import datetime import json import os @@ -7,6 +8,7 @@ from kong import openstack from kong import exceptions from kong import tests from kong.common import ssh +from kong.common import utils class ServersTest(tests.FunctionalTest): @@ -68,17 +70,16 @@ class ServersTest(tests.FunctionalTest): self.assertEqual(server['links'], expected_links) - def test_build_server(self): - """Build a server""" + def test_build_update_delete(self): + """Build and delete a server""" + + server_password = 'testpwd' expected_server = { 'name': 'testserver', - 'metadata': { - 'key1': 'value1', - 'key2': 'value2', - }, 'imageRef': self.image_ref, 'flavorRef': self.flavor_ref, + 'metadata': {'testEntry': 'testValue'}, } post_body = json.dumps({'server': expected_server}) @@ -86,28 +87,252 @@ class ServersTest(tests.FunctionalTest): '/servers', body=post_body) + # Ensure attributes were returned self.assertEqual(response.status, 202) - _body = json.loads(body) self.assertEqual(_body.keys(), ['server']) created_server = _body['server'] - self.server_id = created_server['id'] # for the tearDown - admin_pass = created_server.pop('adminPass') self._assert_server_entity(created_server) self.assertEqual(expected_server['name'], created_server['name']) + self.assertEqual(created_server['accessIPv4'], '') + self.assertEqual(created_server['accessIPv6'], '') self.assertEqual(expected_server['metadata'], created_server['metadata']) + self.server_id = created_server['id'] - self.os.nova.wait_for_server_status(created_server['id'], + # Get server again and ensure attributes stuck + server = self.os.nova.get_server(self.server_id) + self._assert_server_entity(server) + self.assertEqual(server['name'], expected_server['name']) + self.assertEqual(server['accessIPv4'], '') + self.assertEqual(server['accessIPv6'], '') + self.assertEqual(server['metadata'], created_server['metadata']) + + # Parse last-updated time + update_time = utils.load_isotime(server['created']) + + # Ensure server not returned with future changes-since + future_time = utils.dump_isotime(update_time + datetime.timedelta(100)) + params = 'changes-since=%s' % future_time + response, body = self.os.nova.request('GET', '/servers?%s' % params) + servers = json.loads(body)['servers'] + self.assertTrue(len(servers) == 0) + + # Ensure server is returned with past changes-since + future_time = utils.dump_isotime(update_time - datetime.timedelta(1)) + params = 'changes-since=%s' % future_time + response, body = self.os.nova.request('GET', '/servers?%s' % params) + servers = json.loads(body)['servers'] + server_ids = map(lambda x: x['id'], servers) + self.assertTrue(self.server_id in server_ids) + + # Update name + new_name = 'testserver2' + new_server = {'name': new_name} + put_body = json.dumps({'server': new_server}) + url = '/servers/%s' % self.server_id + resp, body = self.os.nova.request('PUT', url, body=put_body) + + # Output from update should be a full server + self.assertEqual(resp.status, 200) + data = json.loads(body) + self.assertEqual(data.keys(), ['server']) + self._assert_server_entity(data['server']) + self.assertEqual(new_name, data['server']['name']) + + # Check that name was changed + updated_server = self.os.nova.get_server(self.server_id) + self._assert_server_entity(updated_server) + self.assertEqual(new_name, updated_server['name']) + + # Update accessIPv4 + new_server = {'accessIPv4': '192.168.0.200'} + put_body = json.dumps({'server': new_server}) + url = '/servers/%s' % self.server_id + resp, body = self.os.nova.request('PUT', url, body=put_body) + + # Output from update should be a full server + self.assertEqual(resp.status, 200) + data = json.loads(body) + self.assertEqual(data.keys(), ['server']) + self._assert_server_entity(data['server']) + self.assertEqual('192.168.0.200', data['server']['accessIPv4']) + + # Check that accessIPv4 was changed + updated_server = self.os.nova.get_server(self.server_id) + self._assert_server_entity(updated_server) + self.assertEqual('192.168.0.200', updated_server['accessIPv4']) + + # Update accessIPv6 + new_server = {'accessIPv6': 'feed::beef'} + put_body = json.dumps({'server': new_server}) + url = '/servers/%s' % self.server_id + resp, body = self.os.nova.request('PUT', url, body=put_body) + + # Output from update should be a full server + self.assertEqual(resp.status, 200) + data = json.loads(body) + self.assertEqual(data.keys(), ['server']) + self._assert_server_entity(data['server']) + self.assertEqual('feed::beef', data['server']['accessIPv6']) + + # Check that accessIPv6 was changed + updated_server = self.os.nova.get_server(self.server_id) + self._assert_server_entity(updated_server) + self.assertEqual('feed::beef', updated_server['accessIPv6']) + + # Check metadata subresource + 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) + + # Ensure metadata container can be modified + expected = { + 'metadata': { + 'new_meta1': 'new_value1', + 'new_meta2': 'new_value2', + }, + } + post_body = json.dumps(expected) + url = '/servers/%s/metadata' % self.server_id + response, body = self.os.nova.request('POST', url, body=post_body) + self.assertEqual(200, response.status) + result = json.loads(body) + expected['metadata']['testEntry'] = 'testValue' + self.assertEqual(expected, result) + + # Ensure values stick + url = '/servers/%s/metadata' % self.server_id + response, body = self.os.nova.request('GET', url) + self.assertEqual(200, response.status) + result = json.loads(body) + self.assertEqual(expected, result) + + # Ensure metadata container can be overwritten + expected = { + 'metadata': { + 'new_meta3': 'new_value3', + 'new_meta4': 'new_value4', + }, + } + 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) + result = json.loads(body) + self.assertEqual(expected, result) + + # Ensure values stick + url = '/servers/%s/metadata' % self.server_id + response, body = self.os.nova.request('GET', url) + self.assertEqual(200, response.status) + result = json.loads(body) + self.assertEqual(expected, result) + + # Set specific key + expected_meta = {'meta': {'new_meta5': 'new_value5'}} + put_body = json.dumps(expected_meta) + url = '/servers/%s/metadata/new_meta5' % 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) + + # Ensure value sticks + expected_metadata = { + 'metadata': { + 'new_meta3': 'new_value3', + 'new_meta4': 'new_value4', + 'new_meta5': 'new_value5', + }, + } + url = '/servers/%s/metadata' % self.server_id + response, body = self.os.nova.request('GET', url) + result = json.loads(body) + self.assertDictEqual(expected_metadata, result) + + # Update existing key + expected_meta = {'meta': {'new_meta4': 'new_value6'}} + put_body = json.dumps(expected_meta) + url = '/servers/%s/metadata/new_meta4' % 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) + + # Ensure value sticks + expected_metadata = { + 'metadata': { + 'new_meta3': 'new_value3', + 'new_meta4': 'new_value6', + 'new_meta5': 'new_value5', + }, + } + url = '/servers/%s/metadata' % self.server_id + response, body = self.os.nova.request('GET', url) + result = json.loads(body) + self.assertDictEqual(expected_metadata, result) + + # Delete a certain key + url = '/servers/%s/metadata/new_meta3' % self.server_id + response, body = self.os.nova.request('DELETE', url) + self.assertEquals(204, response.status) + + # Make sure the key is gone + url = '/servers/%s/metadata/new_meta3' % self.server_id + response, body = self.os.nova.request('GET', url) + self.assertEquals(404, response.status) + + # Delete a nonexistant key + url = '/servers/%s/metadata/new_meta3' % self.server_id + response, body = self.os.nova.request('DELETE', url) + self.assertEquals(404, response.status) + + # Wait for instance to boot + self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE', timeout=self.build_timeout) - server = self.os.nova.get_server(created_server['id']) + # Look for 'addresses' attribute on server + 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'] + + # Addresses should be available from subresource + 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'] + + # Ensure both resources return identical information + self.assertEqual(server_addresses, ips_addresses) + + # Validate entities within network containers + for (network, network_data) in ips_addresses.items(): + 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) + + # Check each IP entity + for ip_data in network_data: + self.assertEqual(set(ip_data.keys()), set(['addr', 'version'])) # Find IP of server try: - (_, network) = server['addresses'].popitem() + (_, network) = server_addresses.items()[0] ip = network[0]['addr'] except KeyError: self.fail("Failed to retrieve IP address from server entity") @@ -116,10 +341,19 @@ class ServersTest(tests.FunctionalTest): if int(self.nova['ssh_timeout']) > 0: client = ssh.Client(ip, 'root', admin_pass, self.ssh_timeout) self.assertTrue(client.test_connection_auth()) - test_build_server.tags = ['nova', 'glance'] - def test_build_server_with_file(self): - """Build a server with an injected file""" + self.os.nova.delete_server(self.server_id) + + # Poll server until deleted + try: + url = '/servers/%s' % self.server_id + self.os.nova.poll_request_status('GET', url, 404) + except exceptions.TimeoutException: + self.fail("Server deletion timed out") + test_build_update_delete.tags = ['nova'] + + def test_build_with_password_and_file(self): + """Build a server with a custom password and an injected file""" file_contents = 'testing' @@ -137,6 +371,7 @@ class ServersTest(tests.FunctionalTest): ], 'imageRef': self.image_ref, 'flavorRef': self.flavor_ref, + 'adminPass': 'secrete', } post_body = json.dumps({'server': expected_server}) @@ -152,6 +387,7 @@ class ServersTest(tests.FunctionalTest): self.server_id = _body['server']['id'] admin_pass = created_server.pop('adminPass', None) + self.assertEqual(expected_server['adminPass'], admin_pass) self._assert_server_entity(created_server) self.assertEqual(expected_server['name'], created_server['name']) self.assertEqual(expected_server['metadata'], @@ -175,62 +411,9 @@ class ServersTest(tests.FunctionalTest): 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) - test_build_server_with_file.tags = ['nova', 'glance'] + test_build_with_password_and_file.tags = ['nova'] - 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 ( if ssh_timeout is > 0 - if int(self.nova['ssh_timeout']) > 0: - client = ssh.Client(ip, 'root', server_password, self.ssh_timeout) - self.assertTrue(client.test_connection_auth()) - test_build_server_with_password.tags = ['nova', 'glance'] - - def test_delete_server_building(self): + def test_delete_while_building(self): """Delete a server while building""" # Make create server request @@ -253,79 +436,9 @@ class ServersTest(tests.FunctionalTest): self.os.nova.poll_request_status('GET', url, 404) except exceptions.TimeoutException: self.fail("Server deletion timed out") - test_delete_server_building.tags = ['nova', 'glance'] + test_delete_while_building.tags = ['nova'] - 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") - test_delete_server_active.tags = ['nova', 'glance'] - - 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) - test_update_server_name.tags = ['nova', 'glance'] - - def test_create_server_invalid_image(self): + def test_create_with_invalid_image(self): """Create a server with an unknown image""" post_body = json.dumps({ @@ -349,9 +462,9 @@ class ServersTest(tests.FunctionalTest): } # KNOWN-ISSUE - The error message is confusing and should be improved #self.assertEqual(fault, expected_fault) - test_create_server_invalid_image.tags = ['nova', 'glance'] + test_create_with_invalid_image.tags = ['nova'] - def test_create_server_invalid_flavor(self): + def test_create_with_invalid_flavor(self): """Create a server with an unknown flavor""" post_body = json.dumps({ @@ -375,4 +488,4 @@ class ServersTest(tests.FunctionalTest): } # KNOWN-ISSUE lp804084 #self.assertEqual(fault, expected_fault) - test_create_server_invalid_flavor.tags = ['nova', 'glance'] + test_create_with_invalid_flavor.tags = ['nova']