summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustin Santa Barbara <justin@fathomdb.com>2011-03-24 01:13:20 -0700
committerJustin Santa Barbara <justin@fathomdb.com>2011-03-24 01:13:20 -0700
commit694c2cfd2afdc0ed293f205890bda977968dc079 (patch)
treea750c989e2a1daa97cf022234f34a59cc8bb19a6
parent143a8387fcbec34fd919e322a3fed7e9505c8a7c (diff)
Created simple test case for server creation, so that we can have something to attach to...
-rw-r--r--.bzrignore3
-rw-r--r--nova/image/fake.py109
-rw-r--r--nova/tests/integrated/integrated_helpers.py22
-rw-r--r--nova/tests/integrated/test_servers.py218
4 files changed, 352 insertions, 0 deletions
diff --git a/.bzrignore b/.bzrignore
index f10df621d..b751ad825 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -16,3 +16,6 @@ nova/vcsversion.py
*.DS_Store
.project
.pydevproject
+clean.sqlite
+run_tests.log
+tests.sqlite
diff --git a/nova/image/fake.py b/nova/image/fake.py
new file mode 100644
index 000000000..bc63780d3
--- /dev/null
+++ b/nova/image/fake.py
@@ -0,0 +1,109 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Justin Santa Barbara
+# 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.
+"""Implementation of an fake image service"""
+
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova.image import service
+
+
+LOG = logging.getLogger('nova.image.fake')
+
+FLAGS = flags.FLAGS
+
+
+class MockImageService(service.BaseImageService):
+ """Mock (fake) image service for unit testing"""
+
+ def __init__(self):
+ self.images = {}
+ # NOTE(justinsb): The OpenStack API can't upload an image???
+ # So, make sure we've got one..
+ image = {'id': '123456',
+ 'status': 'active',
+ 'type': 'machine',
+ 'disk_format': 'ami',
+ 'properties': {'kernel_id': FLAGS.null_kernel,
+ 'ramdisk_id': FLAGS.null_kernel,
+ }
+ }
+ self.create(None, image)
+ super(MockImageService, self).__init__()
+
+ def index(self, context):
+ """Returns list of images"""
+ return self.images.values()
+
+ def detail(self, context):
+ """Return list of detailed image information"""
+ return self.images.values()
+
+ def show(self, context, image_id):
+ """
+ Returns a dict containing image data for the given opaque image id.
+ """
+ image_id = int(image_id)
+ image = self.images.get(image_id)
+ if image:
+ return image
+ LOG.warn("Unable to find image id %s. Have images: %s",
+ image_id, self.images)
+ raise exception.NotFound
+
+ def create(self, context, data):
+ """
+ Store the image data and return the new image id.
+
+ :raises AlreadyExists if the image already exist.
+
+ """
+ image_id = int(data['id'])
+ if self.images.get(image_id):
+ #TODO(justinsb): Where is this AlreadyExists exception??
+ raise exception.Error("AlreadyExists")
+
+ self.images[image_id] = data
+
+ def update(self, context, image_id, data):
+ """Replace the contents of the given image with the new data.
+
+ :raises NotFound if the image does not exist.
+
+ """
+ image_id = int(image_id)
+ if not self.images.get(image_id):
+ raise exception.NotFound
+ self.images[image_id] = data
+
+ def delete(self, context, image_id):
+ """
+ Delete the given image.
+
+ :raises NotFound if the image does not exist.
+
+ """
+ image_id = int(image_id)
+ removed = self.images.pop(image_id, None)
+ if not removed:
+ raise exception.NotFound
+
+ def delete_all(self):
+ """
+ Clears out all images
+ """
+ self.images.clear()
diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py
index 47093636e..dc6897e08 100644
--- a/nova/tests/integrated/integrated_helpers.py
+++ b/nova/tests/integrated/integrated_helpers.py
@@ -73,6 +73,28 @@ class TestUser(object):
self.secret,
self.auth_url)
+ def get_unused_server_name(self):
+ servers = self.openstack_api.get_servers()
+ server_names = [server['name'] for server in servers]
+ return generate_new_element(server_names, 'server')
+
+ def get_invalid_image(self):
+ images = self.openstack_api.get_images()
+ image_ids = [image['id'] for image in images]
+ return generate_new_element(image_ids, '', numeric=True)
+
+ def get_valid_image(self, create=False):
+ images = self.openstack_api.get_images()
+ if create and not images:
+ # TODO(justinsb): No way to create an image through API???
+ #created_image = self.openstack_api.post_image(image)
+ #images.append(created_image)
+ raise exception.Error("No way to create an image through API??")
+
+ if images:
+ return images[0]
+ return None
+
class IntegratedUnitTestContext(object):
__INSTANCE = None
diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py
new file mode 100644
index 000000000..3c38295c5
--- /dev/null
+++ b/nova/tests/integrated/test_servers.py
@@ -0,0 +1,218 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Justin Santa Barbara
+# 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 time
+import unittest
+
+from nova import flags
+from nova import test
+from nova.log import logging
+from nova.tests.integrated import integrated_helpers
+from nova.tests.integrated.api import client
+
+
+LOG = logging.getLogger('nova.tests.integrated')
+
+FLAGS = flags.FLAGS
+FLAGS.verbose = True
+
+
+class ServersTest(test.TestCase):
+ def setUp(self):
+ super(ServersTest, self).setUp()
+
+ self.flags(image_service='nova.image.fake.MockImageService')
+
+ context = integrated_helpers.IntegratedUnitTestContext.startup()
+ self.user = context.test_user
+ self.api = self.user.openstack_api
+
+ def tearDown(self):
+ integrated_helpers.IntegratedUnitTestContext.shutdown()
+ super(ServersTest, self).tearDown()
+
+ def test_get_servers(self):
+ """Simple check that listing servers works."""
+ servers = self.api.get_servers()
+ for server in servers:
+ LOG.debug("server: %s" % server)
+
+ def test_create_and_delete_server(self):
+ """Creates and deletes a server"""
+
+ # Create server
+
+ # Build the server data gradually, checking errors along the way
+ server = {}
+ good_server = self._build_minimal_create_server_request()
+
+ post = {'server': server}
+
+ # Without an imageId, this throws 500.
+ # TODO(justinsb): Check whatever the spec says should be thrown here
+ self.assertRaises(client.OpenStackApiException,
+ self.api.post_server, post)
+
+ # With an invalid imageId, this throws 500.
+ server['imageId'] = self.user.get_invalid_image()
+ # TODO(justinsb): Check whatever the spec says should be thrown here
+ self.assertRaises(client.OpenStackApiException,
+ self.api.post_server, post)
+
+ # Add a valid imageId
+ server['imageId'] = good_server['imageId']
+
+ # Without flavorId, this throws 500
+ # TODO(justinsb): Check whatever the spec says should be thrown here
+ self.assertRaises(client.OpenStackApiException,
+ self.api.post_server, post)
+
+ # Set a valid flavorId
+ server['flavorId'] = good_server['flavorId']
+
+ # Without a name, this throws 500
+ # TODO(justinsb): Check whatever the spec says should be thrown here
+ self.assertRaises(client.OpenStackApiException,
+ self.api.post_server, post)
+
+ # Set a valid server name
+ server['name'] = good_server['name']
+
+ created_server = self.api.post_server(post)
+ LOG.debug("created_server: %s" % created_server)
+ self.assertTrue(created_server['id'])
+ created_server_id = created_server['id']
+
+ # Check it's there
+ found_server = self.api.get_server(created_server_id)
+ self.assertEqual(created_server_id, found_server['id'])
+
+ # It should also be in the all-servers list
+ servers = self.api.get_servers()
+ server_ids = [server['id'] for server in servers]
+ self.assertTrue(created_server_id in server_ids)
+
+ # Wait (briefly) for creation
+ retries = 0
+ while found_server['status'] == 'build':
+ LOG.debug("found server: %s" % found_server)
+ time.sleep(1)
+ found_server = self.api.get_server(created_server_id)
+ retries = retries + 1
+ if retries > 5:
+ break
+
+ # It should be available...
+ # TODO(justinsb): Mock doesn't yet do this...
+ #self.assertEqual('available', found_server['status'])
+
+ self._delete_server(created_server_id)
+
+ def _delete_server(self, server_id):
+ # Delete the server
+ self.api.delete_server(server_id)
+
+ # Wait (briefly) for deletion
+ for _retries in range(5):
+ try:
+ found_server = self.api.get_server(server_id)
+ except client.OpenStackApiNotFoundException:
+ found_server = None
+ LOG.debug("Got 404, proceeding")
+ break
+
+ LOG.debug("Found_server=%s" % found_server)
+
+ # TODO(justinsb): Mock doesn't yet do accurate state changes
+ #if found_server['status'] != 'deleting':
+ # break
+ time.sleep(1)
+
+ # Should be gone
+ self.assertFalse(found_server)
+
+ def _build_minimal_create_server_request(self):
+ server = {}
+
+ image = self.user.get_valid_image(create=True)
+ image_id = image['id']
+
+ #TODO(justinsb): This is FUBAR
+ image_id = abs(hash(image_id))
+
+ # We now have a valid imageId
+ server['imageId'] = image_id
+
+ # Set a valid flavorId
+ flavor = self.api.get_flavors()[0]
+ LOG.debug("Using flavor: %s" % flavor)
+ server['flavorId'] = flavor['id']
+
+ # Set a valid server name
+ server_name = self.user.get_unused_server_name()
+ server['name'] = server_name
+
+ return server
+
+# TODO(justinsb): Enable this unit test when the metadata bug is fixed
+# def test_create_server_with_metadata(self):
+# """Creates a server with metadata"""
+#
+# # Build the server data gradually, checking errors along the way
+# server = self._build_minimal_create_server_request()
+#
+# for metadata_count in range(30):
+# metadata = {}
+# for i in range(metadata_count):
+# metadata['key_%s' % i] = 'value_%s' % i
+# server['metadata'] = metadata
+#
+# post = {'server': server}
+# created_server = self.api.post_server(post)
+# LOG.debug("created_server: %s" % created_server)
+# self.assertTrue(created_server['id'])
+# created_server_id = created_server['id']
+# # Reenable when bug fixed
+# # self.assertEqual(metadata, created_server.get('metadata'))
+#
+# # Check it's there
+# found_server = self.api.get_server(created_server_id)
+# self.assertEqual(created_server_id, found_server['id'])
+# self.assertEqual(metadata, found_server.get('metadata'))
+#
+# # The server should also be in the all-servers details list
+# servers = self.api.get_servers(detail=True)
+# server_map = dict((server['id'], server) for server in servers)
+# found_server = server_map.get(created_server_id)
+# self.assertTrue(found_server)
+# # Details do include metadata
+# self.assertEqual(metadata, found_server.get('metadata'))
+#
+# # The server should also be in the all-servers summary list
+# servers = self.api.get_servers(detail=False)
+# server_map = dict((server['id'], server) for server in servers)
+# found_server = server_map.get(created_server_id)
+# self.assertTrue(found_server)
+# # Summary should not include metadata
+# self.assertFalse(found_server.get('metadata'))
+#
+# # Cleanup
+# self._delete_server(created_server_id)
+
+
+if __name__ == "__main__":
+ unittest.main()