summaryrefslogtreecommitdiffstats
path: root/nova/tests
diff options
context:
space:
mode:
authorJesse Andrews <anotherjesse@gmail.com>2010-05-27 23:05:26 -0700
committerJesse Andrews <anotherjesse@gmail.com>2010-05-27 23:05:26 -0700
commitbf6e6e718cdc7488e2da87b21e258ccc065fe499 (patch)
tree51cf4f72047eb6b16079c7fe21e9822895541801 /nova/tests
downloadnova-bf6e6e718cdc7488e2da87b21e258ccc065fe499.tar.gz
nova-bf6e6e718cdc7488e2da87b21e258ccc065fe499.tar.xz
nova-bf6e6e718cdc7488e2da87b21e258ccc065fe499.zip
initial commit
Diffstat (limited to 'nova/tests')
-rw-r--r--nova/tests/CA/cacert.pem17
-rw-r--r--nova/tests/CA/private/cakey.pem15
-rw-r--r--nova/tests/__init__.py27
-rw-r--r--nova/tests/access_unittest.py60
-rw-r--r--nova/tests/api_integration.py50
-rw-r--r--nova/tests/api_unittest.py189
-rw-r--r--nova/tests/bundle/1mb.manifest.xml1
-rw-r--r--nova/tests/bundle/1mb.part.0bin0 -> 1024 bytes
-rw-r--r--nova/tests/bundle/1mb.part.11
-rw-r--r--nova/tests/cloud_unittest.py161
-rw-r--r--nova/tests/datastore_unittest.py60
-rw-r--r--nova/tests/fake_flags.py26
-rw-r--r--nova/tests/future_unittest.py74
-rw-r--r--nova/tests/keeper_unittest.py57
-rw-r--r--nova/tests/network_unittest.py113
-rw-r--r--nova/tests/node_unittest.py128
-rw-r--r--nova/tests/objectstore_unittest.py190
-rw-r--r--nova/tests/real_flags.py24
-rw-r--r--nova/tests/storage_unittest.py86
-rw-r--r--nova/tests/users_unittest.py137
20 files changed, 1416 insertions, 0 deletions
diff --git a/nova/tests/CA/cacert.pem b/nova/tests/CA/cacert.pem
new file mode 100644
index 000000000..9ffb5bb80
--- /dev/null
+++ b/nova/tests/CA/cacert.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyzCCAjSgAwIBAgIJANiqHZUcbScCMA0GCSqGSIb3DQEBBAUAME4xEjAQBgNV
+BAoTCU5PVkEgUk9PVDEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEGA1UECBMK
+Q2FsaWZvcm5pYTELMAkGA1UEBhMCVVMwHhcNMTAwNTI4MDExOTI1WhcNMTEwNTI4
+MDExOTI1WjBOMRIwEAYDVQQKEwlOT1ZBIFJPT1QxFjAUBgNVBAcTDU1vdW50YWlu
+IFZpZXcxEzARBgNVBAgTCkNhbGlmb3JuaWExCzAJBgNVBAYTAlVTMIGfMA0GCSqG
+SIb3DQEBAQUAA4GNADCBiQKBgQDobUnq8rpXA/HQZ2Uu9Me3SlqCayz3ws2wtvFQ
+koWPUzpriIYPkpprz2EaVu07Zb9uJHvjcoY07nYntl4jR8S7PH4XZhlVFn8AQWzs
+iThU4KJF71UfVM00dDrarSgVpyOIcFXO3iUvLoJj7+RUPjrWdLuJoMqnhicgLeHZ
+LAZ8ewIDAQABo4GwMIGtMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFMh1RMlTVtt8
+EdESYpsTU08r0FnpMH4GA1UdIwR3MHWAFMh1RMlTVtt8EdESYpsTU08r0FnpoVKk
+UDBOMRIwEAYDVQQKEwlOT1ZBIFJPT1QxFjAUBgNVBAcTDU1vdW50YWluIFZpZXcx
+EzARBgNVBAgTCkNhbGlmb3JuaWExCzAJBgNVBAYTAlVTggkA2KodlRxtJwIwDQYJ
+KoZIhvcNAQEEBQADgYEAq+YCgflK36HCdodNu2ya3O6UDRUE2dW8n96tAOmvHqmR
+v38k8GIW0pjWDo+lZYnFmeJYd+QGcJl9fLzXxffV5k+rNCfr/gEYtznWLNUX7AZB
+b/VC7L+yK9qz08C8n51TslXaf3fUGkfkQxsvEP7+hi0qavdd/8eTbdheWahYwWg=
+-----END CERTIFICATE-----
diff --git a/nova/tests/CA/private/cakey.pem b/nova/tests/CA/private/cakey.pem
new file mode 100644
index 000000000..eee54cc38
--- /dev/null
+++ b/nova/tests/CA/private/cakey.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDobUnq8rpXA/HQZ2Uu9Me3SlqCayz3ws2wtvFQkoWPUzpriIYP
+kpprz2EaVu07Zb9uJHvjcoY07nYntl4jR8S7PH4XZhlVFn8AQWzsiThU4KJF71Uf
+VM00dDrarSgVpyOIcFXO3iUvLoJj7+RUPjrWdLuJoMqnhicgLeHZLAZ8ewIDAQAB
+AoGBANQonmZ2Nh2jniFrn/LiwULP/ho6Fov6J6N8+n1focaYZCUwM58XZRmv7KUM
+X/PuBnVVnDibm2HJodTSJM/zfODnGO15kdmJ9X23FkkdTyuvphO5tYF0ONARXdfX
+9LbPcLYA14VSCZCKCye6mbv/xi0C/s7q6ZBoMl7XaeD9hgUxAkEA9lxQY/ZxcLV0
+Ae5I2spBbtuXEGns11YnKnppc59RrAono1gaDeYY2WZRwztIcD6VtUv7qkzH6ubo
+shAG4fvnPQJBAPGFaDODs2ckPvxnILEbjpnZXGQqDCpQ3sVJ6nfu+qdAWS92ESNo
+Y6DC8zFjFaQFbKy6Jxr1VsvYDXhF8cmy7hcCQHkLElSLGWGPRdhNA268QTn+mlJu
+OPf0VHoCex1cAfzNYHxZJTP/AeaO501NK2I63cOd+aDK6M75dQtH5JnT8uECQQCg
+jVydkhk6oV+1jiCvW3BKWbIPa9w2bRgJ8n8JRzYc5Kvk3wm5jfVcsvvTgtip9mkt
+0XmZdCpEy9T4dRasTGP1AkBMhShiVP7+P+SIQlZtSn8ckTt9G6cefEjxsv0kVFZe
+SjkUO0ZifahF8r3Q1eEUSzdXEvicEwONvcpc7MLwfSD7
+-----END RSA PRIVATE KEY-----
diff --git a/nova/tests/__init__.py b/nova/tests/__init__.py
new file mode 100644
index 000000000..a4ccbbaeb
--- /dev/null
+++ b/nova/tests/__init__.py
@@ -0,0 +1,27 @@
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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:`nova.tests` -- Nova Unittests
+=====================================================
+
+.. automodule:: nova.tests
+ :platform: Unix
+.. moduleauthor:: Jesse Andrews <jesse@ansolabs.com>
+.. moduleauthor:: Devin Carlen <devin.carlen@gmail.com>
+.. moduleauthor:: Vishvananda Ishaya <vishvananda@yahoo.com>
+.. moduleauthor:: Joshua McKenty <joshua@cognition.ca>
+.. moduleauthor:: Manish Singh <yosh@gimp.org>
+.. moduleauthor:: Andy Smith <andy@anarkystic.com>
+""" \ No newline at end of file
diff --git a/nova/tests/access_unittest.py b/nova/tests/access_unittest.py
new file mode 100644
index 000000000..ab0759c2d
--- /dev/null
+++ b/nova/tests/access_unittest.py
@@ -0,0 +1,60 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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 logging
+import os
+import unittest
+
+from nova import flags
+from nova import test
+from nova.auth import users
+from nova.endpoint import cloud
+
+FLAGS = flags.FLAGS
+
+class AccessTestCase(test.BaseTestCase):
+ def setUp(self):
+ FLAGS.fake_libvirt = True
+ FLAGS.fake_storage = True
+ self.users = users.UserManager.instance()
+ super(AccessTestCase, self).setUp()
+ # Make a test project
+ # Make a test user
+ self.users.create_user('test1', 'access', 'secret')
+
+ # Make the test user a member of the project
+
+ def tearDown(self):
+ # Delete the test user
+ # Delete the test project
+ self.users.delete_user('test1')
+ pass
+
+ def test_001_basic_user_access(self):
+ user = self.users.get_user('test1')
+ # instance-foo, should be using object and not owner_id
+ instance_id = "i-12345678"
+ self.assertTrue(user.is_authorized(instance_id, action="describe_instances"))
+
+ def test_002_sysadmin_access(self):
+ user = self.users.get_user('test1')
+ bucket = "foo/bar/image"
+ self.assertFalse(user.is_authorized(bucket, action="register"))
+ self.users.add_role(user, "sysadmin")
+
+
+if __name__ == "__main__":
+ # TODO: Implement use_fake as an option
+ unittest.main()
diff --git a/nova/tests/api_integration.py b/nova/tests/api_integration.py
new file mode 100644
index 000000000..d2e1026b8
--- /dev/null
+++ b/nova/tests/api_integration.py
@@ -0,0 +1,50 @@
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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 unittest
+
+import boto
+from boto.ec2.regioninfo import RegionInfo
+
+ACCESS_KEY = 'fake'
+SECRET_KEY = 'fake'
+CLC_IP = '127.0.0.1'
+CLC_PORT = 8773
+REGION = 'test'
+
+def get_connection():
+ return boto.connect_ec2 (
+ aws_access_key_id=ACCESS_KEY,
+ aws_secret_access_key=SECRET_KEY,
+ is_secure=False,
+ region=RegionInfo(None, REGION, CLC_IP),
+ port=CLC_PORT,
+ path='/services/Cloud',
+ debug=99
+ )
+
+class APIIntegrationTests(unittest.TestCase):
+ def test_001_get_all_images(self):
+ conn = get_connection()
+ res = conn.get_all_images()
+ print res
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+#print conn.get_all_key_pairs()
+#print conn.create_key_pair
+#print conn.create_security_group('name', 'description')
+
diff --git a/nova/tests/api_unittest.py b/nova/tests/api_unittest.py
new file mode 100644
index 000000000..fdbf088f9
--- /dev/null
+++ b/nova/tests/api_unittest.py
@@ -0,0 +1,189 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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 httplib
+import random
+import StringIO
+
+from nova import vendor
+import boto
+from boto.ec2 import regioninfo
+from tornado import httpserver
+from twisted.internet import defer
+
+from nova import flags
+from nova import test
+from nova.auth import users
+from nova.endpoint import api
+from nova.endpoint import cloud
+
+
+FLAGS = flags.FLAGS
+
+
+# NOTE(termie): These are a bunch of helper methods and classes to short
+# circuit boto calls and feed them into our tornado handlers,
+# it's pretty damn circuitous so apologies if you have to fix
+# a bug in it
+def boto_to_tornado(method, path, headers, data, host, connection=None):
+ """ translate boto requests into tornado requests
+
+ connection should be a FakeTornadoHttpConnection instance
+ """
+ headers = httpserver.HTTPHeaders()
+ for k, v in headers.iteritems():
+ headers[k] = v
+
+ req = httpserver.HTTPRequest(method=method,
+ uri=path,
+ headers=headers,
+ body=data,
+ host=host,
+ remote_ip='127.0.0.1',
+ connection=connection)
+ return req
+
+
+def raw_to_httpresponse(s):
+ """ translate a raw tornado http response into an httplib.HTTPResponse """
+ sock = FakeHttplibSocket(s)
+ resp = httplib.HTTPResponse(sock)
+ resp.begin()
+ return resp
+
+
+class FakeHttplibSocket(object):
+ """ a fake socket implementation for httplib.HTTPResponse, trivial """
+ def __init__(self, s):
+ self.fp = StringIO.StringIO(s)
+
+ def makefile(self, mode, other):
+ return self.fp
+
+
+class FakeTornadoStream(object):
+ """ a fake stream to satisfy tornado's assumptions, trivial """
+ def set_close_callback(self, f):
+ pass
+
+
+class FakeTornadoConnection(object):
+ """ a fake connection object for tornado to pass to its handlers
+
+ web requests are expected to write to this as they get data and call
+ finish when they are done with the request, we buffer the writes and
+ kick off a callback when it is done so that we can feed the result back
+ into boto.
+ """
+ def __init__(self, d):
+ self.d = d
+ self._buffer = StringIO.StringIO()
+
+ def write(self, chunk):
+ self._buffer.write(chunk)
+
+ def finish(self):
+ s = self._buffer.getvalue()
+ self.d.callback(s)
+
+ xheaders = None
+
+ @property
+ def stream(self):
+ return FakeTornadoStream()
+
+
+class FakeHttplibConnection(object):
+ """ a fake httplib.HTTPConnection for boto to use
+
+ requests made via this connection actually get translated and routed into
+ our tornado app, we then wait for the response and turn it back into
+ the httplib.HTTPResponse that boto expects.
+ """
+ def __init__(self, app, host, is_secure=False):
+ self.app = app
+ self.host = host
+ self.deferred = defer.Deferred()
+
+ def request(self, method, path, data, headers):
+ req = boto_to_tornado
+ conn = FakeTornadoConnection(self.deferred)
+ request = boto_to_tornado(connection=conn,
+ method=method,
+ path=path,
+ headers=headers,
+ data=data,
+ host=self.host)
+ handler = self.app(request)
+ self.deferred.addCallback(raw_to_httpresponse)
+
+ def getresponse(self):
+ @defer.inlineCallbacks
+ def _waiter():
+ result = yield self.deferred
+ defer.returnValue(result)
+ d = _waiter()
+ # NOTE(termie): defer.returnValue above should ensure that
+ # this deferred has already been called by the time
+ # we get here, we are going to cheat and return
+ # the result of the callback
+ return d.result
+
+ def close(self):
+ pass
+
+
+class ApiEc2TestCase(test.BaseTestCase):
+ def setUp(self):
+ super(ApiEc2TestCase, self).setUp()
+
+ self.users = users.UserManager.instance()
+ self.cloud = cloud.CloudController()
+
+ self.host = '127.0.0.1'
+
+ self.app = api.APIServerApplication(self.users, {'Cloud': self.cloud})
+ self.ec2 = boto.connect_ec2(
+ aws_access_key_id='fake',
+ aws_secret_access_key='fake',
+ is_secure=False,
+ region=regioninfo.RegionInfo(None, 'test', self.host),
+ port=FLAGS.cc_port,
+ path='/services/Cloud')
+
+ self.mox.StubOutWithMock(self.ec2, 'new_http_connection')
+
+ def expect_http(self, host=None, is_secure=False):
+ http = FakeHttplibConnection(
+ self.app, '%s:%d' % (self.host, FLAGS.cc_port), False)
+ self.ec2.new_http_connection(host, is_secure).AndReturn(http)
+ return http
+
+ def test_describe_instances(self):
+ self.expect_http()
+ self.mox.ReplayAll()
+
+ self.assertEqual(self.ec2.get_all_instances(), [])
+
+
+ def test_get_all_key_pairs(self):
+ self.expect_http()
+ self.mox.ReplayAll()
+ keyname = "".join(random.choice("sdiuisudfsdcnpaqwertasd") for x in range(random.randint(4, 8)))
+ self.users.generate_key_pair('fake', keyname)
+
+ rv = self.ec2.get_all_key_pairs()
+ self.assertTrue(filter(lambda k: k.name == keyname, rv))
+
diff --git a/nova/tests/bundle/1mb.manifest.xml b/nova/tests/bundle/1mb.manifest.xml
new file mode 100644
index 000000000..dc3315957
--- /dev/null
+++ b/nova/tests/bundle/1mb.manifest.xml
@@ -0,0 +1 @@
+<?xml version="1.0" ?><manifest><version>2007-10-10</version><bundler><name>euca-tools</name><version>1.2</version><release>31337</release></bundler><machine_configuration><architecture>x86_64</architecture></machine_configuration><image><name>1mb</name><user>42</user><type>machine</type><digest algorithm="SHA1">da39a3ee5e6b4b0d3255bfef95601890afd80709</digest><size>1048576</size><bundled_size>1136</bundled_size><ec2_encrypted_key algorithm="AES-128-CBC">33a2ea00dc64083dd9a10eb5e233635b42a7beb1670ab75452087d9de74c60aba1cd27c136fda56f62beb581de128fb1f10d072b9e556fd25e903107a57827c21f6ee8a93a4ff55b11311fcef217e3eefb07e81f71e88216f43b4b54029c1f2549f2925a839a73947d2d5aeecec4a62ece4af9156d557ae907978298296d9915</ec2_encrypted_key><user_encrypted_key algorithm="AES-128-CBC">4c11147fd8caf92447e90ce339928933d7579244c2f8ffb07cc0ea35f8738da8b90eff6c7a49671a84500e993e9462e4c36d5c19c0b3a2b397d035b4c0cce742b58e12552175d81d129b0425e9f71ebacb9aeb539fa9dd2ac36749fb82876f6902e5fb24b6ec19f35ec4c20acd50437fd30966e99c4d9a0647577970a8fa3023</user_encrypted_key><ec2_encrypted_iv>14bd082c9715f071160c69bbfb070f51d2ba1076775f1d988ccde150e515088156b248e4b5a64e46c4fe064feeeedfe14511f7fde478a51acb89f9b2f6c84b60593e5c3f792ba6b01fed9bf2158fdac03086374883b39d13a3ca74497eeaaf579fc3f26effc73bfd9446a2a8c4061f0874bfaca058905180e22d3d8881551cb3</ec2_encrypted_iv><user_encrypted_iv>8f7606f19f00e4e19535dd234b66b31b77e9c7bad3885d9c9efa75c863631fd4f82a009e17d789066d9cc6032a436f05384832f6d9a3283d3e63eab04fa0da5c8c87db9b17e854e842c3fb416507d067a266b44538125ce732e486098e8ebd1ca91fa3079f007fce7d14957a9b7e57282407ead3c6eb68fe975df3d83190021b</user_encrypted_iv><parts count="2"><part index="0"><filename>1mb.part.0</filename><digest algorithm="SHA1">c4413423cf7a57e71187e19bfd5cd4b514a64283</digest></part><part index="1"><filename>1mb.part.1</filename><digest algorithm="SHA1">9d4262e6589393d09a11a0332af169887bc2e57d</digest></part></parts></image><signature>4e00b5ba28114dda4a9df7eeae94be847ec46117a09a1cbe41e578660642f0660dda1776b39fb3bf826b6cfec019e2a5e9c566728d186b7400ebc989a30670eb1db26ce01e68bd9d3f31290370077a85b81c66b63c1e0d5499bac115c06c17a21a81b6d3a67ebbce6c17019095af7ab07f3796c708cc843e58efc12ddc788c5e</signature></manifest> \ No newline at end of file
diff --git a/nova/tests/bundle/1mb.part.0 b/nova/tests/bundle/1mb.part.0
new file mode 100644
index 000000000..15a1657c5
--- /dev/null
+++ b/nova/tests/bundle/1mb.part.0
Binary files differ
diff --git a/nova/tests/bundle/1mb.part.1 b/nova/tests/bundle/1mb.part.1
new file mode 100644
index 000000000..2f0406e2d
--- /dev/null
+++ b/nova/tests/bundle/1mb.part.1
@@ -0,0 +1 @@
+­´ˆà«€ç‰°Ƴ ¡ÀiDHW̽×JÈ8ïrV¼³h§X’·@Yj“~Ø ·Gû5û 3Nt«˜•H6Ñ$§Ëgö™é Lá¢+³æ¤X†pm¬@,øŽ>7ÚÊ×užp¼ aü`¥V2X@£#á¶ \ No newline at end of file
diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py
new file mode 100644
index 000000000..568a8dcd3
--- /dev/null
+++ b/nova/tests/cloud_unittest.py
@@ -0,0 +1,161 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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 logging
+import StringIO
+import time
+import unittest
+from xml.etree import ElementTree
+
+from nova import vendor
+import mox
+from tornado import ioloop
+from twisted.internet import defer
+
+from nova import flags
+from nova import rpc
+from nova import test
+from nova.auth import users
+from nova.compute import node
+from nova.endpoint import api
+from nova.endpoint import cloud
+
+
+FLAGS = flags.FLAGS
+
+
+class CloudTestCase(test.BaseTestCase):
+ def setUp(self):
+ super(CloudTestCase, self).setUp()
+ self.flags(fake_libvirt=True,
+ fake_storage=True,
+ fake_users=True,
+ redis_db=8)
+
+ self.conn = rpc.Connection.instance()
+ logging.getLogger().setLevel(logging.DEBUG)
+
+ # set up our cloud
+ self.cloud = cloud.CloudController()
+ self.cloud_consumer = rpc.AdapterConsumer(connection=self.conn,
+ topic=FLAGS.cloud_topic,
+ proxy=self.cloud)
+ self.injected.append(self.cloud_consumer.attach_to_tornado(self.ioloop))
+
+ # set up a node
+ self.node = node.Node()
+ self.node_consumer = rpc.AdapterConsumer(connection=self.conn,
+ topic=FLAGS.compute_topic,
+ proxy=self.node)
+ self.injected.append(self.node_consumer.attach_to_tornado(self.ioloop))
+
+ user_mocker = mox.Mox()
+ self.admin = user_mocker.CreateMock(users.User)
+ self.admin.is_authorized(mox.IgnoreArg()).AndReturn(True)
+ self.context = api.APIRequestContext(handler=None,user=self.admin)
+
+ def test_console_output(self):
+ if FLAGS.fake_libvirt:
+ logging.debug("Can't test instances without a real virtual env.")
+ return
+ instance_id = 'foo'
+ inst = yield self.node.run_instance(instance_id)
+ output = yield self.cloud.get_console_output(self.context, [instance_id])
+ logging.debug(output)
+ self.assert_(output)
+ rv = yield self.node.terminate_instance(instance_id)
+
+ def test_run_instances(self):
+ if FLAGS.fake_libvirt:
+ logging.debug("Can't test instances without a real virtual env.")
+ return
+ image_id = FLAGS.default_image
+ instance_type = FLAGS.default_instance_type
+ max_count = 1
+ kwargs = {'image_id': image_id,
+ 'instance_type': instance_type,
+ 'max_count': max_count}
+ rv = yield self.cloud.run_instances(self.context, **kwargs)
+ # TODO: check for proper response
+ instance = rv['reservationSet'][0][rv['reservationSet'][0].keys()[0]][0]
+ logging.debug("Need to watch instance %s until it's running..." % instance['instance_id'])
+ while True:
+ rv = yield defer.succeed(time.sleep(1))
+ info = self.cloud._get_instance(instance['instance_id'])
+ logging.debug(info['state'])
+ if info['state'] == node.Instance.RUNNING:
+ break
+ self.assert_(rv)
+
+ if not FLAGS.fake_libvirt:
+ time.sleep(45) # Should use boto for polling here
+ for reservations in rv['reservationSet']:
+ # for res_id in reservations.keys():
+ # logging.debug(reservations[res_id])
+ # for instance in reservations[res_id]:
+ for instance in reservations[reservations.keys()[0]]:
+ logging.debug("Terminating instance %s" % instance['instance_id'])
+ rv = yield self.node.terminate_instance(instance['instance_id'])
+
+ def test_instance_update_state(self):
+ def instance(num):
+ return {
+ 'reservation_id': 'r-1',
+ 'instance_id': 'i-%s' % num,
+ 'image_id': 'ami-%s' % num,
+ 'private_dns_name': '10.0.0.%s' % num,
+ 'dns_name': '10.0.0%s' % num,
+ 'ami_launch_index': str(num),
+ 'instance_type': 'fake',
+ 'availability_zone': 'fake',
+ 'key_name': None,
+ 'kernel_id': 'fake',
+ 'ramdisk_id': 'fake',
+ 'groups': ['default'],
+ 'product_codes': None,
+ 'state': 0x01,
+ 'user_data': ''
+ }
+
+ rv = self.cloud.format_instances(self.admin)
+ print rv
+ self.assert_(len(rv['reservationSet']) == 0)
+
+ # simulate launch of 5 instances
+ # self.cloud.instances['pending'] = {}
+ #for i in xrange(5):
+ # inst = instance(i)
+ # self.cloud.instances['pending'][inst['instance_id']] = inst
+
+ #rv = self.cloud.format_instances(self.admin)
+ #self.assert_(len(rv['reservationSet']) == 1)
+ #self.assert_(len(rv['reservationSet'][0]['instances_set']) == 5)
+
+ # report 4 nodes each having 1 of the instances
+ #for i in xrange(4):
+ # self.cloud.update_state('instances', {('node-%s' % i): {('i-%s' % i): instance(i)}})
+
+ # one instance should be pending still
+ #self.assert_(len(self.cloud.instances['pending'].keys()) == 1)
+
+ # check that the reservations collapse
+ #rv = self.cloud.format_instances(self.admin)
+ #self.assert_(len(rv['reservationSet']) == 1)
+ #self.assert_(len(rv['reservationSet'][0]['instances_set']) == 5)
+
+ # check that we can get metadata for each instance
+ #for i in xrange(4):
+ # data = self.cloud.get_metadata(instance(i)['private_dns_name'])
+ # self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i)
diff --git a/nova/tests/datastore_unittest.py b/nova/tests/datastore_unittest.py
new file mode 100644
index 000000000..4e4d8586a
--- /dev/null
+++ b/nova/tests/datastore_unittest.py
@@ -0,0 +1,60 @@
+from nova import test
+from nova import datastore
+import random
+
+class KeeperTestCase(test.BaseTestCase):
+ """
+ Basic persistence tests for Keeper datastore.
+ Generalize, then use these to support
+ migration to redis / cassandra / multiple stores.
+ """
+
+ def __init__(self, *args, **kwargs):
+ """
+ Create a new keeper instance for test keys.
+ """
+ super(KeeperTestCase, self).__init__(*args, **kwargs)
+ self.keeper = datastore.Keeper('test-')
+
+ def tear_down(self):
+ """
+ Scrub out test keeper data.
+ """
+ pass
+
+ def test_store_strings(self):
+ """
+ Confirm that simple strings go in and come out safely.
+ Should also test unicode strings.
+ """
+ randomstring = ''.join(
+ [random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-')
+ for _x in xrange(20)]
+ )
+ self.keeper['test_string'] = randomstring
+ self.assertEqual(randomstring, self.keeper['test_string'])
+
+ def test_store_dicts(self):
+ """
+ Arbitrary dictionaries should be storable.
+ """
+ test_dict = {'key_one': 'value_one'}
+ self.keeper['test_dict'] = test_dict
+ self.assertEqual(test_dict['key_one'],
+ self.keeper['test_dict']['key_one'])
+
+ def test_sets(self):
+ """
+ A keeper dict should be self-serializing.
+ """
+ self.keeper.set_add('test_set', 'foo')
+ test_dict = {'arbitrary': 'dict of stuff'}
+ self.keeper.set_add('test_set', test_dict)
+ self.assertTrue(self.keeper.set_is_member('test_set', 'foo'))
+ self.assertFalse(self.keeper.set_is_member('test_set', 'bar'))
+ self.keeper.set_remove('test_set', 'foo')
+ self.assertFalse(self.keeper.set_is_member('test_set', 'foo'))
+ rv = self.keeper.set_fetch('test_set')
+ self.assertEqual(test_dict, rv.next())
+ self.keeper.set_remove('test_set', test_dict)
+
diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py
new file mode 100644
index 000000000..3c7b0be52
--- /dev/null
+++ b/nova/tests/fake_flags.py
@@ -0,0 +1,26 @@
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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.
+
+from nova import flags
+
+FLAGS = flags.FLAGS
+
+FLAGS.fake_libvirt = True
+FLAGS.fake_storage = True
+FLAGS.fake_rabbit = True
+FLAGS.fake_network = True
+FLAGS.fake_users = True
+FLAGS.keeper_backend = 'sqlite'
+FLAGS.datastore_path = ':memory:'
+FLAGS.verbose = True
diff --git a/nova/tests/future_unittest.py b/nova/tests/future_unittest.py
new file mode 100644
index 000000000..81d69dfff
--- /dev/null
+++ b/nova/tests/future_unittest.py
@@ -0,0 +1,74 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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 logging
+import StringIO
+import time
+import unittest
+from xml.etree import ElementTree
+
+from nova import vendor
+import mox
+from tornado import ioloop
+from twisted.internet import defer
+
+from nova import cloud
+from nova import exception
+from nova import flags
+from nova import node
+from nova import rpc
+from nova import test
+
+
+FLAGS = flags.FLAGS
+
+
+class AdminTestCase(test.BaseTestCase):
+ def setUp(self):
+ super(AdminTestCase, self).setUp()
+ self.flags(fake_libvirt=True,
+ fake_rabbit=True)
+
+ self.conn = rpc.Connection.instance()
+
+ logging.getLogger().setLevel(logging.INFO)
+
+ # set up our cloud
+ self.cloud = cloud.CloudController()
+ self.cloud_consumer = rpc.AdapterConsumer(connection=self.conn,
+ topic=FLAGS.cloud_topic,
+ proxy=self.cloud)
+ self.injected.append(self.cloud_consumer.attach_to_tornado(self.ioloop))
+
+ # set up a node
+ self.node = node.Node()
+ self.node_consumer = rpc.AdapterConsumer(connection=self.conn,
+ topic=FLAGS.compute_topic,
+ proxy=self.node)
+ self.injected.append(self.node_consumer.attach_to_tornado(self.ioloop))
+
+ def test_flush_terminated(self):
+ # Launch an instance
+
+ # Wait until it's running
+
+ # Terminate it
+
+ # Wait until it's terminated
+
+ # Flush terminated nodes
+
+ # ASSERT that it's gone
+ pass
diff --git a/nova/tests/keeper_unittest.py b/nova/tests/keeper_unittest.py
new file mode 100644
index 000000000..3896c9e57
--- /dev/null
+++ b/nova/tests/keeper_unittest.py
@@ -0,0 +1,57 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+import random
+
+from nova import datastore
+from nova import test
+
+class KeeperTestCase(test.TrialTestCase):
+ """
+ Basic persistence tests for Keeper datastore.
+ Generalize, then use these to support
+ migration to redis / cassandra / multiple stores.
+ """
+
+ def setUp(self):
+ super(KeeperTestCase, self).setUp()
+ self.keeper = datastore.Keeper('test')
+
+ def tearDown(self):
+ super(KeeperTestCase, self).tearDown()
+ self.keeper.clear()
+
+ def test_store_strings(self):
+ """
+ Confirm that simple strings go in and come out safely.
+ Should also test unicode strings.
+ """
+ randomstring = ''.join(
+ [random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-')
+ for _x in xrange(20)]
+ )
+ self.keeper['test_string'] = randomstring
+ self.assertEqual(randomstring, self.keeper['test_string'])
+
+ def test_store_dicts(self):
+ """
+ Arbitrary dictionaries should be storable.
+ """
+ test_dict = {'key_one': 'value_one'}
+ self.keeper['test_dict'] = test_dict
+ self.assertEqual(test_dict['key_one'],
+ self.keeper['test_dict']['key_one'])
+
+ def test_sets(self):
+ """
+ A keeper dict should be self-serializing.
+ """
+ self.keeper.set_add('test_set', 'foo')
+ test_dict = {'arbitrary': 'dict of stuff'}
+ self.keeper.set_add('test_set', test_dict)
+ self.assertTrue(self.keeper.set_is_member('test_set', 'foo'))
+ self.assertFalse(self.keeper.set_is_member('test_set', 'bar'))
+ self.keeper.set_remove('test_set', 'foo')
+ self.assertFalse(self.keeper.set_is_member('test_set', 'foo'))
+ rv = self.keeper.set_fetch('test_set')
+ self.assertEqual(test_dict, rv.next())
+ self.keeper.set_remove('test_set', test_dict)
+
diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py
new file mode 100644
index 000000000..43c7831a7
--- /dev/null
+++ b/nova/tests/network_unittest.py
@@ -0,0 +1,113 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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 logging
+import unittest
+
+from nova import vendor
+import IPy
+
+from nova import flags
+from nova import test
+from nova.compute import network
+from nova.auth import users
+
+
+class NetworkTestCase(test.TrialTestCase):
+ def setUp(self):
+ super(NetworkTestCase, self).setUp()
+ logging.getLogger().setLevel(logging.DEBUG)
+ self.manager = users.UserManager.instance()
+ for i in range(0, 6):
+ name = 'user%s' % i
+ if not self.manager.get_user(name):
+ self.manager.create_user(name, name, name)
+ self.network = network.NetworkController(netsize=16)
+
+ def tearDown(self):
+ super(NetworkTestCase, self).tearDown()
+ for i in range(0, 6):
+ name = 'user%s' % i
+ self.manager.delete_user(name)
+
+ def test_network_serialization(self):
+ net1 = network.Network(vlan=100, network="192.168.100.0/24", conn=None)
+ address = net1.allocate_ip("user0", "01:24:55:36:f2:a0")
+ net_json = str(net1)
+ net2 = network.Network.from_json(net_json)
+ self.assertEqual(net_json, str(net2))
+ self.assertTrue(IPy.IP(address) in net2.network)
+
+ def test_allocate_deallocate_address(self):
+ for flag in flags.FLAGS:
+ print "%s=%s" % (flag, flags.FLAGS.get(flag, None))
+ (address, net_name) = self.network.allocate_address(
+ "user0", "01:24:55:36:f2:a0")
+ logging.debug("Was allocated %s" % (address))
+ self.assertEqual(True, address in self._get_user_addresses("user0"))
+ rv = self.network.deallocate_address(address)
+ self.assertEqual(False, address in self._get_user_addresses("user0"))
+
+ def test_range_allocation(self):
+ (address, net_name) = self.network.allocate_address(
+ "user0", "01:24:55:36:f2:a0")
+ (secondaddress, net_name) = self.network.allocate_address(
+ "user1", "01:24:55:36:f2:a0")
+ self.assertEqual(True, address in self._get_user_addresses("user0"))
+ self.assertEqual(True,
+ secondaddress in self._get_user_addresses("user1"))
+ self.assertEqual(False, address in self._get_user_addresses("user1"))
+ rv = self.network.deallocate_address(address)
+ self.assertEqual(False, address in self._get_user_addresses("user0"))
+ rv = self.network.deallocate_address(secondaddress)
+ self.assertEqual(False,
+ secondaddress in self._get_user_addresses("user1"))
+
+ def test_subnet_edge(self):
+ (secondaddress, net_name) = self.network.allocate_address("user0")
+ for user in range(1,5):
+ user_id = "user%s" % (user)
+ (address, net_name) = self.network.allocate_address(
+ user_id, "01:24:55:36:f2:a0")
+ (address2, net_name) = self.network.allocate_address(
+ user_id, "01:24:55:36:f2:a0")
+ (address3, net_name) = self.network.allocate_address(
+ user_id, "01:24:55:36:f2:a0")
+ self.assertEqual(False,
+ address in self._get_user_addresses("user0"))
+ self.assertEqual(False,
+ address2 in self._get_user_addresses("user0"))
+ self.assertEqual(False,
+ address3 in self._get_user_addresses("user0"))
+ rv = self.network.deallocate_address(address)
+ rv = self.network.deallocate_address(address2)
+ rv = self.network.deallocate_address(address3)
+ rv = self.network.deallocate_address(secondaddress)
+
+ def test_too_many_users(self):
+ for i in range(0, 30):
+ name = 'toomany-user%s' % i
+ self.manager.create_user(name, name, name)
+ (address, net_name) = self.network.allocate_address(
+ name, "01:24:55:36:f2:a0")
+ self.manager.delete_user(name)
+
+ def _get_user_addresses(self, user_id):
+ rv = self.network.describe_addresses()
+ user_addresses = []
+ for item in rv:
+ if item['user_id'] == user_id:
+ user_addresses.append(item['address'])
+ return user_addresses
diff --git a/nova/tests/node_unittest.py b/nova/tests/node_unittest.py
new file mode 100644
index 000000000..7a6115fcc
--- /dev/null
+++ b/nova/tests/node_unittest.py
@@ -0,0 +1,128 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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 logging
+import StringIO
+import time
+import unittest
+from xml.etree import ElementTree
+
+from nova import vendor
+import mox
+from tornado import ioloop
+from twisted.internet import defer
+
+from nova import exception
+from nova import flags
+from nova import test
+from nova import utils
+from nova.compute import model
+from nova.compute import node
+
+FLAGS = flags.FLAGS
+
+
+class InstanceXmlTestCase(test.TrialTestCase):
+ # @defer.inlineCallbacks
+ def test_serialization(self):
+ # TODO: Reimplement this, it doesn't make sense in redis-land
+ return
+
+ # instance_id = 'foo'
+ # first_node = node.Node()
+ # inst = yield first_node.run_instance(instance_id)
+ #
+ # # force the state so that we can verify that it changes
+ # inst._s['state'] = node.Instance.NOSTATE
+ # xml = inst.toXml()
+ # self.assert_(ElementTree.parse(StringIO.StringIO(xml)))
+ #
+ # second_node = node.Node()
+ # new_inst = node.Instance.fromXml(second_node._conn, pool=second_node._pool, xml=xml)
+ # self.assertEqual(new_inst.state, node.Instance.RUNNING)
+ # rv = yield first_node.terminate_instance(instance_id)
+
+
+class NodeConnectionTestCase(test.TrialTestCase):
+ def setUp(self):
+ logging.getLogger().setLevel(logging.DEBUG)
+ super(NodeConnectionTestCase, self).setUp()
+ self.flags(fake_libvirt=True,
+ fake_storage=True,
+ fake_users=True,
+ redis_db=8)
+ self.node = node.Node()
+
+ def create_instance(self):
+ instdir = model.InstanceDirectory()
+ inst = instdir.new()
+ # TODO(ja): add ami, ari, aki, user_data
+ inst['reservation_id'] = 'r-fakeres'
+ inst['launch_time'] = '10'
+ inst['owner_id'] = 'fake'
+ inst['node_name'] = FLAGS.node_name
+ inst['mac_address'] = utils.generate_mac()
+ inst['ami_launch_index'] = 0
+ inst.save()
+ return inst['instance_id']
+
+ @defer.inlineCallbacks
+ def test_run_describe_terminate(self):
+ instance_id = self.create_instance()
+
+ rv = yield self.node.run_instance(instance_id)
+
+ rv = yield self.node.describe_instances()
+ self.assertEqual(rv[instance_id].name, instance_id)
+
+ rv = yield self.node.terminate_instance(instance_id)
+
+ rv = yield self.node.describe_instances()
+ self.assertEqual(rv, {})
+
+ @defer.inlineCallbacks
+ def test_reboot(self):
+ instance_id = self.create_instance()
+ rv = yield self.node.run_instance(instance_id)
+
+ rv = yield self.node.describe_instances()
+ logging.debug("describe_instances returns %s" % (rv))
+ self.assertEqual(rv[instance_id].name, instance_id)
+
+ yield self.node.reboot_instance(instance_id)
+
+ rv = yield self.node.describe_instances()
+ self.assertEqual(rv[instance_id].name, instance_id)
+ rv = yield self.node.terminate_instance(instance_id)
+
+ @defer.inlineCallbacks
+ def test_console_output(self):
+ instance_id = self.create_instance()
+ rv = yield self.node.run_instance(instance_id)
+
+ console = yield self.node.get_console_output(instance_id)
+ self.assert_(console)
+ rv = yield self.node.terminate_instance(instance_id)
+
+ @defer.inlineCallbacks
+ def test_run_instance_existing(self):
+ instance_id = self.create_instance()
+ rv = yield self.node.run_instance(instance_id)
+
+ rv = yield self.node.describe_instances()
+ self.assertEqual(rv[instance_id].name, instance_id)
+
+ self.assertRaises(exception.Error, self.node.run_instance, instance_id)
+ rv = yield self.node.terminate_instance(instance_id)
diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py
new file mode 100644
index 000000000..5f41d47a0
--- /dev/null
+++ b/nova/tests/objectstore_unittest.py
@@ -0,0 +1,190 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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 glob
+import hashlib
+import logging
+import os
+import shutil
+import tempfile
+
+from nova import vendor
+
+from nova import flags
+from nova import rpc
+from nova import objectstore
+from nova import test
+from nova.auth import users
+
+FLAGS = flags.FLAGS
+
+
+oss_tempdir = tempfile.mkdtemp(prefix='test_oss-')
+
+
+# delete tempdirs from previous runs (we don't delete after test to allow
+# checking the contents after running tests)
+for path in glob.glob(os.path.abspath(os.path.join(oss_tempdir, '../test_oss-*'))):
+ if path != oss_tempdir:
+ shutil.rmtree(path)
+
+
+# create bucket/images path
+os.makedirs(os.path.join(oss_tempdir, 'images'))
+os.makedirs(os.path.join(oss_tempdir, 'buckets'))
+
+class ObjectStoreTestCase(test.BaseTestCase):
+ def setUp(self):
+ super(ObjectStoreTestCase, self).setUp()
+ self.flags(fake_users=True,
+ buckets_path=os.path.join(oss_tempdir, 'buckets'),
+ images_path=os.path.join(oss_tempdir, 'images'),
+ ca_path=os.path.join(os.path.dirname(__file__), 'CA'))
+ self.conn = rpc.Connection.instance()
+ logging.getLogger().setLevel(logging.DEBUG)
+
+ self.um = users.UserManager.instance()
+
+ def test_buckets(self):
+ try:
+ self.um.create_user('user1')
+ except: pass
+ try:
+ self.um.create_user('user2')
+ except: pass
+ try:
+ self.um.create_user('admin_user', admin=True)
+ except: pass
+
+ objectstore.bucket.Bucket.create('new_bucket', self.um.get_user('user1'))
+ bucket = objectstore.bucket.Bucket('new_bucket')
+
+ # creator is authorized to use bucket
+ self.assert_(bucket.is_authorized(self.um.get_user('user1')))
+
+ # another user is not authorized
+ self.assert_(bucket.is_authorized(self.um.get_user('user2')) == False)
+
+ # admin is authorized to use bucket
+ self.assert_(bucket.is_authorized(self.um.get_user('admin_user')))
+
+ # new buckets are empty
+ self.assert_(bucket.list_keys()['Contents'] == [])
+
+ # storing keys works
+ bucket['foo'] = "bar"
+
+ self.assert_(len(bucket.list_keys()['Contents']) == 1)
+
+ self.assert_(bucket['foo'].read() == 'bar')
+
+ # md5 of key works
+ self.assert_(bucket['foo'].md5 == hashlib.md5('bar').hexdigest())
+
+ # deleting non-empty bucket throws exception
+ exception = False
+ try:
+ bucket.delete()
+ except:
+ exception = True
+
+ self.assert_(exception)
+
+ # deleting key
+ del bucket['foo']
+
+ # deleting empty button
+ bucket.delete()
+
+ # accessing deleted bucket throws exception
+ exception = False
+ try:
+ objectstore.bucket.Bucket('new_bucket')
+ except:
+ exception = True
+
+ self.assert_(exception)
+ self.um.delete_user('user1')
+ self.um.delete_user('user2')
+ self.um.delete_user('admin_user')
+
+ def test_images(self):
+ try:
+ self.um.create_user('image_creator')
+ except: pass
+ image_user = self.um.get_user('image_creator')
+
+ # create a bucket for our bundle
+ objectstore.bucket.Bucket.create('image_bucket', image_user)
+ bucket = objectstore.bucket.Bucket('image_bucket')
+
+ # upload an image manifest/parts
+ bundle_path = os.path.join(os.path.dirname(__file__), 'bundle')
+ for path in glob.glob(bundle_path + '/*'):
+ bucket[os.path.basename(path)] = open(path, 'rb').read()
+
+ # register an image
+ objectstore.image.Image.create('i-testing', 'image_bucket/1mb.manifest.xml', image_user)
+
+ # verify image
+ my_img = objectstore.image.Image('i-testing')
+ result_image_file = os.path.join(my_img.path, 'image')
+ self.assertEqual(os.stat(result_image_file).st_size, 1048576)
+
+ sha = hashlib.sha1(open(result_image_file).read()).hexdigest()
+ self.assertEqual(sha, '3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3')
+
+ # verify image permissions
+ try:
+ self.um.create_user('new_user')
+ except: pass
+ new_user = self.um.get_user('new_user')
+ self.assert_(my_img.is_authorized(new_user) == False)
+
+ self.um.delete_user('new_user')
+ self.um.delete_user('image_creator')
+
+# class ApiObjectStoreTestCase(test.BaseTestCase):
+# def setUp(self):
+# super(ApiObjectStoreTestCase, self).setUp()
+# FLAGS.fake_users = True
+# FLAGS.buckets_path = os.path.join(tempdir, 'buckets')
+# FLAGS.images_path = os.path.join(tempdir, 'images')
+# FLAGS.ca_path = os.path.join(os.path.dirname(__file__), 'CA')
+#
+# self.users = users.UserManager.instance()
+# self.app = handler.Application(self.users)
+#
+# self.host = '127.0.0.1'
+#
+# self.conn = boto.s3.connection.S3Connection(
+# aws_access_key_id=user.access,
+# aws_secret_access_key=user.secret,
+# is_secure=False,
+# calling_format=boto.s3.connection.OrdinaryCallingFormat(),
+# port=FLAGS.s3_port,
+# host=FLAGS.s3_host)
+#
+# self.mox.StubOutWithMock(self.ec2, 'new_http_connection')
+#
+# def tearDown(self):
+# FLAGS.Reset()
+# super(ApiObjectStoreTestCase, self).tearDown()
+#
+# def test_describe_instances(self):
+# self.expect_http()
+# self.mox.ReplayAll()
+#
+# self.assertEqual(self.ec2.get_all_instances(), [])
diff --git a/nova/tests/real_flags.py b/nova/tests/real_flags.py
new file mode 100644
index 000000000..68fe8dc5b
--- /dev/null
+++ b/nova/tests/real_flags.py
@@ -0,0 +1,24 @@
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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.
+
+from nova import flags
+
+FLAGS = flags.FLAGS
+
+FLAGS.fake_libvirt = False
+FLAGS.fake_storage = False
+FLAGS.fake_rabbit = False
+FLAGS.fake_network = False
+FLAGS.fake_users = False
+FLAGS.verbose = False
diff --git a/nova/tests/storage_unittest.py b/nova/tests/storage_unittest.py
new file mode 100644
index 000000000..31966d2d5
--- /dev/null
+++ b/nova/tests/storage_unittest.py
@@ -0,0 +1,86 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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 logging
+import StringIO
+import time
+import unittest
+from xml.etree import ElementTree
+
+from nova import vendor
+import mox
+from tornado import ioloop
+from twisted.internet import defer
+
+from nova import exception
+from nova import flags
+from nova import test
+from nova.compute import node
+from nova.volume import storage
+
+
+FLAGS = flags.FLAGS
+
+
+class StorageTestCase(test.TrialTestCase):
+ def setUp(self):
+ logging.getLogger().setLevel(logging.DEBUG)
+ super(StorageTestCase, self).setUp()
+ self.mynode = node.Node()
+ self.mystorage = None
+ self.flags(fake_libvirt=True,
+ fake_storage=True,
+ redis_db=8)
+ if FLAGS.fake_storage:
+ self.mystorage = storage.FakeBlockStore()
+ else:
+ self.mystorage = storage.BlockStore()
+
+ @test.skip_if_fake
+ def test_run_create_volume(self):
+ vol_size = '0'
+ user_id = 'fake'
+ volume_id = self.mystorage.create_volume(vol_size, user_id)
+ # rv = self.mystorage.describe_volumes()
+
+ # Volumes have to be sorted by timestamp in order to work here...
+ # TODO(termie): get_volume returns differently than create_volume
+ self.assertEqual(volume_id,
+ self.mystorage.get_volume(volume_id)['volume_id'])
+
+ rv = self.mystorage.delete_volume(volume_id)
+ self.assertRaises(exception.Error,
+ self.mystorage.get_volume,
+ volume_id)
+
+ @test.skip_if_fake
+ def test_run_attach_detach_volume(self):
+ # Create one volume and one node to test with
+ instance_id = "storage-test"
+ # TODO(joshua) - Redo this test, can't make fake instances this way any more
+ # rv = self.mynode.run_instance(instance_id)
+ vol_size = "5"
+ user_id = "fake"
+ volume_id = self.mystorage.create_volume(vol_size, user_id)
+ rv = self.mystorage.attach_volume(volume_id,
+ instance_id,
+ "/dev/sdf")
+ volume_obj = self.mystorage.get_volume(volume_id)
+ self.assertEqual(volume_obj['status'], "attached")
+ # TODO(???): assert that it's attached to the right instance
+
+ rv = self.mystorage.detach_volume(volume_id)
+ volume_obj = self.mystorage.get_volume(volume_id)
+ self.assertEqual(volume_obj['status'], "available")
diff --git a/nova/tests/users_unittest.py b/nova/tests/users_unittest.py
new file mode 100644
index 000000000..70f508b35
--- /dev/null
+++ b/nova/tests/users_unittest.py
@@ -0,0 +1,137 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright [2010] [Anso Labs, LLC]
+#
+# 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 logging
+import unittest
+
+from nova import vendor
+from M2Crypto import BIO
+from M2Crypto import RSA
+from M2Crypto import X509
+
+from nova import crypto
+from nova import flags
+from nova import test
+from nova import utils
+from nova.auth import users
+from nova.endpoint import cloud
+
+
+FLAGS = flags.FLAGS
+
+
+class UserTestCase(test.BaseTestCase):
+ def setUp(self):
+ super(UserTestCase, self).setUp()
+ self.flags(fake_libvirt=True,
+ fake_storage=True,
+ redis_db=8)
+ self.users = users.UserManager.instance()
+
+ def test_001_can_create_user(self):
+ self.users.create_user('test1', 'access', 'secret')
+
+ def test_002_can_get_user(self):
+ user = self.users.get_user('test1')
+
+ def test_003_can_retreive_properties(self):
+ user = self.users.get_user('test1')
+ self.assertEqual('test1', user.id)
+ self.assertEqual('access', user.access)
+ self.assertEqual('secret', user.secret)
+
+ def test_004_signature_is_valid(self):
+ #self.assertTrue(self.users.authenticate( **boto.generate_url ... ? ? ? ))
+ pass
+ #raise NotImplementedError
+
+ def test_005_can_get_credentials(self):
+ return
+ credentials = self.users.get_user('test1').get_credentials()
+ self.assertEqual(credentials,
+ 'export EC2_ACCESS_KEY="access"\n' +
+ 'export EC2_SECRET_KEY="secret"\n' +
+ 'export EC2_URL="http://127.0.0.1:8773/services/Cloud"\n' +
+ 'export S3_URL="http://127.0.0.1:3333/"\n' +
+ 'export EC2_USER_ID="test1"\n')
+
+ def test_006_test_key_storage(self):
+ user = self.users.get_user('test1')
+ user.create_key_pair('public', 'key', 'fingerprint')
+ key = user.get_key_pair('public')
+ self.assertEqual('key', key.public_key)
+ self.assertEqual('fingerprint', key.fingerprint)
+
+ def test_007_test_key_generation(self):
+ user = self.users.get_user('test1')
+ private_key, fingerprint = user.generate_key_pair('public2')
+ key = RSA.load_key_string(private_key, callback=lambda: None)
+ bio = BIO.MemoryBuffer()
+ public_key = user.get_key_pair('public2').public_key
+ key.save_pub_key_bio(bio)
+ converted = crypto.ssl_pub_to_ssh_pub(bio.read())
+ # assert key fields are equal
+ print converted
+ self.assertEqual(public_key.split(" ")[1].strip(),
+ converted.split(" ")[1].strip())
+
+ def test_008_can_list_key_pairs(self):
+ keys = self.users.get_user('test1').get_key_pairs()
+ self.assertTrue(filter(lambda k: k.name == 'public', keys))
+ self.assertTrue(filter(lambda k: k.name == 'public2', keys))
+
+ def test_009_can_delete_key_pair(self):
+ self.users.get_user('test1').delete_key_pair('public')
+ keys = self.users.get_user('test1').get_key_pairs()
+ self.assertFalse(filter(lambda k: k.name == 'public', keys))
+
+ def test_010_can_list_users(self):
+ users = self.users.get_users()
+ self.assertTrue(filter(lambda u: u.id == 'test1', users))
+
+ def test_011_can_generate_x509(self):
+ # MUST HAVE RUN CLOUD SETUP BY NOW
+ self.cloud = cloud.CloudController()
+ self.cloud.setup()
+ private_key, signed_cert_string = self.users.get_user('test1').generate_x509_cert()
+ logging.debug(signed_cert_string)
+
+ # Need to verify that it's signed by the right intermediate CA
+ full_chain = crypto.fetch_ca(username='test1', chain=True)
+ int_cert = crypto.fetch_ca(username='test1', chain=False)
+ cloud_cert = crypto.fetch_ca()
+ logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
+ signed_cert = X509.load_cert_string(signed_cert_string)
+ chain_cert = X509.load_cert_string(full_chain)
+ int_cert = X509.load_cert_string(int_cert)
+ cloud_cert = X509.load_cert_string(cloud_cert)
+ self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
+ self.assertTrue(signed_cert.verify(int_cert.get_pubkey()))
+
+ if not FLAGS.use_intermediate_ca:
+ self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
+ else:
+ self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
+
+ def test_012_can_delete_user(self):
+ self.users.delete_user('test1')
+ users = self.users.get_users()
+ if users != None:
+ self.assertFalse(filter(lambda u: u.id == 'test1', users))
+
+
+if __name__ == "__main__":
+ # TODO: Implement use_fake as an option
+ unittest.main()