summaryrefslogtreecommitdiffstats
path: root/nova/tests
diff options
context:
space:
mode:
authordanwent@gmail.com <>2011-08-28 20:06:55 -0700
committerdanwent@gmail.com <>2011-08-28 20:06:55 -0700
commita4bd14f86f31ea82daad8e194b26e86e6dd37398 (patch)
tree3807a1131dc6a7ba7119782f356eb365f28d8b6c /nova/tests
parent605fe4f19af3af830a2a8c82809e9ce5909c602d (diff)
parent0ef2581749f39fa4fd41c2376186418e730f0afb (diff)
merge trunk
Diffstat (limited to 'nova/tests')
-rw-r--r--nova/tests/api/openstack/contrib/test_vsa.py450
-rw-r--r--nova/tests/api/openstack/test_extensions.py2
-rw-r--r--nova/tests/api/openstack/test_server_actions.py147
-rw-r--r--nova/tests/api/openstack/test_servers.py296
-rw-r--r--nova/tests/api/openstack/test_volume_types.py171
-rw-r--r--nova/tests/api/openstack/test_volume_types_extra_specs.py181
-rw-r--r--nova/tests/integrated/integrated_helpers.py109
-rw-r--r--nova/tests/integrated/test_login.py39
-rw-r--r--nova/tests/integrated/test_servers.py5
-rw-r--r--nova/tests/integrated/test_volumes.py17
-rw-r--r--nova/tests/monkey_patch_example/__init__.py33
-rw-r--r--nova/tests/monkey_patch_example/example_a.py29
-rw-r--r--nova/tests/monkey_patch_example/example_b.py30
-rw-r--r--nova/tests/notifier/__init__.py16
-rw-r--r--nova/tests/notifier/test_list_notifier.py88
-rw-r--r--nova/tests/scheduler/test_vsa_scheduler.py641
-rw-r--r--nova/tests/test_auth.py1
-rw-r--r--nova/tests/test_compute.py20
-rw-r--r--nova/tests/test_instance_types.py68
-rw-r--r--nova/tests/test_ipv6.py38
-rw-r--r--nova/tests/test_notifier.py21
-rw-r--r--nova/tests/test_nova_manage.py154
-rw-r--r--nova/tests/test_test_utils.py41
-rw-r--r--nova/tests/test_utils.py45
-rw-r--r--nova/tests/test_versions.py61
-rw-r--r--nova/tests/test_virt_drivers.py489
-rw-r--r--nova/tests/test_volume_types.py207
-rw-r--r--nova/tests/test_volume_types_extra_specs.py132
-rw-r--r--nova/tests/test_vsa.py182
-rw-r--r--nova/tests/test_vsa_volumes.py136
-rw-r--r--nova/tests/utils.py68
31 files changed, 3731 insertions, 186 deletions
diff --git a/nova/tests/api/openstack/contrib/test_vsa.py b/nova/tests/api/openstack/contrib/test_vsa.py
new file mode 100644
index 000000000..311b6cb8d
--- /dev/null
+++ b/nova/tests/api/openstack/contrib/test_vsa.py
@@ -0,0 +1,450 @@
+# Copyright 2011 OpenStack LLC.
+# 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 json
+import stubout
+import unittest
+import webob
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import test
+from nova import volume
+from nova import vsa
+from nova.api import openstack
+from nova.tests.api.openstack import fakes
+import nova.wsgi
+
+from nova.api.openstack.contrib.virtual_storage_arrays import _vsa_view
+
+FLAGS = flags.FLAGS
+
+LOG = logging.getLogger('nova.tests.api.openstack.vsa')
+
+last_param = {}
+
+
+def _get_default_vsa_param():
+ return {
+ 'display_name': 'Test_VSA_name',
+ 'display_description': 'Test_VSA_description',
+ 'vc_count': 1,
+ 'instance_type': 'm1.small',
+ 'instance_type_id': 5,
+ 'image_name': None,
+ 'availability_zone': None,
+ 'storage': [],
+ 'shared': False
+ }
+
+
+def stub_vsa_create(self, context, **param):
+ global last_param
+ LOG.debug(_("_create: param=%s"), param)
+ param['id'] = 123
+ param['name'] = 'Test name'
+ param['instance_type_id'] = 5
+ last_param = param
+ return param
+
+
+def stub_vsa_delete(self, context, vsa_id):
+ global last_param
+ last_param = dict(vsa_id=vsa_id)
+
+ LOG.debug(_("_delete: %s"), locals())
+ if vsa_id != '123':
+ raise exception.NotFound
+
+
+def stub_vsa_get(self, context, vsa_id):
+ global last_param
+ last_param = dict(vsa_id=vsa_id)
+
+ LOG.debug(_("_get: %s"), locals())
+ if vsa_id != '123':
+ raise exception.NotFound
+
+ param = _get_default_vsa_param()
+ param['id'] = vsa_id
+ return param
+
+
+def stub_vsa_get_all(self, context):
+ LOG.debug(_("_get_all: %s"), locals())
+ param = _get_default_vsa_param()
+ param['id'] = 123
+ return [param]
+
+
+class VSAApiTest(test.TestCase):
+ def setUp(self):
+ super(VSAApiTest, self).setUp()
+ self.stubs = stubout.StubOutForTesting()
+ fakes.FakeAuthManager.reset_fake_data()
+ fakes.FakeAuthDatabase.data = {}
+ fakes.stub_out_networking(self.stubs)
+ fakes.stub_out_rate_limiting(self.stubs)
+ fakes.stub_out_auth(self.stubs)
+ self.stubs.Set(vsa.api.API, "create", stub_vsa_create)
+ self.stubs.Set(vsa.api.API, "delete", stub_vsa_delete)
+ self.stubs.Set(vsa.api.API, "get", stub_vsa_get)
+ self.stubs.Set(vsa.api.API, "get_all", stub_vsa_get_all)
+
+ self.context = context.get_admin_context()
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ super(VSAApiTest, self).tearDown()
+
+ def test_vsa_create(self):
+ global last_param
+ last_param = {}
+
+ vsa = {"displayName": "VSA Test Name",
+ "displayDescription": "VSA Test Desc"}
+ body = dict(vsa=vsa)
+ req = webob.Request.blank('/v1.1/777/zadr-vsa')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers['content-type'] = 'application/json'
+
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 200)
+
+ # Compare if parameters were correctly passed to stub
+ self.assertEqual(last_param['display_name'], "VSA Test Name")
+ self.assertEqual(last_param['display_description'], "VSA Test Desc")
+
+ resp_dict = json.loads(resp.body)
+ self.assertTrue('vsa' in resp_dict)
+ self.assertEqual(resp_dict['vsa']['displayName'], vsa['displayName'])
+ self.assertEqual(resp_dict['vsa']['displayDescription'],
+ vsa['displayDescription'])
+
+ def test_vsa_create_no_body(self):
+ req = webob.Request.blank('/v1.1/777/zadr-vsa')
+ req.method = 'POST'
+ req.body = json.dumps({})
+ req.headers['content-type'] = 'application/json'
+
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 422)
+
+ def test_vsa_delete(self):
+ global last_param
+ last_param = {}
+
+ vsa_id = 123
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id)
+ req.method = 'DELETE'
+
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 200)
+ self.assertEqual(str(last_param['vsa_id']), str(vsa_id))
+
+ def test_vsa_delete_invalid_id(self):
+ global last_param
+ last_param = {}
+
+ vsa_id = 234
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id)
+ req.method = 'DELETE'
+
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 404)
+ self.assertEqual(str(last_param['vsa_id']), str(vsa_id))
+
+ def test_vsa_show(self):
+ global last_param
+ last_param = {}
+
+ vsa_id = 123
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id)
+ req.method = 'GET'
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 200)
+ self.assertEqual(str(last_param['vsa_id']), str(vsa_id))
+
+ resp_dict = json.loads(resp.body)
+ self.assertTrue('vsa' in resp_dict)
+ self.assertEqual(resp_dict['vsa']['id'], str(vsa_id))
+
+ def test_vsa_show_invalid_id(self):
+ global last_param
+ last_param = {}
+
+ vsa_id = 234
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id)
+ req.method = 'GET'
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 404)
+ self.assertEqual(str(last_param['vsa_id']), str(vsa_id))
+
+ def test_vsa_index(self):
+ req = webob.Request.blank('/v1.1/777/zadr-vsa')
+ req.method = 'GET'
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 200)
+
+ resp_dict = json.loads(resp.body)
+
+ self.assertTrue('vsaSet' in resp_dict)
+ resp_vsas = resp_dict['vsaSet']
+ self.assertEqual(len(resp_vsas), 1)
+
+ resp_vsa = resp_vsas.pop()
+ self.assertEqual(resp_vsa['id'], 123)
+
+ def test_vsa_detail(self):
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/detail')
+ req.method = 'GET'
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 200)
+
+ resp_dict = json.loads(resp.body)
+
+ self.assertTrue('vsaSet' in resp_dict)
+ resp_vsas = resp_dict['vsaSet']
+ self.assertEqual(len(resp_vsas), 1)
+
+ resp_vsa = resp_vsas.pop()
+ self.assertEqual(resp_vsa['id'], 123)
+
+
+def _get_default_volume_param():
+ return {
+ 'id': 123,
+ 'status': 'available',
+ 'size': 100,
+ 'availability_zone': 'nova',
+ 'created_at': None,
+ 'attach_status': 'detached',
+ 'name': 'vol name',
+ 'display_name': 'Default vol name',
+ 'display_description': 'Default vol description',
+ 'volume_type_id': 1,
+ 'volume_metadata': [],
+ }
+
+
+def stub_get_vsa_volume_type(self, context):
+ return {'id': 1,
+ 'name': 'VSA volume type',
+ 'extra_specs': {'type': 'vsa_volume'}}
+
+
+def stub_volume_create(self, context, size, snapshot_id, name, description,
+ **param):
+ LOG.debug(_("_create: param=%s"), size)
+ vol = _get_default_volume_param()
+ vol['size'] = size
+ vol['display_name'] = name
+ vol['display_description'] = description
+ return vol
+
+
+def stub_volume_update(self, context, **param):
+ LOG.debug(_("_volume_update: param=%s"), param)
+ pass
+
+
+def stub_volume_delete(self, context, **param):
+ LOG.debug(_("_volume_delete: param=%s"), param)
+ pass
+
+
+def stub_volume_get(self, context, volume_id):
+ LOG.debug(_("_volume_get: volume_id=%s"), volume_id)
+ vol = _get_default_volume_param()
+ vol['id'] = volume_id
+ meta = {'key': 'from_vsa_id', 'value': '123'}
+ if volume_id == '345':
+ meta = {'key': 'to_vsa_id', 'value': '123'}
+ vol['volume_metadata'].append(meta)
+ return vol
+
+
+def stub_volume_get_notfound(self, context, volume_id):
+ raise exception.NotFound
+
+
+def stub_volume_get_all(self, context, search_opts):
+ vol = stub_volume_get(self, context, '123')
+ vol['metadata'] = search_opts['metadata']
+ return [vol]
+
+
+def return_vsa(context, vsa_id):
+ return {'id': vsa_id}
+
+
+class VSAVolumeApiTest(test.TestCase):
+
+ def setUp(self, test_obj=None, test_objs=None):
+ super(VSAVolumeApiTest, self).setUp()
+ self.stubs = stubout.StubOutForTesting()
+ fakes.FakeAuthManager.reset_fake_data()
+ fakes.FakeAuthDatabase.data = {}
+ fakes.stub_out_networking(self.stubs)
+ fakes.stub_out_rate_limiting(self.stubs)
+ fakes.stub_out_auth(self.stubs)
+ self.stubs.Set(nova.db.api, 'vsa_get', return_vsa)
+ self.stubs.Set(vsa.api.API, "get_vsa_volume_type",
+ stub_get_vsa_volume_type)
+
+ self.stubs.Set(volume.api.API, "update", stub_volume_update)
+ self.stubs.Set(volume.api.API, "delete", stub_volume_delete)
+ self.stubs.Set(volume.api.API, "get", stub_volume_get)
+ self.stubs.Set(volume.api.API, "get_all", stub_volume_get_all)
+
+ self.context = context.get_admin_context()
+ self.test_obj = test_obj if test_obj else "volume"
+ self.test_objs = test_objs if test_objs else "volumes"
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ super(VSAVolumeApiTest, self).tearDown()
+
+ def test_vsa_volume_create(self):
+ self.stubs.Set(volume.api.API, "create", stub_volume_create)
+
+ vol = {"size": 100,
+ "displayName": "VSA Volume Test Name",
+ "displayDescription": "VSA Volume Test Desc"}
+ body = {self.test_obj: vol}
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s' % self.test_objs)
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers['content-type'] = 'application/json'
+ resp = req.get_response(fakes.wsgi_app())
+
+ if self.test_obj == "volume":
+ self.assertEqual(resp.status_int, 200)
+
+ resp_dict = json.loads(resp.body)
+ self.assertTrue(self.test_obj in resp_dict)
+ self.assertEqual(resp_dict[self.test_obj]['size'],
+ vol['size'])
+ self.assertEqual(resp_dict[self.test_obj]['displayName'],
+ vol['displayName'])
+ self.assertEqual(resp_dict[self.test_obj]['displayDescription'],
+ vol['displayDescription'])
+ else:
+ self.assertEqual(resp.status_int, 400)
+
+ def test_vsa_volume_create_no_body(self):
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s' % self.test_objs)
+ req.method = 'POST'
+ req.body = json.dumps({})
+ req.headers['content-type'] = 'application/json'
+
+ resp = req.get_response(fakes.wsgi_app())
+ if self.test_obj == "volume":
+ self.assertEqual(resp.status_int, 422)
+ else:
+ self.assertEqual(resp.status_int, 400)
+
+ def test_vsa_volume_index(self):
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s' % self.test_objs)
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 200)
+
+ def test_vsa_volume_detail(self):
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/detail' % \
+ self.test_objs)
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 200)
+
+ def test_vsa_volume_show(self):
+ obj_num = 234 if self.test_objs == "volumes" else 345
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/%s' % \
+ (self.test_objs, obj_num))
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 200)
+
+ def test_vsa_volume_show_no_vsa_assignment(self):
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/4/%s/333' % \
+ (self.test_objs))
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 400)
+
+ def test_vsa_volume_show_no_volume(self):
+ self.stubs.Set(volume.api.API, "get", stub_volume_get_notfound)
+
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/333' % \
+ (self.test_objs))
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 404)
+
+ def test_vsa_volume_update(self):
+ obj_num = 234 if self.test_objs == "volumes" else 345
+ update = {"status": "available",
+ "displayName": "Test Display name"}
+ body = {self.test_obj: update}
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/%s' % \
+ (self.test_objs, obj_num))
+ req.method = 'PUT'
+ req.body = json.dumps(body)
+ req.headers['content-type'] = 'application/json'
+
+ resp = req.get_response(fakes.wsgi_app())
+ if self.test_obj == "volume":
+ self.assertEqual(resp.status_int, 202)
+ else:
+ self.assertEqual(resp.status_int, 400)
+
+ def test_vsa_volume_delete(self):
+ obj_num = 234 if self.test_objs == "volumes" else 345
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/%s' % \
+ (self.test_objs, obj_num))
+ req.method = 'DELETE'
+ resp = req.get_response(fakes.wsgi_app())
+ if self.test_obj == "volume":
+ self.assertEqual(resp.status_int, 202)
+ else:
+ self.assertEqual(resp.status_int, 400)
+
+ def test_vsa_volume_delete_no_vsa_assignment(self):
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/4/%s/333' % \
+ (self.test_objs))
+ req.method = 'DELETE'
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 400)
+
+ def test_vsa_volume_delete_no_volume(self):
+ self.stubs.Set(volume.api.API, "get", stub_volume_get_notfound)
+
+ req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/333' % \
+ (self.test_objs))
+ req.method = 'DELETE'
+ resp = req.get_response(fakes.wsgi_app())
+ if self.test_obj == "volume":
+ self.assertEqual(resp.status_int, 404)
+ else:
+ self.assertEqual(resp.status_int, 400)
+
+
+class VSADriveApiTest(VSAVolumeApiTest):
+ def setUp(self):
+ super(VSADriveApiTest, self).setUp(test_obj="drive",
+ test_objs="drives")
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ super(VSADriveApiTest, self).tearDown()
diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py
index 9f923852d..05267d8fb 100644
--- a/nova/tests/api/openstack/test_extensions.py
+++ b/nova/tests/api/openstack/test_extensions.py
@@ -95,8 +95,10 @@ class ExtensionControllerTest(test.TestCase):
"Quotas",
"Rescue",
"SecurityGroups",
+ "VSAs",
"VirtualInterfaces",
"Volumes",
+ "VolumeTypes",
]
self.ext_list.sort()
diff --git a/nova/tests/api/openstack/test_server_actions.py b/nova/tests/api/openstack/test_server_actions.py
index 90fe2f0b3..3dfdeb79c 100644
--- a/nova/tests/api/openstack/test_server_actions.py
+++ b/nova/tests/api/openstack/test_server_actions.py
@@ -1,14 +1,13 @@
import base64
+import datetime
import json
-import unittest
-from xml.dom import minidom
import stubout
import webob
from nova import context
-from nova import db
from nova import utils
+from nova import exception
from nova import flags
from nova.api.openstack import create_instance_helper
from nova.compute import instance_types
@@ -23,61 +22,58 @@ FLAGS = flags.FLAGS
def return_server_by_id(context, id):
- return _get_instance()
+ return stub_instance(id)
def instance_update(context, instance_id, kwargs):
- return _get_instance()
+ return stub_instance(instance_id)
-def return_server_with_power_state(power_state):
+def return_server_with_attributes(**kwargs):
def _return_server(context, id):
- instance = _get_instance()
- instance['state'] = power_state
- return instance
+ return stub_instance(id, **kwargs)
return _return_server
+def return_server_with_power_state(power_state):
+ return return_server_with_attributes(power_state=power_state)
+
+
def return_server_with_uuid_and_power_state(power_state):
- def _return_server(context, id):
- return return_server_with_power_state(power_state)
- return _return_server
+ return return_server_with_power_state(power_state)
-class MockSetAdminPassword(object):
- def __init__(self):
- self.instance_id = None
- self.password = None
+def stub_instance(id, power_state=0, metadata=None,
+ image_ref="10", flavor_id="1", name=None):
- def __call__(self, context, instance_id, password):
- self.instance_id = instance_id
- self.password = password
+ if metadata is not None:
+ metadata_items = [{'key':k, 'value':v} for k, v in metadata.items()]
+ else:
+ metadata_items = [{'key':'seq', 'value':id}]
+ inst_type = instance_types.get_instance_type_by_flavor_id(int(flavor_id))
-def _get_instance():
instance = {
- "id": 1,
- "created_at": "2010-10-10 12:00:00",
- "updated_at": "2010-11-11 11:00:00",
+ "id": int(id),
+ "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0),
+ "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0),
"admin_pass": "",
- "user_id": "",
- "project_id": "",
- "image_ref": "5",
+ "user_id": "fake",
+ "project_id": "fake",
+ "image_ref": image_ref,
"kernel_id": "",
"ramdisk_id": "",
"launch_index": 0,
"key_name": "",
"key_data": "",
- "state": 0,
+ "state": power_state,
"state_description": "",
"memory_mb": 0,
"vcpus": 0,
"local_gb": 0,
"hostname": "",
"host": "",
- "instance_type": {
- "flavorid": 1,
- },
+ "instance_type": dict(inst_type),
"user_data": "",
"reservation_id": "",
"mac_address": "",
@@ -85,17 +81,34 @@ def _get_instance():
"launched_at": utils.utcnow(),
"terminated_at": utils.utcnow(),
"availability_zone": "",
- "display_name": "test_server",
+ "display_name": name or "server%s" % id,
"display_description": "",
"locked": False,
- "metadata": [],
- #"address": ,
- #"floating_ips": [{"address":ip} for ip in public_addresses]}
- "uuid": "deadbeef-feed-edee-beef-d0ea7beefedd"}
+ "metadata": metadata_items,
+ "access_ip_v4": "",
+ "access_ip_v6": "",
+ "uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
+ "virtual_interfaces": [],
+ }
+
+ instance["fixed_ips"] = {
+ "address": '192.168.0.1',
+ "floating_ips": [],
+ }
return instance
+class MockSetAdminPassword(object):
+ def __init__(self):
+ self.instance_id = None
+ self.password = None
+
+ def __call__(self, context, instance_id, password):
+ self.instance_id = instance_id
+ self.password = password
+
+
class ServerActionsTest(test.TestCase):
def setUp(self):
@@ -103,8 +116,6 @@ class ServerActionsTest(test.TestCase):
super(ServerActionsTest, self).setUp()
self.flags(verbose=True)
self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
fakes.stub_out_auth(self.stubs)
self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id)
self.stubs.Set(nova.db.api, 'instance_update', instance_update)
@@ -468,8 +479,6 @@ class ServerActionsTestV11(test.TestCase):
self.maxDiff = None
super(ServerActionsTestV11, self).setUp()
self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
fakes.stub_out_auth(self.stubs)
self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id)
self.stubs.Set(nova.db.api, 'instance_update', instance_update)
@@ -606,6 +615,9 @@ class ServerActionsTestV11(test.TestCase):
self.assertEqual(res.status_int, 400)
def test_server_rebuild_accepted_minimum(self):
+ new_return_server = return_server_with_attributes(image_ref='2')
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+
body = {
"rebuild": {
"imageRef": "http://localhost/images/2",
@@ -619,6 +631,9 @@ class ServerActionsTestV11(test.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 202)
+ body = json.loads(res.body)
+ self.assertEqual(body['server']['image']['id'], '2')
+ self.assertEqual(len(body['server']['adminPass']), 16)
def test_server_rebuild_rejected_when_building(self):
body = {
@@ -642,12 +657,15 @@ class ServerActionsTestV11(test.TestCase):
self.assertEqual(res.status_int, 409)
def test_server_rebuild_accepted_with_metadata(self):
+ metadata = {'new': 'metadata'}
+
+ new_return_server = return_server_with_attributes(metadata=metadata)
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+
body = {
"rebuild": {
"imageRef": "http://localhost/images/2",
- "metadata": {
- "new": "metadata",
- },
+ "metadata": metadata,
},
}
@@ -658,6 +676,8 @@ class ServerActionsTestV11(test.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 202)
+ body = json.loads(res.body)
+ self.assertEqual(body['server']['metadata'], metadata)
def test_server_rebuild_accepted_with_bad_metadata(self):
body = {
@@ -727,6 +747,49 @@ class ServerActionsTestV11(test.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 202)
+ body = json.loads(res.body)
+ self.assertTrue('personality' not in body['server'])
+
+ def test_server_rebuild_admin_pass(self):
+ new_return_server = return_server_with_attributes(image_ref='2')
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+
+ body = {
+ "rebuild": {
+ "imageRef": "http://localhost/images/2",
+ "adminPass": "asdf",
+ },
+ }
+
+ req = webob.Request.blank('/v1.1/fake/servers/1/action')
+ req.method = 'POST'
+ req.content_type = 'application/json'
+ req.body = json.dumps(body)
+
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 202)
+ body = json.loads(res.body)
+ self.assertEqual(body['server']['image']['id'], '2')
+ self.assertEqual(body['server']['adminPass'], 'asdf')
+
+ def test_server_rebuild_server_not_found(self):
+ def server_not_found(self, instance_id):
+ raise exception.InstanceNotFound(instance_id=instance_id)
+ self.stubs.Set(nova.db.api, 'instance_get', server_not_found)
+
+ body = {
+ "rebuild": {
+ "imageRef": "http://localhost/images/2",
+ },
+ }
+
+ req = webob.Request.blank('/v1.1/fake/servers/1/action')
+ req.method = 'POST'
+ req.content_type = 'application/json'
+ req.body = json.dumps(body)
+
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 404)
def test_resize_server(self):
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index dd4b63a2c..3559e6de5 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010-2011 OpenStack LLC.
+# Copyright 2011 Piston Cloud Computing, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -233,7 +234,6 @@ class MockSetAdminPassword(object):
class ServersTest(test.TestCase):
-
def setUp(self):
self.maxDiff = None
super(ServersTest, self).setUp()
@@ -265,6 +265,7 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api)
self.webreq = common.webob_factory('/v1.0/servers')
+ self.config_drive = None
def test_get_server_by_id(self):
req = webob.Request.blank('/v1.0/servers/1')
@@ -379,6 +380,7 @@ class ServersTest(test.TestCase):
"metadata": {
"seq": "1",
},
+ "config_drive": None,
"links": [
{
"rel": "self",
@@ -545,6 +547,7 @@ class ServersTest(test.TestCase):
"metadata": {
"seq": "1",
},
+ "config_drive": None,
"links": [
{
"rel": "self",
@@ -638,6 +641,7 @@ class ServersTest(test.TestCase):
"metadata": {
"seq": "1",
},
+ "config_drive": None,
"links": [
{
"rel": "self",
@@ -767,6 +771,27 @@ class ServersTest(test.TestCase):
(ip,) = private_node.getElementsByTagName('ip')
self.assertEquals(ip.getAttribute('addr'), private)
+ # NOTE(bcwaldon): lp830817
+ def test_get_server_by_id_malformed_networks_v1_1(self):
+ ifaces = [
+ {
+ 'network': None,
+ 'fixed_ips': [
+ {'address': '192.168.0.3'},
+ {'address': '192.168.0.4'},
+ ],
+ },
+ ]
+ new_return_server = return_server_with_attributes(interfaces=ifaces)
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+
+ req = webob.Request.blank('/v1.1/fake/servers/1')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ res_dict = json.loads(res.body)
+ self.assertEqual(res_dict['server']['id'], 1)
+ self.assertEqual(res_dict['server']['name'], 'server1')
+
def test_get_server_by_id_with_addresses_v1_1(self):
self.flags(use_ipv6=True)
interfaces = [
@@ -1399,6 +1424,7 @@ class ServersTest(test.TestCase):
'image_ref': image_ref,
"created_at": datetime.datetime(2010, 10, 10, 12, 0, 0),
"updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0),
+ "config_drive": self.config_drive,
}
def server_update(context, id, params):
@@ -1424,8 +1450,7 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.db.api, 'instance_create', instance_create)
self.stubs.Set(nova.rpc, 'cast', fake_method)
self.stubs.Set(nova.rpc, 'call', fake_method)
- self.stubs.Set(nova.db.api, 'instance_update',
- server_update)
+ self.stubs.Set(nova.db.api, 'instance_update', server_update)
self.stubs.Set(nova.db.api, 'queue_get_for', queue_get_for)
self.stubs.Set(nova.network.manager.VlanManager, 'allocate_fixed_ip',
fake_method)
@@ -1768,6 +1793,129 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 400)
+ def test_create_instance_with_config_drive_v1_1(self):
+ self.config_drive = True
+ self._setup_for_create_instance()
+
+ image_href = 'http://localhost/v1.1/123/images/2'
+ flavor_ref = 'http://localhost/v1.1/123/flavors/3'
+ body = {
+ 'server': {
+ 'name': 'config_drive_test',
+ 'imageRef': image_href,
+ 'flavorRef': flavor_ref,
+ 'metadata': {
+ 'hello': 'world',
+ 'open': 'stack',
+ },
+ 'personality': {},
+ 'config_drive': True,
+ },
+ }
+
+ req = webob.Request.blank('/v1.1/123/servers')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+
+ res = req.get_response(fakes.wsgi_app())
+ print res
+ self.assertEqual(res.status_int, 202)
+ server = json.loads(res.body)['server']
+ self.assertEqual(1, server['id'])
+ self.assertTrue(server['config_drive'])
+
+ def test_create_instance_with_config_drive_as_id_v1_1(self):
+ self.config_drive = 2
+ self._setup_for_create_instance()
+
+ image_href = 'http://localhost/v1.1/123/images/2'
+ flavor_ref = 'http://localhost/v1.1/123/flavors/3'
+ body = {
+ 'server': {
+ 'name': 'config_drive_test',
+ 'imageRef': image_href,
+ 'flavorRef': flavor_ref,
+ 'metadata': {
+ 'hello': 'world',
+ 'open': 'stack',
+ },
+ 'personality': {},
+ 'config_drive': 2,
+ },
+ }
+
+ req = webob.Request.blank('/v1.1/123/servers')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+
+ res = req.get_response(fakes.wsgi_app())
+
+ self.assertEqual(res.status_int, 202)
+ server = json.loads(res.body)['server']
+ self.assertEqual(1, server['id'])
+ self.assertTrue(server['config_drive'])
+ self.assertEqual(2, server['config_drive'])
+
+ def test_create_instance_with_bad_config_drive_v1_1(self):
+ self.config_drive = "asdf"
+ self._setup_for_create_instance()
+
+ image_href = 'http://localhost/v1.1/123/images/2'
+ flavor_ref = 'http://localhost/v1.1/123/flavors/3'
+ body = {
+ 'server': {
+ 'name': 'config_drive_test',
+ 'imageRef': image_href,
+ 'flavorRef': flavor_ref,
+ 'metadata': {
+ 'hello': 'world',
+ 'open': 'stack',
+ },
+ 'personality': {},
+ 'config_drive': 'asdf',
+ },
+ }
+
+ req = webob.Request.blank('/v1.1/123/servers')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 400)
+
+ def test_create_instance_without_config_drive_v1_1(self):
+ self._setup_for_create_instance()
+
+ image_href = 'http://localhost/v1.1/123/images/2'
+ flavor_ref = 'http://localhost/v1.1/123/flavors/3'
+ body = {
+ 'server': {
+ 'name': 'config_drive_test',
+ 'imageRef': image_href,
+ 'flavorRef': flavor_ref,
+ 'metadata': {
+ 'hello': 'world',
+ 'open': 'stack',
+ },
+ 'personality': {},
+ 'config_drive': True,
+ },
+ }
+
+ req = webob.Request.blank('/v1.1/123/servers')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 202)
+ server = json.loads(res.body)['server']
+ self.assertEqual(1, server['id'])
+ self.assertFalse(server['config_drive'])
+
def test_create_instance_v1_1_bad_href(self):
self._setup_for_create_instance()
@@ -3449,6 +3597,7 @@ class ServersViewBuilderV11Test(test.TestCase):
"href": "http://localhost/servers/1",
},
],
+ "config_drive": None,
}
}
@@ -3461,6 +3610,7 @@ class ServersViewBuilderV11Test(test.TestCase):
"id": 1,
"uuid": self.instance['uuid'],
"name": "test_server",
+ "config_drive": None,
"links": [
{
"rel": "self",
@@ -3513,6 +3663,7 @@ class ServersViewBuilderV11Test(test.TestCase):
},
"addresses": {},
"metadata": {},
+ "config_drive": None,
"links": [
{
"rel": "self",
@@ -3566,6 +3717,7 @@ class ServersViewBuilderV11Test(test.TestCase):
},
"addresses": {},
"metadata": {},
+ "config_drive": None,
"links": [
{
"rel": "self",
@@ -3618,6 +3770,7 @@ class ServersViewBuilderV11Test(test.TestCase):
},
"addresses": {},
"metadata": {},
+ "config_drive": None,
"accessIPv4": "1.2.3.4",
"accessIPv6": "",
"links": [
@@ -3672,6 +3825,7 @@ class ServersViewBuilderV11Test(test.TestCase):
},
"addresses": {},
"metadata": {},
+ "config_drive": None,
"accessIPv4": "",
"accessIPv6": "fead::1234",
"links": [
@@ -3734,6 +3888,7 @@ class ServersViewBuilderV11Test(test.TestCase):
"Open": "Stack",
"Number": "1",
},
+ "config_drive": None,
"links": [
{
"rel": "self",
@@ -4407,3 +4562,138 @@ class ServerXMLSerializationTest(test.TestCase):
str(ip['version']))
self.assertEqual(str(ip_elem.get('addr')),
str(ip['addr']))
+
+ def test_action(self):
+ serializer = servers.ServerXMLSerializer()
+
+ fixture = {
+ "server": {
+ "id": 1,
+ "uuid": FAKE_UUID,
+ 'created': self.TIMESTAMP,
+ 'updated': self.TIMESTAMP,
+ "progress": 0,
+ "name": "test_server",
+ "status": "BUILD",
+ "accessIPv4": "1.2.3.4",
+ "accessIPv6": "fead::1234",
+ "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0",
+ "adminPass": "test_password",
+ "image": {
+ "id": "5",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": self.IMAGE_BOOKMARK,
+ },
+ ],
+ },
+ "flavor": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": self.FLAVOR_BOOKMARK,
+ },
+ ],
+ },
+ "addresses": {
+ "network_one": [
+ {
+ "version": 4,
+ "addr": "67.23.10.138",
+ },
+ {
+ "version": 6,
+ "addr": "::babe:67.23.10.138",
+ },
+ ],
+ "network_two": [
+ {
+ "version": 4,
+ "addr": "67.23.10.139",
+ },
+ {
+ "version": 6,
+ "addr": "::babe:67.23.10.139",
+ },
+ ],
+ },
+ "metadata": {
+ "Open": "Stack",
+ "Number": "1",
+ },
+ 'links': [
+ {
+ 'href': self.SERVER_HREF,
+ 'rel': 'self',
+ },
+ {
+ 'href': self.SERVER_BOOKMARK,
+ 'rel': 'bookmark',
+ },
+ ],
+ }
+ }
+
+ output = serializer.serialize(fixture, 'action')
+ root = etree.XML(output)
+ xmlutil.validate_schema(root, 'server')
+
+ expected_server_href = self.SERVER_HREF
+ expected_server_bookmark = self.SERVER_BOOKMARK
+ expected_image_bookmark = self.IMAGE_BOOKMARK
+ expected_flavor_bookmark = self.FLAVOR_BOOKMARK
+ expected_now = self.TIMESTAMP
+ expected_uuid = FAKE_UUID
+ server_dict = fixture['server']
+
+ for key in ['name', 'id', 'uuid', 'created', 'accessIPv4',
+ 'updated', 'progress', 'status', 'hostId',
+ 'accessIPv6', 'adminPass']:
+ self.assertEqual(root.get(key), str(server_dict[key]))
+
+ link_nodes = root.findall('{0}link'.format(ATOMNS))
+ self.assertEqual(len(link_nodes), 2)
+ for i, link in enumerate(server_dict['links']):
+ for key, value in link.items():
+ self.assertEqual(link_nodes[i].get(key), value)
+
+ metadata_root = root.find('{0}metadata'.format(NS))
+ metadata_elems = metadata_root.findall('{0}meta'.format(NS))
+ self.assertEqual(len(metadata_elems), 2)
+ for i, metadata_elem in enumerate(metadata_elems):
+ (meta_key, meta_value) = server_dict['metadata'].items()[i]
+ self.assertEqual(str(metadata_elem.get('key')), str(meta_key))
+ self.assertEqual(str(metadata_elem.text).strip(), str(meta_value))
+
+ image_root = root.find('{0}image'.format(NS))
+ self.assertEqual(image_root.get('id'), server_dict['image']['id'])
+ link_nodes = image_root.findall('{0}link'.format(ATOMNS))
+ self.assertEqual(len(link_nodes), 1)
+ for i, link in enumerate(server_dict['image']['links']):
+ for key, value in link.items():
+ self.assertEqual(link_nodes[i].get(key), value)
+
+ flavor_root = root.find('{0}flavor'.format(NS))
+ self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id'])
+ link_nodes = flavor_root.findall('{0}link'.format(ATOMNS))
+ self.assertEqual(len(link_nodes), 1)
+ for i, link in enumerate(server_dict['flavor']['links']):
+ for key, value in link.items():
+ self.assertEqual(link_nodes[i].get(key), value)
+
+ addresses_root = root.find('{0}addresses'.format(NS))
+ addresses_dict = server_dict['addresses']
+ network_elems = addresses_root.findall('{0}network'.format(NS))
+ self.assertEqual(len(network_elems), 2)
+ for i, network_elem in enumerate(network_elems):
+ network = addresses_dict.items()[i]
+ self.assertEqual(str(network_elem.get('id')), str(network[0]))
+ ip_elems = network_elem.findall('{0}ip'.format(NS))
+ for z, ip_elem in enumerate(ip_elems):
+ ip = network[1][z]
+ self.assertEqual(str(ip_elem.get('version')),
+ str(ip['version']))
+ self.assertEqual(str(ip_elem.get('addr')),
+ str(ip['addr']))
diff --git a/nova/tests/api/openstack/test_volume_types.py b/nova/tests/api/openstack/test_volume_types.py
new file mode 100644
index 000000000..192e66854
--- /dev/null
+++ b/nova/tests/api/openstack/test_volume_types.py
@@ -0,0 +1,171 @@
+# Copyright 2011 OpenStack LLC.
+# 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 json
+import stubout
+import webob
+
+from nova import exception
+from nova import context
+from nova import test
+from nova import log as logging
+from nova.volume import volume_types
+from nova.tests.api.openstack import fakes
+
+LOG = logging.getLogger('nova.tests.api.openstack.test_volume_types')
+
+last_param = {}
+
+
+def stub_volume_type(id):
+ specs = {
+ "key1": "value1",
+ "key2": "value2",
+ "key3": "value3",
+ "key4": "value4",
+ "key5": "value5"}
+ return dict(id=id, name='vol_type_%s' % str(id), extra_specs=specs)
+
+
+def return_volume_types_get_all_types(context):
+ return dict(vol_type_1=stub_volume_type(1),
+ vol_type_2=stub_volume_type(2),
+ vol_type_3=stub_volume_type(3))
+
+
+def return_empty_volume_types_get_all_types(context):
+ return {}
+
+
+def return_volume_types_get_volume_type(context, id):
+ if id == "777":
+ raise exception.VolumeTypeNotFound(volume_type_id=id)
+ return stub_volume_type(int(id))
+
+
+def return_volume_types_destroy(context, name):
+ if name == "777":
+ raise exception.VolumeTypeNotFoundByName(volume_type_name=name)
+ pass
+
+
+def return_volume_types_create(context, name, specs):
+ pass
+
+
+def return_volume_types_get_by_name(context, name):
+ if name == "777":
+ raise exception.VolumeTypeNotFoundByName(volume_type_name=name)
+ return stub_volume_type(int(name.split("_")[2]))
+
+
+class VolumeTypesApiTest(test.TestCase):
+ def setUp(self):
+ super(VolumeTypesApiTest, self).setUp()
+ fakes.stub_out_key_pair_funcs(self.stubs)
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ super(VolumeTypesApiTest, self).tearDown()
+
+ def test_volume_types_index(self):
+ self.stubs.Set(volume_types, 'get_all_types',
+ return_volume_types_get_all_types)
+ req = webob.Request.blank('/v1.1/123/os-volume-types')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(200, res.status_int)
+ res_dict = json.loads(res.body)
+ self.assertEqual('application/json', res.headers['Content-Type'])
+
+ self.assertEqual(3, len(res_dict))
+ for name in ['vol_type_1', 'vol_type_2', 'vol_type_3']:
+ self.assertEqual(name, res_dict[name]['name'])
+ self.assertEqual('value1', res_dict[name]['extra_specs']['key1'])
+
+ def test_volume_types_index_no_data(self):
+ self.stubs.Set(volume_types, 'get_all_types',
+ return_empty_volume_types_get_all_types)
+ req = webob.Request.blank('/v1.1/123/os-volume-types')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+ self.assertEqual(200, res.status_int)
+ self.assertEqual('application/json', res.headers['Content-Type'])
+ self.assertEqual(0, len(res_dict))
+
+ def test_volume_types_show(self):
+ self.stubs.Set(volume_types, 'get_volume_type',
+ return_volume_types_get_volume_type)
+ req = webob.Request.blank('/v1.1/123/os-volume-types/1')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(200, res.status_int)
+ res_dict = json.loads(res.body)
+ self.assertEqual('application/json', res.headers['Content-Type'])
+ self.assertEqual(1, len(res_dict))
+ self.assertEqual('vol_type_1', res_dict['volume_type']['name'])
+
+ def test_volume_types_show_not_found(self):
+ self.stubs.Set(volume_types, 'get_volume_type',
+ return_volume_types_get_volume_type)
+ req = webob.Request.blank('/v1.1/123/os-volume-types/777')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(404, res.status_int)
+
+ def test_volume_types_delete(self):
+ self.stubs.Set(volume_types, 'get_volume_type',
+ return_volume_types_get_volume_type)
+ self.stubs.Set(volume_types, 'destroy',
+ return_volume_types_destroy)
+ req = webob.Request.blank('/v1.1/123/os-volume-types/1')
+ req.method = 'DELETE'
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(200, res.status_int)
+
+ def test_volume_types_delete_not_found(self):
+ self.stubs.Set(volume_types, 'get_volume_type',
+ return_volume_types_get_volume_type)
+ self.stubs.Set(volume_types, 'destroy',
+ return_volume_types_destroy)
+ req = webob.Request.blank('/v1.1/123/os-volume-types/777')
+ req.method = 'DELETE'
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(404, res.status_int)
+
+ def test_create(self):
+ self.stubs.Set(volume_types, 'create',
+ return_volume_types_create)
+ self.stubs.Set(volume_types, 'get_volume_type_by_name',
+ return_volume_types_get_by_name)
+ req = webob.Request.blank('/v1.1/123/os-volume-types')
+ req.method = 'POST'
+ req.body = '{"volume_type": {"name": "vol_type_1", '\
+ '"extra_specs": {"key1": "value1"}}}'
+ req.headers["content-type"] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(200, res.status_int)
+ res_dict = json.loads(res.body)
+ self.assertEqual('application/json', res.headers['Content-Type'])
+ self.assertEqual(1, len(res_dict))
+ self.assertEqual('vol_type_1', res_dict['volume_type']['name'])
+
+ def test_create_empty_body(self):
+ self.stubs.Set(volume_types, 'create',
+ return_volume_types_create)
+ self.stubs.Set(volume_types, 'get_volume_type_by_name',
+ return_volume_types_get_by_name)
+ req = webob.Request.blank('/v1.1/123/os-volume-types')
+ req.method = 'POST'
+ req.headers["content-type"] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(400, res.status_int)
diff --git a/nova/tests/api/openstack/test_volume_types_extra_specs.py b/nova/tests/api/openstack/test_volume_types_extra_specs.py
new file mode 100644
index 000000000..34bdada22
--- /dev/null
+++ b/nova/tests/api/openstack/test_volume_types_extra_specs.py
@@ -0,0 +1,181 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 Zadara Storage Inc.
+# Copyright (c) 2011 OpenStack LLC.
+# Copyright 2011 University of Southern California
+# 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 json
+import stubout
+import webob
+import os.path
+
+
+from nova import test
+from nova.api import openstack
+from nova.api.openstack import extensions
+from nova.tests.api.openstack import fakes
+import nova.wsgi
+
+
+def return_create_volume_type_extra_specs(context, volume_type_id,
+ extra_specs):
+ return stub_volume_type_extra_specs()
+
+
+def return_volume_type_extra_specs(context, volume_type_id):
+ return stub_volume_type_extra_specs()
+
+
+def return_empty_volume_type_extra_specs(context, volume_type_id):
+ return {}
+
+
+def delete_volume_type_extra_specs(context, volume_type_id, key):
+ pass
+
+
+def stub_volume_type_extra_specs():
+ specs = {
+ "key1": "value1",
+ "key2": "value2",
+ "key3": "value3",
+ "key4": "value4",
+ "key5": "value5"}
+ return specs
+
+
+class VolumeTypesExtraSpecsTest(test.TestCase):
+
+ def setUp(self):
+ super(VolumeTypesExtraSpecsTest, self).setUp()
+ fakes.stub_out_key_pair_funcs(self.stubs)
+ self.api_path = '/v1.1/123/os-volume-types/1/extra_specs'
+
+ def test_index(self):
+ self.stubs.Set(nova.db.api, 'volume_type_extra_specs_get',
+ return_volume_type_extra_specs)
+ request = webob.Request.blank(self.api_path)
+ res = request.get_response(fakes.wsgi_app())
+ self.assertEqual(200, res.status_int)
+ res_dict = json.loads(res.body)
+ self.assertEqual('application/json', res.headers['Content-Type'])
+ self.assertEqual('value1', res_dict['extra_specs']['key1'])
+
+ def test_index_no_data(self):
+ self.stubs.Set(nova.db.api, 'volume_type_extra_specs_get',
+ return_empty_volume_type_extra_specs)
+ req = webob.Request.blank(self.api_path)
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+ self.assertEqual(200, res.status_int)
+ self.assertEqual('application/json', res.headers['Content-Type'])
+ self.assertEqual(0, len(res_dict['extra_specs']))
+
+ def test_show(self):
+ self.stubs.Set(nova.db.api, 'volume_type_extra_specs_get',
+ return_volume_type_extra_specs)
+ req = webob.Request.blank(self.api_path + '/key5')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(200, res.status_int)
+ res_dict = json.loads(res.body)
+ self.assertEqual('application/json', res.headers['Content-Type'])
+ self.assertEqual('value5', res_dict['key5'])
+
+ def test_show_spec_not_found(self):
+ self.stubs.Set(nova.db.api, 'volume_type_extra_specs_get',
+ return_empty_volume_type_extra_specs)
+ req = webob.Request.blank(self.api_path + '/key6')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+ self.assertEqual(404, res.status_int)
+
+ def test_delete(self):
+ self.stubs.Set(nova.db.api, 'volume_type_extra_specs_delete',
+ delete_volume_type_extra_specs)
+ req = webob.Request.blank(self.api_path + '/key5')
+ req.method = 'DELETE'
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(200, res.status_int)
+
+ def test_create(self):
+ self.stubs.Set(nova.db.api,
+ 'volume_type_extra_specs_update_or_create',
+ return_create_volume_type_extra_specs)
+ req = webob.Request.blank(self.api_path)
+ req.method = 'POST'
+ req.body = '{"extra_specs": {"key1": "value1"}}'
+ req.headers["content-type"] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+ self.assertEqual(200, res.status_int)
+ self.assertEqual('application/json', res.headers['Content-Type'])
+ self.assertEqual('value1', res_dict['extra_specs']['key1'])
+
+ def test_create_empty_body(self):
+ self.stubs.Set(nova.db.api,
+ 'volume_type_extra_specs_update_or_create',
+ return_create_volume_type_extra_specs)
+ req = webob.Request.blank(self.api_path)
+ req.method = 'POST'
+ req.headers["content-type"] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(400, res.status_int)
+
+ def test_update_item(self):
+ self.stubs.Set(nova.db.api,
+ 'volume_type_extra_specs_update_or_create',
+ return_create_volume_type_extra_specs)
+ req = webob.Request.blank(self.api_path + '/key1')
+ req.method = 'PUT'
+ req.body = '{"key1": "value1"}'
+ req.headers["content-type"] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(200, res.status_int)
+ self.assertEqual('application/json', res.headers['Content-Type'])
+ res_dict = json.loads(res.body)
+ self.assertEqual('value1', res_dict['key1'])
+
+ def test_update_item_empty_body(self):
+ self.stubs.Set(nova.db.api,
+ 'volume_type_extra_specs_update_or_create',
+ return_create_volume_type_extra_specs)
+ req = webob.Request.blank(self.api_path + '/key1')
+ req.method = 'PUT'
+ req.headers["content-type"] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(400, res.status_int)
+
+ def test_update_item_too_many_keys(self):
+ self.stubs.Set(nova.db.api,
+ 'volume_type_extra_specs_update_or_create',
+ return_create_volume_type_extra_specs)
+ req = webob.Request.blank(self.api_path + '/key1')
+ req.method = 'PUT'
+ req.body = '{"key1": "value1", "key2": "value2"}'
+ req.headers["content-type"] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(400, res.status_int)
+
+ def test_update_item_body_uri_mismatch(self):
+ self.stubs.Set(nova.db.api,
+ 'volume_type_extra_specs_update_or_create',
+ return_create_volume_type_extra_specs)
+ req = webob.Request.blank(self.api_path + '/bad')
+ req.method = 'PUT'
+ req.body = '{"key1": "value1"}'
+ req.headers["content-type"] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(400, res.status_int)
diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py
index fb2f88502..343190427 100644
--- a/nova/tests/integrated/integrated_helpers.py
+++ b/nova/tests/integrated/integrated_helpers.py
@@ -22,10 +22,8 @@ Provides common functionality for integrated unit tests
import random
import string
-from nova import exception
from nova import service
from nova import test # For the flags
-from nova.auth import manager
import nova.image.glance
from nova.log import logging
from nova.tests.integrated.api import client
@@ -58,90 +56,6 @@ def generate_new_element(items, prefix, numeric=False):
LOG.debug("Random collision on %s" % candidate)
-class TestUser(object):
- def __init__(self, name, secret, auth_url):
- self.name = name
- self.secret = secret
- self.auth_url = auth_url
-
- if not auth_url:
- raise exception.Error("auth_url is required")
- self.openstack_api = client.TestOpenStackClient(self.name,
- 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 currently 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):
- def __init__(self, auth_url):
- self.auth_manager = manager.AuthManager()
-
- self.auth_url = auth_url
- self.project_name = None
-
- self.test_user = None
-
- self.setup()
-
- def setup(self):
- self._create_test_user()
-
- def _create_test_user(self):
- self.test_user = self._create_unittest_user()
-
- # No way to currently pass this through the OpenStack API
- self.project_name = 'openstack'
- self._configure_project(self.project_name, self.test_user)
-
- def cleanup(self):
- self.test_user = None
-
- def _create_unittest_user(self):
- users = self.auth_manager.get_users()
- user_names = [user.name for user in users]
- auth_name = generate_new_element(user_names, 'unittest_user_')
- auth_key = generate_random_alphanumeric(16)
-
- # Right now there's a bug where auth_name and auth_key are reversed
- # bug732907
- auth_key = auth_name
-
- self.auth_manager.create_user(auth_name, auth_name, auth_key, False)
- return TestUser(auth_name, auth_key, self.auth_url)
-
- def _configure_project(self, project_name, user):
- projects = self.auth_manager.get_projects()
- project_names = [project.name for project in projects]
- if not project_name in project_names:
- project = self.auth_manager.create_project(project_name,
- user.name,
- description=None,
- member_users=None)
- else:
- self.auth_manager.add_to_project(user.name, project_name)
-
-
class _IntegratedTestBase(test.TestCase):
def setUp(self):
super(_IntegratedTestBase, self).setUp()
@@ -163,10 +77,7 @@ class _IntegratedTestBase(test.TestCase):
self._start_api_service()
- self.context = IntegratedUnitTestContext(self.auth_url)
-
- self.user = self.context.test_user
- self.api = self.user.openstack_api
+ self.api = client.TestOpenStackClient('fake', 'fake', self.auth_url)
def _start_api_service(self):
osapi = service.WSGIService("osapi")
@@ -174,10 +85,6 @@ class _IntegratedTestBase(test.TestCase):
self.auth_url = 'http://%s:%s/v1.1' % (osapi.host, osapi.port)
LOG.warn(self.auth_url)
- def tearDown(self):
- self.context.cleanup()
- super(_IntegratedTestBase, self).tearDown()
-
def _get_flags(self):
"""An opportunity to setup flags, before the services are started."""
f = {}
@@ -190,10 +97,20 @@ class _IntegratedTestBase(test.TestCase):
f['fake_network'] = True
return f
+ def get_unused_server_name(self):
+ servers = self.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.api.get_images()
+ image_ids = [image['id'] for image in images]
+ return generate_new_element(image_ids, '', numeric=True)
+
def _build_minimal_create_server_request(self):
server = {}
- image = self.user.get_valid_image(create=True)
+ image = self.api.get_images()[0]
LOG.debug("Image: %s" % image)
if 'imageRef' in image:
@@ -211,7 +128,7 @@ class _IntegratedTestBase(test.TestCase):
server['flavorRef'] = 'http://fake.server/%s' % flavor['id']
# Set a valid server name
- server_name = self.user.get_unused_server_name()
+ server_name = self.get_unused_server_name()
server['name'] = server_name
return server
diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py
index 9d1925bc0..3a863d0f9 100644
--- a/nova/tests/integrated/test_login.py
+++ b/nova/tests/integrated/test_login.py
@@ -15,11 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-import unittest
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')
@@ -31,40 +29,3 @@ class LoginTest(integrated_helpers._IntegratedTestBase):
flavors = self.api.get_flavors()
for flavor in flavors:
LOG.debug(_("flavor: %s") % flavor)
-
- def test_bad_login_password(self):
- """Test that I get a 401 with a bad username."""
- bad_credentials_api = client.TestOpenStackClient(self.user.name,
- "notso_password",
- self.user.auth_url)
-
- self.assertRaises(client.OpenStackApiAuthenticationException,
- bad_credentials_api.get_flavors)
-
- def test_bad_login_username(self):
- """Test that I get a 401 with a bad password."""
- bad_credentials_api = client.TestOpenStackClient("notso_username",
- self.user.secret,
- self.user.auth_url)
-
- self.assertRaises(client.OpenStackApiAuthenticationException,
- bad_credentials_api.get_flavors)
-
- def test_bad_login_both_bad(self):
- """Test that I get a 401 with both bad username and bad password."""
- bad_credentials_api = client.TestOpenStackClient("notso_username",
- "notso_password",
- self.user.auth_url)
-
- self.assertRaises(client.OpenStackApiAuthenticationException,
- bad_credentials_api.get_flavors)
-
- def test_good_login_bad_project(self):
- """Test that I get a 401 with valid user/pass but bad project"""
- self.api.project_id = 'openstackBAD'
-
- self.assertRaises(client.OpenStackApiAuthorizationException,
- self.api.get_flavors)
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py
index 725f6d529..b9382038a 100644
--- a/nova/tests/integrated/test_servers.py
+++ b/nova/tests/integrated/test_servers.py
@@ -51,7 +51,7 @@ class ServersTest(integrated_helpers._IntegratedTestBase):
self.api.post_server, post)
# With an invalid imageRef, this throws 500.
- server['imageRef'] = self.user.get_invalid_image()
+ server['imageRef'] = self.get_invalid_image()
# TODO(justinsb): Check whatever the spec says should be thrown here
self.assertRaises(client.OpenStackApiException,
self.api.post_server, post)
@@ -193,7 +193,7 @@ class ServersTest(integrated_helpers._IntegratedTestBase):
# rebuild the server with metadata
post = {}
post['rebuild'] = {
- "imageRef": "https://localhost/v1.1/32278/images/2",
+ "imageRef": "https://localhost/v1.1/32278/images/3",
"name": "blah",
}
@@ -205,6 +205,7 @@ class ServersTest(integrated_helpers._IntegratedTestBase):
self.assertEqual(created_server_id, found_server['id'])
self.assertEqual({}, found_server.get('metadata'))
self.assertEqual('blah', found_server.get('name'))
+ self.assertEqual('3', found_server.get('image')['id'])
# Cleanup
self._delete_server(created_server_id)
diff --git a/nova/tests/integrated/test_volumes.py b/nova/tests/integrated/test_volumes.py
index d3e936462..d6c5e1ba1 100644
--- a/nova/tests/integrated/test_volumes.py
+++ b/nova/tests/integrated/test_volumes.py
@@ -285,6 +285,23 @@ class VolumesTest(integrated_helpers._IntegratedTestBase):
self.assertEquals(undisco_move['mountpoint'], device)
self.assertEquals(undisco_move['instance_id'], server_id)
+ def test_create_volume_with_metadata(self):
+ """Creates and deletes a volume."""
+
+ # Create volume
+ metadata = {'key1': 'value1',
+ 'key2': 'value2'}
+ created_volume = self.api.post_volume(
+ {'volume': {'size': 1,
+ 'metadata': metadata}})
+ LOG.debug("created_volume: %s" % created_volume)
+ self.assertTrue(created_volume['id'])
+ created_volume_id = created_volume['id']
+
+ # Check it's there and metadata present
+ found_volume = self.api.get_volume(created_volume_id)
+ self.assertEqual(created_volume_id, found_volume['id'])
+ self.assertEqual(metadata, found_volume['metadata'])
if __name__ == "__main__":
unittest.main()
diff --git a/nova/tests/monkey_patch_example/__init__.py b/nova/tests/monkey_patch_example/__init__.py
new file mode 100644
index 000000000..25cf9ccfe
--- /dev/null
+++ b/nova/tests/monkey_patch_example/__init__.py
@@ -0,0 +1,33 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack LLC.
+# 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.
+"""Example Module for testing utils.monkey_patch()."""
+
+
+CALLED_FUNCTION = []
+
+
+def example_decorator(name, function):
+ """ decorator for notify which is used from utils.monkey_patch()
+
+ :param name: name of the function
+ :param function: - object of the function
+ :returns: function -- decorated function
+ """
+ def wrapped_func(*args, **kwarg):
+ CALLED_FUNCTION.append(name)
+ return function(*args, **kwarg)
+ return wrapped_func
diff --git a/nova/tests/monkey_patch_example/example_a.py b/nova/tests/monkey_patch_example/example_a.py
new file mode 100644
index 000000000..21e79bcb0
--- /dev/null
+++ b/nova/tests/monkey_patch_example/example_a.py
@@ -0,0 +1,29 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack LLC.
+# 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.
+"""Example Module A for testing utils.monkey_patch()."""
+
+
+def example_function_a():
+ return 'Example function'
+
+
+class ExampleClassA():
+ def example_method(self):
+ return 'Example method'
+
+ def example_method_add(self, arg1, arg2):
+ return arg1 + arg2
diff --git a/nova/tests/monkey_patch_example/example_b.py b/nova/tests/monkey_patch_example/example_b.py
new file mode 100644
index 000000000..9d8f6d339
--- /dev/null
+++ b/nova/tests/monkey_patch_example/example_b.py
@@ -0,0 +1,30 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack LLC.
+# 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.
+
+"""Example Module B for testing utils.monkey_patch()."""
+
+
+def example_function_b():
+ return 'Example function'
+
+
+class ExampleClassB():
+ def example_method(self):
+ return 'Example method'
+
+ def example_method_add(self, arg1, arg2):
+ return arg1 + arg2
diff --git a/nova/tests/notifier/__init__.py b/nova/tests/notifier/__init__.py
new file mode 100644
index 000000000..bd862c46a
--- /dev/null
+++ b/nova/tests/notifier/__init__.py
@@ -0,0 +1,16 @@
+# Copyright 2011 Openstack LLC.
+# 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.
+
+from nova.tests import *
diff --git a/nova/tests/notifier/test_list_notifier.py b/nova/tests/notifier/test_list_notifier.py
new file mode 100644
index 000000000..b77720759
--- /dev/null
+++ b/nova/tests/notifier/test_list_notifier.py
@@ -0,0 +1,88 @@
+# Copyright 2011 OpenStack LLC.
+# 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 stubout
+import sys
+
+import nova
+from nova import log as logging
+import nova.notifier.api
+from nova.notifier.api import notify
+from nova.notifier import log_notifier
+from nova.notifier import no_op_notifier
+from nova.notifier import list_notifier
+from nova import test
+
+
+class NotifierListTestCase(test.TestCase):
+ """Test case for notifications"""
+
+ def setUp(self):
+ super(NotifierListTestCase, self).setUp()
+ list_notifier._reset_drivers()
+ self.stubs = stubout.StubOutForTesting()
+ # Mock log to add one to exception_count when log.exception is called
+
+ def mock_exception(cls, *args):
+ self.exception_count += 1
+
+ self.exception_count = 0
+ list_notifier_log = logging.getLogger('nova.notifier.list_notifier')
+ self.stubs.Set(list_notifier_log, "exception", mock_exception)
+ # Mock no_op notifier to add one to notify_count when called.
+
+ def mock_notify(cls, *args):
+ self.notify_count += 1
+
+ self.notify_count = 0
+ self.stubs.Set(nova.notifier.no_op_notifier, 'notify', mock_notify)
+ # Mock log_notifier to raise RuntimeError when called.
+
+ def mock_notify2(cls, *args):
+ raise RuntimeError("Bad notifier.")
+
+ self.stubs.Set(nova.notifier.log_notifier, 'notify', mock_notify2)
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ list_notifier._reset_drivers()
+ super(NotifierListTestCase, self).tearDown()
+
+ def test_send_notifications_successfully(self):
+ self.flags(notification_driver='nova.notifier.list_notifier',
+ list_notifier_drivers=['nova.notifier.no_op_notifier',
+ 'nova.notifier.no_op_notifier'])
+ notify('publisher_id', 'event_type',
+ nova.notifier.api.WARN, dict(a=3))
+ self.assertEqual(self.notify_count, 2)
+ self.assertEqual(self.exception_count, 0)
+
+ def test_send_notifications_with_errors(self):
+
+ self.flags(notification_driver='nova.notifier.list_notifier',
+ list_notifier_drivers=['nova.notifier.no_op_notifier',
+ 'nova.notifier.log_notifier'])
+ notify('publisher_id', 'event_type', nova.notifier.api.WARN, dict(a=3))
+ self.assertEqual(self.notify_count, 1)
+ self.assertEqual(self.exception_count, 1)
+
+ def test_when_driver_fails_to_import(self):
+ self.flags(notification_driver='nova.notifier.list_notifier',
+ list_notifier_drivers=['nova.notifier.no_op_notifier',
+ 'nova.notifier.logo_notifier',
+ 'fdsjgsdfhjkhgsfkj'])
+ notify('publisher_id', 'event_type', nova.notifier.api.WARN, dict(a=3))
+ self.assertEqual(self.exception_count, 2)
+ self.assertEqual(self.notify_count, 1)
diff --git a/nova/tests/scheduler/test_vsa_scheduler.py b/nova/tests/scheduler/test_vsa_scheduler.py
new file mode 100644
index 000000000..37964f00d
--- /dev/null
+++ b/nova/tests/scheduler/test_vsa_scheduler.py
@@ -0,0 +1,641 @@
+# Copyright 2011 OpenStack LLC.
+# 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 stubout
+
+import nova
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import test
+from nova import utils
+from nova.volume import volume_types
+
+from nova.scheduler import vsa as vsa_sched
+from nova.scheduler import driver
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.scheduler.vsa')
+
+scheduled_volumes = []
+scheduled_volume = {}
+global_volume = {}
+
+
+class FakeVsaLeastUsedScheduler(
+ vsa_sched.VsaSchedulerLeastUsedHost):
+ # No need to stub anything at the moment
+ pass
+
+
+class FakeVsaMostAvailCapacityScheduler(
+ vsa_sched.VsaSchedulerMostAvailCapacity):
+ # No need to stub anything at the moment
+ pass
+
+
+class VsaSchedulerTestCase(test.TestCase):
+
+ def _get_vol_creation_request(self, num_vols, drive_ix, size=0):
+ volume_params = []
+ for i in range(num_vols):
+
+ name = 'name_' + str(i)
+ try:
+ volume_types.create(self.context, name,
+ extra_specs={'type': 'vsa_drive',
+ 'drive_name': name,
+ 'drive_type': 'type_' + str(drive_ix),
+ 'drive_size': 1 + 100 * (drive_ix)})
+ self.created_types_lst.append(name)
+ except exception.ApiError:
+ # type is already created
+ pass
+
+ volume_type = volume_types.get_volume_type_by_name(self.context,
+ name)
+ volume = {'size': size,
+ 'snapshot_id': None,
+ 'name': 'vol_' + str(i),
+ 'description': None,
+ 'volume_type_id': volume_type['id']}
+ volume_params.append(volume)
+
+ return {'num_volumes': len(volume_params),
+ 'vsa_id': 123,
+ 'volumes': volume_params}
+
+ def _generate_default_service_states(self):
+ service_states = {}
+ for i in range(self.host_num):
+ host = {}
+ hostname = 'host_' + str(i)
+ if hostname in self.exclude_host_list:
+ continue
+
+ host['volume'] = {'timestamp': utils.utcnow(),
+ 'drive_qos_info': {}}
+
+ for j in range(self.drive_type_start_ix,
+ self.drive_type_start_ix + self.drive_type_num):
+ dtype = {}
+ dtype['Name'] = 'name_' + str(j)
+ dtype['DriveType'] = 'type_' + str(j)
+ dtype['TotalDrives'] = 2 * (self.init_num_drives + i)
+ dtype['DriveCapacity'] = vsa_sched.GB_TO_BYTES(1 + 100 * j)
+ dtype['TotalCapacity'] = dtype['TotalDrives'] * \
+ dtype['DriveCapacity']
+ dtype['AvailableCapacity'] = (dtype['TotalDrives'] - i) * \
+ dtype['DriveCapacity']
+ dtype['DriveRpm'] = 7200
+ dtype['DifCapable'] = 0
+ dtype['SedCapable'] = 0
+ dtype['PartitionDrive'] = {
+ 'PartitionSize': 0,
+ 'NumOccupiedPartitions': 0,
+ 'NumFreePartitions': 0}
+ dtype['FullDrive'] = {
+ 'NumFreeDrives': dtype['TotalDrives'] - i,
+ 'NumOccupiedDrives': i}
+ host['volume']['drive_qos_info'][dtype['Name']] = dtype
+
+ service_states[hostname] = host
+
+ return service_states
+
+ def _print_service_states(self):
+ for host, host_val in self.service_states.iteritems():
+ LOG.info(_("Host %s"), host)
+ total_used = 0
+ total_available = 0
+ qos = host_val['volume']['drive_qos_info']
+
+ for k, d in qos.iteritems():
+ LOG.info("\t%s: type %s: drives (used %2d, total %2d) "\
+ "size %3d, total %4d, used %4d, avail %d",
+ k, d['DriveType'],
+ d['FullDrive']['NumOccupiedDrives'], d['TotalDrives'],
+ vsa_sched.BYTES_TO_GB(d['DriveCapacity']),
+ vsa_sched.BYTES_TO_GB(d['TotalCapacity']),
+ vsa_sched.BYTES_TO_GB(d['TotalCapacity'] - \
+ d['AvailableCapacity']),
+ vsa_sched.BYTES_TO_GB(d['AvailableCapacity']))
+
+ total_used += vsa_sched.BYTES_TO_GB(d['TotalCapacity'] - \
+ d['AvailableCapacity'])
+ total_available += vsa_sched.BYTES_TO_GB(
+ d['AvailableCapacity'])
+ LOG.info("Host %s: used %d, avail %d",
+ host, total_used, total_available)
+
+ def _set_service_states(self, host_num,
+ drive_type_start_ix, drive_type_num,
+ init_num_drives=10,
+ exclude_host_list=[]):
+ self.host_num = host_num
+ self.drive_type_start_ix = drive_type_start_ix
+ self.drive_type_num = drive_type_num
+ self.exclude_host_list = exclude_host_list
+ self.init_num_drives = init_num_drives
+ self.service_states = self._generate_default_service_states()
+
+ def _get_service_states(self):
+ return self.service_states
+
+ def _fake_get_service_states(self):
+ return self._get_service_states()
+
+ def _fake_provision_volume(self, context, vol, vsa_id, availability_zone):
+ global scheduled_volumes
+ scheduled_volumes.append(dict(vol=vol,
+ vsa_id=vsa_id,
+ az=availability_zone))
+ name = vol['name']
+ host = vol['host']
+ LOG.debug(_("Test: provision vol %(name)s on host %(host)s"),
+ locals())
+ LOG.debug(_("\t vol=%(vol)s"), locals())
+ pass
+
+ def _fake_vsa_update(self, context, vsa_id, values):
+ LOG.debug(_("Test: VSA update request: vsa_id=%(vsa_id)s "\
+ "values=%(values)s"), locals())
+ pass
+
+ def _fake_volume_create(self, context, options):
+ LOG.debug(_("Test: Volume create: %s"), options)
+ options['id'] = 123
+ global global_volume
+ global_volume = options
+ return options
+
+ def _fake_volume_get(self, context, volume_id):
+ LOG.debug(_("Test: Volume get request: id=%(volume_id)s"), locals())
+ global global_volume
+ global_volume['id'] = volume_id
+ global_volume['availability_zone'] = None
+ return global_volume
+
+ def _fake_volume_update(self, context, volume_id, values):
+ LOG.debug(_("Test: Volume update request: id=%(volume_id)s "\
+ "values=%(values)s"), locals())
+ global scheduled_volume
+ scheduled_volume = {'id': volume_id, 'host': values['host']}
+ pass
+
+ def _fake_service_get_by_args(self, context, host, binary):
+ return "service"
+
+ def _fake_service_is_up_True(self, service):
+ return True
+
+ def _fake_service_is_up_False(self, service):
+ return False
+
+ def setUp(self, sched_class=None):
+ super(VsaSchedulerTestCase, self).setUp()
+ self.stubs = stubout.StubOutForTesting()
+ self.context = context.get_admin_context()
+
+ if sched_class is None:
+ self.sched = FakeVsaLeastUsedScheduler()
+ else:
+ self.sched = sched_class
+
+ self.host_num = 10
+ self.drive_type_num = 5
+
+ self.stubs.Set(self.sched,
+ '_get_service_states', self._fake_get_service_states)
+ self.stubs.Set(self.sched,
+ '_provision_volume', self._fake_provision_volume)
+ self.stubs.Set(nova.db, 'vsa_update', self._fake_vsa_update)
+
+ self.stubs.Set(nova.db, 'volume_get', self._fake_volume_get)
+ self.stubs.Set(nova.db, 'volume_update', self._fake_volume_update)
+
+ self.created_types_lst = []
+
+ def tearDown(self):
+ for name in self.created_types_lst:
+ volume_types.purge(self.context, name)
+
+ self.stubs.UnsetAll()
+ super(VsaSchedulerTestCase, self).tearDown()
+
+ def test_vsa_sched_create_volumes_simple(self):
+ global scheduled_volumes
+ scheduled_volumes = []
+ self._set_service_states(host_num=10,
+ drive_type_start_ix=0,
+ drive_type_num=5,
+ init_num_drives=10,
+ exclude_host_list=['host_1', 'host_3'])
+ prev = self._generate_default_service_states()
+ request_spec = self._get_vol_creation_request(num_vols=3, drive_ix=2)
+
+ self.sched.schedule_create_volumes(self.context,
+ request_spec,
+ availability_zone=None)
+
+ self.assertEqual(len(scheduled_volumes), 3)
+ self.assertEqual(scheduled_volumes[0]['vol']['host'], 'host_0')
+ self.assertEqual(scheduled_volumes[1]['vol']['host'], 'host_2')
+ self.assertEqual(scheduled_volumes[2]['vol']['host'], 'host_4')
+
+ cur = self._get_service_states()
+ for host in ['host_0', 'host_2', 'host_4']:
+ cur_dtype = cur[host]['volume']['drive_qos_info']['name_2']
+ prev_dtype = prev[host]['volume']['drive_qos_info']['name_2']
+ self.assertEqual(cur_dtype['DriveType'], prev_dtype['DriveType'])
+ self.assertEqual(cur_dtype['FullDrive']['NumFreeDrives'],
+ prev_dtype['FullDrive']['NumFreeDrives'] - 1)
+ self.assertEqual(cur_dtype['FullDrive']['NumOccupiedDrives'],
+ prev_dtype['FullDrive']['NumOccupiedDrives'] + 1)
+
+ def test_vsa_sched_no_drive_type(self):
+ self._set_service_states(host_num=10,
+ drive_type_start_ix=0,
+ drive_type_num=5,
+ init_num_drives=1)
+ request_spec = self._get_vol_creation_request(num_vols=1, drive_ix=6)
+ self.assertRaises(driver.WillNotSchedule,
+ self.sched.schedule_create_volumes,
+ self.context,
+ request_spec,
+ availability_zone=None)
+
+ def test_vsa_sched_no_enough_drives(self):
+ global scheduled_volumes
+ scheduled_volumes = []
+
+ self._set_service_states(host_num=3,
+ drive_type_start_ix=0,
+ drive_type_num=1,
+ init_num_drives=0)
+ prev = self._generate_default_service_states()
+ request_spec = self._get_vol_creation_request(num_vols=3, drive_ix=0)
+
+ self.assertRaises(driver.WillNotSchedule,
+ self.sched.schedule_create_volumes,
+ self.context,
+ request_spec,
+ availability_zone=None)
+
+ # check that everything was returned back
+ cur = self._get_service_states()
+ for k, v in prev.iteritems():
+ self.assertEqual(prev[k]['volume']['drive_qos_info'],
+ cur[k]['volume']['drive_qos_info'])
+
+ def test_vsa_sched_wrong_topic(self):
+ self._set_service_states(host_num=1,
+ drive_type_start_ix=0,
+ drive_type_num=5,
+ init_num_drives=1)
+ states = self._get_service_states()
+ new_states = {}
+ new_states['host_0'] = {'compute': states['host_0']['volume']}
+ self.service_states = new_states
+ request_spec = self._get_vol_creation_request(num_vols=1, drive_ix=0)
+
+ self.assertRaises(driver.WillNotSchedule,
+ self.sched.schedule_create_volumes,
+ self.context,
+ request_spec,
+ availability_zone=None)
+
+ def test_vsa_sched_provision_volume(self):
+ global global_volume
+ global_volume = {}
+ self._set_service_states(host_num=1,
+ drive_type_start_ix=0,
+ drive_type_num=1,
+ init_num_drives=1)
+ request_spec = self._get_vol_creation_request(num_vols=1, drive_ix=0)
+
+ self.stubs.UnsetAll()
+ self.stubs.Set(self.sched,
+ '_get_service_states', self._fake_get_service_states)
+ self.stubs.Set(nova.db, 'volume_create', self._fake_volume_create)
+
+ self.sched.schedule_create_volumes(self.context,
+ request_spec,
+ availability_zone=None)
+
+ self.assertEqual(request_spec['volumes'][0]['name'],
+ global_volume['display_name'])
+
+ def test_vsa_sched_no_free_drives(self):
+ self._set_service_states(host_num=1,
+ drive_type_start_ix=0,
+ drive_type_num=1,
+ init_num_drives=1)
+ request_spec = self._get_vol_creation_request(num_vols=1, drive_ix=0)
+
+ self.sched.schedule_create_volumes(self.context,
+ request_spec,
+ availability_zone=None)
+
+ cur = self._get_service_states()
+ cur_dtype = cur['host_0']['volume']['drive_qos_info']['name_0']
+ self.assertEqual(cur_dtype['FullDrive']['NumFreeDrives'], 1)
+
+ new_request = self._get_vol_creation_request(num_vols=1, drive_ix=0)
+
+ self.sched.schedule_create_volumes(self.context,
+ request_spec,
+ availability_zone=None)
+ self._print_service_states()
+
+ self.assertRaises(driver.WillNotSchedule,
+ self.sched.schedule_create_volumes,
+ self.context,
+ new_request,
+ availability_zone=None)
+
+ def test_vsa_sched_forced_host(self):
+ global scheduled_volumes
+ scheduled_volumes = []
+
+ self._set_service_states(host_num=10,
+ drive_type_start_ix=0,
+ drive_type_num=5,
+ init_num_drives=10)
+
+ request_spec = self._get_vol_creation_request(num_vols=3, drive_ix=2)
+
+ self.assertRaises(exception.HostBinaryNotFound,
+ self.sched.schedule_create_volumes,
+ self.context,
+ request_spec,
+ availability_zone="nova:host_5")
+
+ self.stubs.Set(nova.db,
+ 'service_get_by_args', self._fake_service_get_by_args)
+ self.stubs.Set(self.sched,
+ 'service_is_up', self._fake_service_is_up_False)
+
+ self.assertRaises(driver.WillNotSchedule,
+ self.sched.schedule_create_volumes,
+ self.context,
+ request_spec,
+ availability_zone="nova:host_5")
+
+ self.stubs.Set(self.sched,
+ 'service_is_up', self._fake_service_is_up_True)
+
+ self.sched.schedule_create_volumes(self.context,
+ request_spec,
+ availability_zone="nova:host_5")
+
+ self.assertEqual(len(scheduled_volumes), 3)
+ self.assertEqual(scheduled_volumes[0]['vol']['host'], 'host_5')
+ self.assertEqual(scheduled_volumes[1]['vol']['host'], 'host_5')
+ self.assertEqual(scheduled_volumes[2]['vol']['host'], 'host_5')
+
+ def test_vsa_sched_create_volumes_partition(self):
+ global scheduled_volumes
+ scheduled_volumes = []
+ self._set_service_states(host_num=5,
+ drive_type_start_ix=0,
+ drive_type_num=5,
+ init_num_drives=1,
+ exclude_host_list=['host_0', 'host_2'])
+ prev = self._generate_default_service_states()
+ request_spec = self._get_vol_creation_request(num_vols=3,
+ drive_ix=3,
+ size=50)
+ self.sched.schedule_create_volumes(self.context,
+ request_spec,
+ availability_zone=None)
+
+ self.assertEqual(len(scheduled_volumes), 3)
+ self.assertEqual(scheduled_volumes[0]['vol']['host'], 'host_1')
+ self.assertEqual(scheduled_volumes[1]['vol']['host'], 'host_3')
+ self.assertEqual(scheduled_volumes[2]['vol']['host'], 'host_4')
+
+ cur = self._get_service_states()
+ for host in ['host_1', 'host_3', 'host_4']:
+ cur_dtype = cur[host]['volume']['drive_qos_info']['name_3']
+ prev_dtype = prev[host]['volume']['drive_qos_info']['name_3']
+
+ self.assertEqual(cur_dtype['DriveType'], prev_dtype['DriveType'])
+ self.assertEqual(cur_dtype['FullDrive']['NumFreeDrives'],
+ prev_dtype['FullDrive']['NumFreeDrives'] - 1)
+ self.assertEqual(cur_dtype['FullDrive']['NumOccupiedDrives'],
+ prev_dtype['FullDrive']['NumOccupiedDrives'] + 1)
+
+ self.assertEqual(prev_dtype['PartitionDrive']
+ ['NumOccupiedPartitions'], 0)
+ self.assertEqual(cur_dtype['PartitionDrive']
+ ['NumOccupiedPartitions'], 1)
+ self.assertEqual(cur_dtype['PartitionDrive']
+ ['NumFreePartitions'], 5)
+
+ self.assertEqual(prev_dtype['PartitionDrive']
+ ['NumFreePartitions'], 0)
+ self.assertEqual(prev_dtype['PartitionDrive']
+ ['PartitionSize'], 0)
+
+ def test_vsa_sched_create_single_volume_az(self):
+ global scheduled_volume
+ scheduled_volume = {}
+
+ def _fake_volume_get_az(context, volume_id):
+ LOG.debug(_("Test: Volume get: id=%(volume_id)s"), locals())
+ return {'id': volume_id, 'availability_zone': 'nova:host_3'}
+
+ self.stubs.Set(nova.db, 'volume_get', _fake_volume_get_az)
+ self.stubs.Set(nova.db,
+ 'service_get_by_args', self._fake_service_get_by_args)
+ self.stubs.Set(self.sched,
+ 'service_is_up', self._fake_service_is_up_True)
+
+ host = self.sched.schedule_create_volume(self.context,
+ 123, availability_zone=None)
+
+ self.assertEqual(host, 'host_3')
+ self.assertEqual(scheduled_volume['id'], 123)
+ self.assertEqual(scheduled_volume['host'], 'host_3')
+
+ def test_vsa_sched_create_single_non_vsa_volume(self):
+ global scheduled_volume
+ scheduled_volume = {}
+
+ global global_volume
+ global_volume = {}
+ global_volume['volume_type_id'] = None
+
+ self.assertRaises(driver.NoValidHost,
+ self.sched.schedule_create_volume,
+ self.context,
+ 123,
+ availability_zone=None)
+
+ def test_vsa_sched_create_single_volume(self):
+ global scheduled_volume
+ scheduled_volume = {}
+ self._set_service_states(host_num=10,
+ drive_type_start_ix=0,
+ drive_type_num=5,
+ init_num_drives=10,
+ exclude_host_list=['host_0', 'host_1'])
+ prev = self._generate_default_service_states()
+
+ global global_volume
+ global_volume = {}
+
+ drive_ix = 2
+ name = 'name_' + str(drive_ix)
+ volume_types.create(self.context, name,
+ extra_specs={'type': 'vsa_drive',
+ 'drive_name': name,
+ 'drive_type': 'type_' + str(drive_ix),
+ 'drive_size': 1 + 100 * (drive_ix)})
+ self.created_types_lst.append(name)
+ volume_type = volume_types.get_volume_type_by_name(self.context, name)
+
+ global_volume['volume_type_id'] = volume_type['id']
+ global_volume['size'] = 0
+
+ host = self.sched.schedule_create_volume(self.context,
+ 123, availability_zone=None)
+
+ self.assertEqual(host, 'host_2')
+ self.assertEqual(scheduled_volume['id'], 123)
+ self.assertEqual(scheduled_volume['host'], 'host_2')
+
+
+class VsaSchedulerTestCaseMostAvail(VsaSchedulerTestCase):
+
+ def setUp(self):
+ super(VsaSchedulerTestCaseMostAvail, self).setUp(
+ FakeVsaMostAvailCapacityScheduler())
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ super(VsaSchedulerTestCaseMostAvail, self).tearDown()
+
+ def test_vsa_sched_create_single_volume(self):
+ global scheduled_volume
+ scheduled_volume = {}
+ self._set_service_states(host_num=10,
+ drive_type_start_ix=0,
+ drive_type_num=5,
+ init_num_drives=10,
+ exclude_host_list=['host_0', 'host_1'])
+ prev = self._generate_default_service_states()
+
+ global global_volume
+ global_volume = {}
+
+ drive_ix = 2
+ name = 'name_' + str(drive_ix)
+ volume_types.create(self.context, name,
+ extra_specs={'type': 'vsa_drive',
+ 'drive_name': name,
+ 'drive_type': 'type_' + str(drive_ix),
+ 'drive_size': 1 + 100 * (drive_ix)})
+ self.created_types_lst.append(name)
+ volume_type = volume_types.get_volume_type_by_name(self.context, name)
+
+ global_volume['volume_type_id'] = volume_type['id']
+ global_volume['size'] = 0
+
+ host = self.sched.schedule_create_volume(self.context,
+ 123, availability_zone=None)
+
+ self.assertEqual(host, 'host_9')
+ self.assertEqual(scheduled_volume['id'], 123)
+ self.assertEqual(scheduled_volume['host'], 'host_9')
+
+ def test_vsa_sched_create_volumes_simple(self):
+ global scheduled_volumes
+ scheduled_volumes = []
+ self._set_service_states(host_num=10,
+ drive_type_start_ix=0,
+ drive_type_num=5,
+ init_num_drives=10,
+ exclude_host_list=['host_1', 'host_3'])
+ prev = self._generate_default_service_states()
+ request_spec = self._get_vol_creation_request(num_vols=3, drive_ix=2)
+
+ self._print_service_states()
+
+ self.sched.schedule_create_volumes(self.context,
+ request_spec,
+ availability_zone=None)
+
+ self.assertEqual(len(scheduled_volumes), 3)
+ self.assertEqual(scheduled_volumes[0]['vol']['host'], 'host_9')
+ self.assertEqual(scheduled_volumes[1]['vol']['host'], 'host_8')
+ self.assertEqual(scheduled_volumes[2]['vol']['host'], 'host_7')
+
+ cur = self._get_service_states()
+ for host in ['host_9', 'host_8', 'host_7']:
+ cur_dtype = cur[host]['volume']['drive_qos_info']['name_2']
+ prev_dtype = prev[host]['volume']['drive_qos_info']['name_2']
+ self.assertEqual(cur_dtype['DriveType'], prev_dtype['DriveType'])
+ self.assertEqual(cur_dtype['FullDrive']['NumFreeDrives'],
+ prev_dtype['FullDrive']['NumFreeDrives'] - 1)
+ self.assertEqual(cur_dtype['FullDrive']['NumOccupiedDrives'],
+ prev_dtype['FullDrive']['NumOccupiedDrives'] + 1)
+
+ def test_vsa_sched_create_volumes_partition(self):
+ global scheduled_volumes
+ scheduled_volumes = []
+ self._set_service_states(host_num=5,
+ drive_type_start_ix=0,
+ drive_type_num=5,
+ init_num_drives=1,
+ exclude_host_list=['host_0', 'host_2'])
+ prev = self._generate_default_service_states()
+ request_spec = self._get_vol_creation_request(num_vols=3,
+ drive_ix=3,
+ size=50)
+ self.sched.schedule_create_volumes(self.context,
+ request_spec,
+ availability_zone=None)
+
+ self.assertEqual(len(scheduled_volumes), 3)
+ self.assertEqual(scheduled_volumes[0]['vol']['host'], 'host_4')
+ self.assertEqual(scheduled_volumes[1]['vol']['host'], 'host_3')
+ self.assertEqual(scheduled_volumes[2]['vol']['host'], 'host_1')
+
+ cur = self._get_service_states()
+ for host in ['host_1', 'host_3', 'host_4']:
+ cur_dtype = cur[host]['volume']['drive_qos_info']['name_3']
+ prev_dtype = prev[host]['volume']['drive_qos_info']['name_3']
+
+ self.assertEqual(cur_dtype['DriveType'], prev_dtype['DriveType'])
+ self.assertEqual(cur_dtype['FullDrive']['NumFreeDrives'],
+ prev_dtype['FullDrive']['NumFreeDrives'] - 1)
+ self.assertEqual(cur_dtype['FullDrive']['NumOccupiedDrives'],
+ prev_dtype['FullDrive']['NumOccupiedDrives'] + 1)
+
+ self.assertEqual(prev_dtype['PartitionDrive']
+ ['NumOccupiedPartitions'], 0)
+ self.assertEqual(cur_dtype['PartitionDrive']
+ ['NumOccupiedPartitions'], 1)
+ self.assertEqual(cur_dtype['PartitionDrive']
+ ['NumFreePartitions'], 5)
+ self.assertEqual(prev_dtype['PartitionDrive']
+ ['NumFreePartitions'], 0)
+ self.assertEqual(prev_dtype['PartitionDrive']
+ ['PartitionSize'], 0)
diff --git a/nova/tests/test_auth.py b/nova/tests/test_auth.py
index 4561eb7f2..1b3166af7 100644
--- a/nova/tests/test_auth.py
+++ b/nova/tests/test_auth.py
@@ -147,6 +147,7 @@ class _AuthManagerBaseTestCase(test.TestCase):
'/services/Cloud'))
def test_can_get_credentials(self):
+ self.flags(use_deprecated_auth=True)
st = {'access': 'access', 'secret': 'secret'}
with user_and_project_generator(self.manager, user_state=st) as (u, p):
credentials = self.manager.get_environment_rc(u, p)
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 993a87f23..6659b81eb 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -2,6 +2,7 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
+# Copyright 2011 Piston Cloud Computing, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -159,9 +160,24 @@ class ComputeTestCase(test.TestCase):
db.security_group_destroy(self.context, group['id'])
db.instance_destroy(self.context, ref[0]['id'])
+ def test_create_instance_associates_config_drive(self):
+ """Make sure create associates a config drive."""
+
+ instance_id = self._create_instance(params={'config_drive': True, })
+
+ try:
+ self.compute.run_instance(self.context, instance_id)
+ instances = db.instance_get_all(context.get_admin_context())
+ instance = instances[0]
+
+ self.assertTrue(instance.config_drive)
+ finally:
+ db.instance_destroy(self.context, instance_id)
+
def test_default_hostname_generator(self):
- cases = [(None, 'server_1'), ('Hello, Server!', 'hello_server'),
- ('<}\x1fh\x10e\x08l\x02l\x05o\x12!{>', 'hello')]
+ cases = [(None, 'server-1'), ('Hello, Server!', 'hello-server'),
+ ('<}\x1fh\x10e\x08l\x02l\x05o\x12!{>', 'hello'),
+ ('hello_server', 'hello-server')]
for display_name, hostname in cases:
ref = self.compute_api.create(self.context,
instance_types.get_default_instance_type(), None,
diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py
index ef271518c..09f532239 100644
--- a/nova/tests/test_instance_types.py
+++ b/nova/tests/test_instance_types.py
@@ -47,6 +47,29 @@ class InstanceTypeTestCase(test.TestCase):
self.id = max_id["id"] + 1
self.name = str(int(time.time()))
+ def _nonexistent_flavor_name(self):
+ """return an instance type name not in the DB"""
+ nonexistent_flavor = "sdfsfsdf"
+ flavors = instance_types.get_all_types()
+ while nonexistent_flavor in flavors:
+ nonexistent_flavor += "z"
+ else:
+ return nonexistent_flavor
+
+ def _nonexistent_flavor_id(self):
+ """return an instance type ID not in the DB"""
+ nonexistent_flavor = 2700
+ flavor_ids = [value["id"] for key, value in\
+ instance_types.get_all_types().iteritems()]
+ while nonexistent_flavor in flavor_ids:
+ nonexistent_flavor += 1
+ else:
+ return nonexistent_flavor
+
+ def _existing_flavor(self):
+ """return first instance type name"""
+ return instance_types.get_all_types().keys()[0]
+
def test_instance_type_create_then_delete(self):
"""Ensure instance types can be created"""
starting_inst_list = instance_types.get_all_types()
@@ -84,10 +107,11 @@ class InstanceTypeTestCase(test.TestCase):
exception.InvalidInput,
instance_types.create, self.name, 256, 1, "aa", self.flavorid)
- def test_non_existant_inst_type_shouldnt_delete(self):
+ def test_non_existent_inst_type_shouldnt_delete(self):
"""Ensures that instance type creation fails with invalid args"""
self.assertRaises(exception.ApiError,
- instance_types.destroy, "sfsfsdfdfs")
+ instance_types.destroy,
+ self._nonexistent_flavor_name())
def test_repeated_inst_types_should_raise_api_error(self):
"""Ensures that instance duplicates raises ApiError"""
@@ -97,3 +121,43 @@ class InstanceTypeTestCase(test.TestCase):
self.assertRaises(
exception.ApiError,
instance_types.create, new_name, 256, 1, 120, self.flavorid)
+
+ def test_will_not_destroy_with_no_name(self):
+ """Ensure destroy sad path of no name raises error"""
+ self.assertRaises(exception.ApiError,
+ instance_types.destroy,
+ self._nonexistent_flavor_name())
+
+ def test_will_not_purge_without_name(self):
+ """Ensure purge without a name raises error"""
+ self.assertRaises(exception.InvalidInstanceType,
+ instance_types.purge, None)
+
+ def test_will_not_purge_with_wrong_name(self):
+ """Ensure purge without correct name raises error"""
+ self.assertRaises(exception.ApiError,
+ instance_types.purge,
+ self._nonexistent_flavor_name())
+
+ def test_will_not_get_bad_default_instance_type(self):
+ """ensures error raised on bad default instance type"""
+ FLAGS.default_instance_type = self._nonexistent_flavor_name()
+ self.assertRaises(exception.InstanceTypeNotFoundByName,
+ instance_types.get_default_instance_type)
+
+ def test_will_not_get_instance_type_by_name_with_no_name(self):
+ """Ensure get by name returns default flavor with no name"""
+ self.assertEqual(instance_types.get_default_instance_type(),
+ instance_types.get_instance_type_by_name(None))
+
+ def test_will_not_get_instance_type_with_bad_name(self):
+ """Ensure get by name returns default flavor with bad name"""
+ self.assertRaises(exception.InstanceTypeNotFound,
+ instance_types.get_instance_type,
+ self._nonexistent_flavor_name())
+
+ def test_will_not_get_flavor_by_bad_flavor_id(self):
+ """Ensure get by flavor raises error with wrong flavorid"""
+ self.assertRaises(exception.InstanceTypeNotFound,
+ instance_types.get_instance_type_by_name,
+ self._nonexistent_flavor_id())
diff --git a/nova/tests/test_ipv6.py b/nova/tests/test_ipv6.py
index d123df6f1..e1ba4aafb 100644
--- a/nova/tests/test_ipv6.py
+++ b/nova/tests/test_ipv6.py
@@ -40,6 +40,25 @@ class IPv6RFC2462TestCase(test.TestCase):
mac = ipv6.to_mac('2001:db8::216:3eff:fe33:4455')
self.assertEquals(mac, '00:16:3e:33:44:55')
+ def test_to_global_with_bad_mac(self):
+ bad_mac = '02:16:3e:33:44:5Z'
+ self.assertRaises(TypeError, ipv6.to_global,
+ '2001:db8::', bad_mac, 'test')
+
+ def test_to_global_with_bad_prefix(self):
+ bad_prefix = '82'
+ self.assertRaises(TypeError, ipv6.to_global,
+ bad_prefix,
+ '2001:db8::216:3eff:fe33:4455',
+ 'test')
+
+ def test_to_global_with_bad_project(self):
+ bad_project = 'non-existent-project-name'
+ self.assertRaises(TypeError, ipv6.to_global,
+ '2001:db8::',
+ '2001:db8::a94a:8fe5:ff33:4455',
+ bad_project)
+
class IPv6AccountIdentiferTestCase(test.TestCase):
"""Unit tests for IPv6 account_identifier backend operations."""
@@ -55,3 +74,22 @@ class IPv6AccountIdentiferTestCase(test.TestCase):
def test_to_mac(self):
mac = ipv6.to_mac('2001:db8::a94a:8fe5:ff33:4455')
self.assertEquals(mac, '02:16:3e:33:44:55')
+
+ def test_to_global_with_bad_mac(self):
+ bad_mac = '02:16:3e:33:44:5X'
+ self.assertRaises(TypeError, ipv6.to_global,
+ '2001:db8::', bad_mac, 'test')
+
+ def test_to_global_with_bad_prefix(self):
+ bad_prefix = '78'
+ self.assertRaises(TypeError, ipv6.to_global,
+ bad_prefix,
+ '2001:db8::a94a:8fe5:ff33:4455',
+ 'test')
+
+ def test_to_global_with_bad_project(self):
+ bad_project = 'non-existent-project-name'
+ self.assertRaises(TypeError, ipv6.to_global,
+ '2001:db8::',
+ '2001:db8::a94a:8fe5:ff33:4455',
+ bad_project)
diff --git a/nova/tests/test_notifier.py b/nova/tests/test_notifier.py
index 64b799a2c..7de3a4a99 100644
--- a/nova/tests/test_notifier.py
+++ b/nova/tests/test_notifier.py
@@ -134,3 +134,24 @@ class NotifierTestCase(test.TestCase):
self.assertEqual(msg['event_type'], 'error_notification')
self.assertEqual(msg['priority'], 'ERROR')
self.assertEqual(msg['payload']['error'], 'foo')
+
+ def test_send_notification_by_decorator(self):
+ self.notify_called = False
+
+ def example_api(arg1, arg2):
+ return arg1 + arg2
+
+ example_api = nova.notifier.api.notify_decorator(
+ 'example_api',
+ example_api)
+
+ def mock_notify(cls, *args):
+ self.notify_called = True
+
+ self.stubs.Set(nova.notifier.no_op_notifier, 'notify',
+ mock_notify)
+
+ class Mock(object):
+ pass
+ self.assertEqual(3, example_api(1, 2))
+ self.assertEqual(self.notify_called, True)
diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py
index f5ea68a03..520bfbea1 100644
--- a/nova/tests/test_nova_manage.py
+++ b/nova/tests/test_nova_manage.py
@@ -31,6 +31,7 @@ sys.dont_write_bytecode = False
import mox
import stubout
+import StringIO
from nova import context
from nova import db
from nova import exception
@@ -70,3 +71,156 @@ class FixedIpCommandsTestCase(test.TestCase):
self.assertRaises(SystemExit,
self.commands.unreserve,
'55.55.55.55')
+
+
+class NetworkCommandsTestCase(test.TestCase):
+ def setUp(self):
+ super(NetworkCommandsTestCase, self).setUp()
+ self.stubs = stubout.StubOutForTesting()
+ self.commands = nova_manage.NetworkCommands()
+ self.context = context.get_admin_context()
+ self.net = {'id': 0,
+ 'label': 'fake',
+ 'injected': False,
+ 'cidr': '192.168.0.0/24',
+ 'cidr_v6': 'dead:beef::/64',
+ 'multi_host': False,
+ 'gateway_v6': 'dead:beef::1',
+ 'netmask_v6': '64',
+ 'netmask': '255.255.255.0',
+ 'bridge': 'fa0',
+ 'bridge_interface': 'fake_fa0',
+ 'gateway': '192.168.0.1',
+ 'broadcast': '192.168.0.255',
+ 'dns1': '8.8.8.8',
+ 'dns2': '8.8.4.4',
+ 'vlan': 200,
+ 'vpn_public_address': '10.0.0.2',
+ 'vpn_public_port': '2222',
+ 'vpn_private_address': '192.168.0.2',
+ 'dhcp_start': '192.168.0.3',
+ 'project_id': 'fake_project',
+ 'host': 'fake_host',
+ 'uuid': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'}
+
+ def fake_network_get_by_cidr(context, cidr):
+ self.assertTrue(context.to_dict()['is_admin'])
+ self.assertEqual(cidr, self.fake_net['cidr'])
+ return db_fakes.FakeModel(self.fake_net)
+
+ def fake_network_update(context, network_id, values):
+ self.assertTrue(context.to_dict()['is_admin'])
+ self.assertEqual(network_id, self.fake_net['id'])
+ self.assertEqual(values, self.fake_update_value)
+ self.fake_network_get_by_cidr = fake_network_get_by_cidr
+ self.fake_network_update = fake_network_update
+
+ def tearDown(self):
+ super(NetworkCommandsTestCase, self).tearDown()
+ self.stubs.UnsetAll()
+
+ def test_create(self):
+
+ def fake_create_networks(obj, context, **kwargs):
+ self.assertTrue(context.to_dict()['is_admin'])
+ self.assertEqual(kwargs['label'], 'Test')
+ self.assertEqual(kwargs['cidr'], '10.2.0.0/24')
+ self.assertEqual(kwargs['multi_host'], False)
+ self.assertEqual(kwargs['num_networks'], 1)
+ self.assertEqual(kwargs['network_size'], 256)
+ self.assertEqual(kwargs['vlan_start'], 200)
+ self.assertEqual(kwargs['vpn_start'], 2000)
+ self.assertEqual(kwargs['cidr_v6'], 'fd00:2::/120')
+ self.assertEqual(kwargs['gateway_v6'], 'fd00:2::22')
+ self.assertEqual(kwargs['bridge'], 'br200')
+ self.assertEqual(kwargs['bridge_interface'], 'eth0')
+ self.assertEqual(kwargs['dns1'], '8.8.8.8')
+ self.assertEqual(kwargs['dns2'], '8.8.4.4')
+ self.flags(network_manager='nova.network.manager.VlanManager')
+ from nova.network import manager as net_manager
+ self.stubs.Set(net_manager.VlanManager, 'create_networks',
+ fake_create_networks)
+ self.commands.create(
+ label='Test',
+ fixed_range_v4='10.2.0.0/24',
+ num_networks=1,
+ network_size=256,
+ multi_host='F',
+ vlan_start=200,
+ vpn_start=2000,
+ fixed_range_v6='fd00:2::/120',
+ gateway_v6='fd00:2::22',
+ bridge='br200',
+ bridge_interface='eth0',
+ dns1='8.8.8.8',
+ dns2='8.8.4.4')
+
+ def test_list(self):
+
+ def fake_network_get_all(context):
+ return [db_fakes.FakeModel(self.net)]
+ self.stubs.Set(db, 'network_get_all', fake_network_get_all)
+ output = StringIO.StringIO()
+ sys.stdout = output
+ self.commands.list()
+ sys.stdout = sys.__stdout__
+ result = output.getvalue()
+ _fmt = "%(id)-5s\t%(cidr)-18s\t%(cidr_v6)-15s\t%(dhcp_start)-15s\t" +\
+ "%(dns1)-15s\t%(dns2)-15s\t%(vlan)-15s\t%(project_id)-15s\t" +\
+ "%(uuid)-15s"
+ head = _fmt % {'id': _('id'),
+ 'cidr': _('IPv4'),
+ 'cidr_v6': _('IPv6'),
+ 'dhcp_start': _('start address'),
+ 'dns1': _('DNS1'),
+ 'dns2': _('DNS2'),
+ 'vlan': _('VlanID'),
+ 'project_id': _('project'),
+ 'uuid': _("uuid")}
+ body = _fmt % {'id': self.net['id'],
+ 'cidr': self.net['cidr'],
+ 'cidr_v6': self.net['cidr_v6'],
+ 'dhcp_start': self.net['dhcp_start'],
+ 'dns1': self.net['dns1'],
+ 'dns2': self.net['dns2'],
+ 'vlan': self.net['vlan'],
+ 'project_id': self.net['project_id'],
+ 'uuid': self.net['uuid']}
+ answer = '%s\n%s\n' % (head, body)
+ self.assertEqual(result, answer)
+
+ def test_delete(self):
+ self.fake_net = self.net
+ self.fake_net['project_id'] = None
+ self.fake_net['host'] = None
+ self.stubs.Set(db, 'network_get_by_cidr',
+ self.fake_network_get_by_cidr)
+
+ def fake_network_delete_safe(context, network_id):
+ self.assertTrue(context.to_dict()['is_admin'])
+ self.assertEqual(network_id, self.fake_net['id'])
+ self.stubs.Set(db, 'network_delete_safe', fake_network_delete_safe)
+ self.commands.delete(fixed_range=self.fake_net['cidr'])
+
+ def _test_modify_base(self, update_value, project, host, dis_project=None,
+ dis_host=None):
+ self.fake_net = self.net
+ self.fake_update_value = update_value
+ self.stubs.Set(db, 'network_get_by_cidr',
+ self.fake_network_get_by_cidr)
+ self.stubs.Set(db, 'network_update', self.fake_network_update)
+ self.commands.modify(self.fake_net['cidr'], project=project, host=host,
+ dis_project=dis_project, dis_host=dis_host)
+
+ def test_modify_associate(self):
+ self._test_modify_base(update_value={'project_id': 'test_project',
+ 'host': 'test_host'},
+ project='test_project', host='test_host')
+
+ def test_modify_unchanged(self):
+ self._test_modify_base(update_value={}, project=None, host=None)
+
+ def test_modify_disassociate(self):
+ self._test_modify_base(update_value={'project_id': None, 'host': None},
+ project=None, host=None, dis_project=True,
+ dis_host=True)
diff --git a/nova/tests/test_test_utils.py b/nova/tests/test_test_utils.py
new file mode 100644
index 000000000..237339758
--- /dev/null
+++ b/nova/tests/test_test_utils.py
@@ -0,0 +1,41 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2010 OpenStack 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 db
+from nova import test
+from nova.tests import utils as test_utils
+
+
+class TestUtilsTestCase(test.TestCase):
+ def test_get_test_admin_context(self):
+ """get_test_admin_context's return value behaves like admin context"""
+ ctxt = test_utils.get_test_admin_context()
+
+ # TODO(soren): This should verify the full interface context
+ # objects expose.
+ self.assertTrue(ctxt.is_admin)
+
+ def test_get_test_instance(self):
+ """get_test_instance's return value looks like an instance_ref"""
+ instance_ref = test_utils.get_test_instance()
+ ctxt = test_utils.get_test_admin_context()
+ db.instance_get(ctxt, instance_ref['id'])
+
+ def _test_get_test_network_info(self):
+ """Does the return value match a real network_info structure"""
+ # The challenge here is to define what exactly such a structure
+ # must look like.
+ pass
diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py
index 28e366a8e..1ba794a1a 100644
--- a/nova/tests/test_utils.py
+++ b/nova/tests/test_utils.py
@@ -18,6 +18,7 @@ import datetime
import os
import tempfile
+import nova
from nova import exception
from nova import test
from nova import utils
@@ -394,3 +395,47 @@ class ToPrimitiveTestCase(test.TestCase):
self.assertTrue(ret[0].startswith(u"<module 'datetime' from "))
self.assertTrue(ret[1].startswith(u'<function foo at 0x'))
self.assertEquals(ret[2], u'<built-in function dir>')
+
+
+class MonkeyPatchTestCase(test.TestCase):
+ """Unit test for utils.monkey_patch()."""
+ def setUp(self):
+ super(MonkeyPatchTestCase, self).setUp()
+ self.example_package = 'nova.tests.monkey_patch_example.'
+ self.flags(
+ monkey_patch=True,
+ monkey_patch_modules=[self.example_package + 'example_a' + ':'
+ + self.example_package + 'example_decorator'])
+
+ def test_monkey_patch(self):
+ utils.monkey_patch()
+ nova.tests.monkey_patch_example.CALLED_FUNCTION = []
+ from nova.tests.monkey_patch_example import example_a, example_b
+
+ self.assertEqual('Example function', example_a.example_function_a())
+ exampleA = example_a.ExampleClassA()
+ exampleA.example_method()
+ ret_a = exampleA.example_method_add(3, 5)
+ self.assertEqual(ret_a, 8)
+
+ self.assertEqual('Example function', example_b.example_function_b())
+ exampleB = example_b.ExampleClassB()
+ exampleB.example_method()
+ ret_b = exampleB.example_method_add(3, 5)
+
+ self.assertEqual(ret_b, 8)
+ package_a = self.example_package + 'example_a.'
+ self.assertTrue(package_a + 'example_function_a'
+ in nova.tests.monkey_patch_example.CALLED_FUNCTION)
+
+ self.assertTrue(package_a + 'ExampleClassA.example_method'
+ in nova.tests.monkey_patch_example.CALLED_FUNCTION)
+ self.assertTrue(package_a + 'ExampleClassA.example_method_add'
+ in nova.tests.monkey_patch_example.CALLED_FUNCTION)
+ package_b = self.example_package + 'example_b.'
+ self.assertFalse(package_b + 'example_function_b'
+ in nova.tests.monkey_patch_example.CALLED_FUNCTION)
+ self.assertFalse(package_b + 'ExampleClassB.example_method'
+ in nova.tests.monkey_patch_example.CALLED_FUNCTION)
+ self.assertFalse(package_b + 'ExampleClassB.example_method_add'
+ in nova.tests.monkey_patch_example.CALLED_FUNCTION)
diff --git a/nova/tests/test_versions.py b/nova/tests/test_versions.py
new file mode 100644
index 000000000..4621b042b
--- /dev/null
+++ b/nova/tests/test_versions.py
@@ -0,0 +1,61 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Ken Pepple
+#
+# 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 exception
+from nova import test
+from nova import utils
+from nova import version
+
+
+class VersionTestCase(test.TestCase):
+ """Test cases for Versions code"""
+ def setUp(self):
+ """setup test with unchanging values"""
+ super(VersionTestCase, self).setUp()
+ self.version = version
+ self.version.FINAL = False
+ self.version.NOVA_VERSION = ['2012', '10']
+ self.version.YEAR, self.version.COUNT = self.version.NOVA_VERSION
+ self.version.version_info = {'branch_nick': u'LOCALBRANCH',
+ 'revision_id': 'LOCALREVISION',
+ 'revno': 0}
+
+ def test_version_string_is_good(self):
+ """Ensure version string works"""
+ self.assertEqual("2012.10-dev", self.version.version_string())
+
+ def test_canonical_version_string_is_good(self):
+ """Ensure canonical version works"""
+ self.assertEqual("2012.10", self.version.canonical_version_string())
+
+ def test_final_version_strings_are_identical(self):
+ """Ensure final version strings match only at release"""
+ self.assertNotEqual(self.version.canonical_version_string(),
+ self.version.version_string())
+ self.version.FINAL = True
+ self.assertEqual(self.version.canonical_version_string(),
+ self.version.version_string())
+
+ def test_vcs_version_string_is_good(self):
+ """Ensure uninstalled code generates local """
+ self.assertEqual("LOCALBRANCH:LOCALREVISION",
+ self.version.vcs_version_string())
+
+ def test_version_string_with_vcs_is_good(self):
+ """Ensure uninstalled code get version string"""
+ self.assertEqual("2012.10-LOCALBRANCH:LOCALREVISION",
+ self.version.version_string_with_vcs())
diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py
new file mode 100644
index 000000000..480247c91
--- /dev/null
+++ b/nova/tests/test_virt_drivers.py
@@ -0,0 +1,489 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2010 OpenStack 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 base64
+import netaddr
+import sys
+import traceback
+
+from nova import exception
+from nova import flags
+from nova import image
+from nova import log as logging
+from nova import test
+from nova.tests import utils as test_utils
+
+libvirt = None
+FLAGS = flags.FLAGS
+
+LOG = logging.getLogger('nova.tests.test_virt_drivers')
+
+
+def catch_notimplementederror(f):
+ """Decorator to simplify catching drivers raising NotImplementedError
+
+ If a particular call makes a driver raise NotImplementedError, we
+ log it so that we can extract this information afterwards to
+ automatically generate a hypervisor/feature support matrix."""
+ def wrapped_func(self, *args, **kwargs):
+ try:
+ return f(self, *args, **kwargs)
+ except NotImplementedError:
+ frame = traceback.extract_tb(sys.exc_info()[2])[-1]
+ LOG.error('%(driver)s does not implement %(method)s' % {
+ 'driver': type(self.connection),
+ 'method': frame[2]})
+
+ wrapped_func.__name__ = f.__name__
+ wrapped_func.__doc__ = f.__doc__
+ return wrapped_func
+
+
+class _VirtDriverTestCase(test.TestCase):
+ def setUp(self):
+ super(_VirtDriverTestCase, self).setUp()
+ self.connection = self.driver_module.get_connection('')
+ self.ctxt = test_utils.get_test_admin_context()
+ self.image_service = image.get_default_image_service()
+
+ @catch_notimplementederror
+ def test_init_host(self):
+ self.connection.init_host('myhostname')
+
+ @catch_notimplementederror
+ def test_list_instances(self):
+ self.connection.list_instances()
+
+ @catch_notimplementederror
+ def test_list_instances_detail(self):
+ self.connection.list_instances_detail()
+
+ @catch_notimplementederror
+ def test_spawn(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+
+ domains = self.connection.list_instances()
+ self.assertIn(instance_ref['name'], domains)
+
+ domains_details = self.connection.list_instances_detail()
+ self.assertIn(instance_ref['name'], [i.name for i in domains_details])
+
+ @catch_notimplementederror
+ def test_snapshot_not_running(self):
+ instance_ref = test_utils.get_test_instance()
+ img_ref = self.image_service.create(self.ctxt, {'name': 'snap-1'})
+ self.assertRaises(exception.InstanceNotRunning,
+ self.connection.snapshot,
+ self.ctxt, instance_ref, img_ref['id'])
+
+ @catch_notimplementederror
+ def test_snapshot_running(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ img_ref = self.image_service.create(self.ctxt, {'name': 'snap-1'})
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.snapshot(self.ctxt, instance_ref, img_ref['id'])
+
+ @catch_notimplementederror
+ def test_reboot(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.reboot(instance_ref, network_info)
+
+ @catch_notimplementederror
+ def test_get_host_ip_addr(self):
+ host_ip = self.connection.get_host_ip_addr()
+
+ # Will raise an exception if it's not a valid IP at all
+ ip = netaddr.IPAddress(host_ip)
+
+ # For now, assume IPv4.
+ self.assertEquals(ip.version, 4)
+
+ @catch_notimplementederror
+ def test_resize_running(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.resize(instance_ref, 7)
+
+ @catch_notimplementederror
+ def test_set_admin_password(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.set_admin_password(instance_ref, 'p4ssw0rd')
+
+ @catch_notimplementederror
+ def test_inject_file(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.inject_file(instance_ref,
+ base64.b64encode('/testfile'),
+ base64.b64encode('testcontents'))
+
+ @catch_notimplementederror
+ def test_agent_update(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.agent_update(instance_ref, 'http://www.openstack.org/',
+ 'd41d8cd98f00b204e9800998ecf8427e')
+
+ @catch_notimplementederror
+ def test_rescue(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.rescue(self.ctxt, instance_ref,
+ lambda x: None, network_info)
+
+ @catch_notimplementederror
+ def test_unrescue_unrescued_instance(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.unrescue(instance_ref, lambda x: None, network_info)
+
+ @catch_notimplementederror
+ def test_unrescue_rescued_instance(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.rescue(self.ctxt, instance_ref,
+ lambda x: None, network_info)
+ self.connection.unrescue(instance_ref, lambda x: None, network_info)
+
+ @catch_notimplementederror
+ def test_poll_rescued_instances(self):
+ self.connection.poll_rescued_instances(10)
+
+ @catch_notimplementederror
+ def test_migrate_disk_and_power_off(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.migrate_disk_and_power_off(instance_ref, 'dest_host')
+
+ @catch_notimplementederror
+ def test_pause(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.pause(instance_ref, None)
+
+ @catch_notimplementederror
+ def test_unpause_unpaused_instance(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.unpause(instance_ref, None)
+
+ @catch_notimplementederror
+ def test_unpause_paused_instance(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.pause(instance_ref, None)
+ self.connection.unpause(instance_ref, None)
+
+ @catch_notimplementederror
+ def test_suspend(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.suspend(instance_ref, None)
+
+ @catch_notimplementederror
+ def test_resume_unsuspended_instance(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.resume(instance_ref, None)
+
+ @catch_notimplementederror
+ def test_resume_suspended_instance(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.suspend(instance_ref, None)
+ self.connection.resume(instance_ref, None)
+
+ @catch_notimplementederror
+ def test_destroy_instance_nonexistant(self):
+ fake_instance = {'id': 42, 'name': 'I just made this up!'}
+ network_info = test_utils.get_test_network_info()
+ self.connection.destroy(fake_instance, network_info)
+
+ @catch_notimplementederror
+ def test_destroy_instance(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.assertIn(instance_ref['name'],
+ self.connection.list_instances())
+ self.connection.destroy(instance_ref, network_info)
+ self.assertNotIn(instance_ref['name'],
+ self.connection.list_instances())
+
+ @catch_notimplementederror
+ def test_attach_detach_volume(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.attach_volume(instance_ref['name'],
+ '/dev/null', '/mnt/nova/something')
+ self.connection.detach_volume(instance_ref['name'],
+ '/mnt/nova/something')
+
+ @catch_notimplementederror
+ def test_get_info(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ info = self.connection.get_info(instance_ref['name'])
+ self.assertIn('state', info)
+ self.assertIn('max_mem', info)
+ self.assertIn('mem', info)
+ self.assertIn('num_cpu', info)
+ self.assertIn('cpu_time', info)
+
+ @catch_notimplementederror
+ def test_get_info_for_unknown_instance(self):
+ self.assertRaises(exception.NotFound,
+ self.connection.get_info, 'I just made this name up')
+
+ @catch_notimplementederror
+ def test_get_diagnostics(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.get_diagnostics(instance_ref['name'])
+
+ @catch_notimplementederror
+ def test_list_disks(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.list_disks(instance_ref['name'])
+
+ @catch_notimplementederror
+ def test_list_interfaces(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.list_interfaces(instance_ref['name'])
+
+ @catch_notimplementederror
+ def test_block_stats(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ stats = self.connection.block_stats(instance_ref['name'], 'someid')
+ self.assertEquals(len(stats), 5)
+
+ @catch_notimplementederror
+ def test_interface_stats(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ stats = self.connection.interface_stats(instance_ref['name'], 'someid')
+ self.assertEquals(len(stats), 8)
+
+ @catch_notimplementederror
+ def test_get_console_output(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ console_output = self.connection.get_console_output(instance_ref)
+ self.assertTrue(isinstance(console_output, basestring))
+
+ @catch_notimplementederror
+ def test_get_ajax_console(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ ajax_console = self.connection.get_ajax_console(instance_ref)
+ self.assertIn('token', ajax_console)
+ self.assertIn('host', ajax_console)
+ self.assertIn('port', ajax_console)
+
+ @catch_notimplementederror
+ def test_get_vnc_console(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ vnc_console = self.connection.get_vnc_console(instance_ref)
+ self.assertIn('token', vnc_console)
+ self.assertIn('host', vnc_console)
+ self.assertIn('port', vnc_console)
+
+ @catch_notimplementederror
+ def test_get_console_pool_info(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ console_pool = self.connection.get_console_pool_info(instance_ref)
+ self.assertIn('address', console_pool)
+ self.assertIn('username', console_pool)
+ self.assertIn('password', console_pool)
+
+ @catch_notimplementederror
+ def test_refresh_security_group_rules(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ # FIXME: Create security group and add the instance to it
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.refresh_security_group_rules(1)
+
+ @catch_notimplementederror
+ def test_refresh_security_group_members(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ # FIXME: Create security group and add the instance to it
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.refresh_security_group_members(1)
+
+ @catch_notimplementederror
+ def test_refresh_provider_fw_rules(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.refresh_provider_fw_rules()
+
+ @catch_notimplementederror
+ def test_update_available_resource(self):
+ self.compute = self.start_service('compute', host='dummy')
+ self.connection.update_available_resource(self.ctxt, 'dummy')
+
+ @catch_notimplementederror
+ def test_compare_cpu(self):
+ cpu_info = '''{ "topology": {
+ "sockets": 1,
+ "cores": 2,
+ "threads": 1 },
+ "features": [
+ "xtpr",
+ "tm2",
+ "est",
+ "vmx",
+ "ds_cpl",
+ "monitor",
+ "pbe",
+ "tm",
+ "ht",
+ "ss",
+ "acpi",
+ "ds",
+ "vme"],
+ "arch": "x86_64",
+ "model": "Penryn",
+ "vendor": "Intel" }'''
+
+ self.connection.compare_cpu(cpu_info)
+
+ @catch_notimplementederror
+ def test_ensure_filtering_for_instance(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.ensure_filtering_rules_for_instance(instance_ref,
+ network_info)
+
+ @catch_notimplementederror
+ def test_unfilter_instance(self):
+ instance_ref = test_utils.get_test_instance()
+ network_info = test_utils.get_test_network_info()
+ self.connection.unfilter_instance(instance_ref, network_info)
+
+ @catch_notimplementederror
+ def test_live_migration(self):
+ network_info = test_utils.get_test_network_info()
+ instance_ref = test_utils.get_test_instance()
+ self.connection.spawn(self.ctxt, instance_ref, network_info)
+ self.connection.live_migration(self.ctxt, instance_ref, 'otherhost',
+ None, None)
+
+ @catch_notimplementederror
+ def _check_host_status_fields(self, host_status):
+ self.assertIn('host_name-description', host_status)
+ self.assertIn('host_hostname', host_status)
+ self.assertIn('host_memory_total', host_status)
+ self.assertIn('host_memory_overhead', host_status)
+ self.assertIn('host_memory_free', host_status)
+ self.assertIn('host_memory_free_computed', host_status)
+ self.assertIn('host_other_config', host_status)
+ self.assertIn('host_ip_address', host_status)
+ self.assertIn('host_cpu_info', host_status)
+ self.assertIn('disk_available', host_status)
+ self.assertIn('disk_total', host_status)
+ self.assertIn('disk_used', host_status)
+ self.assertIn('host_uuid', host_status)
+ self.assertIn('host_name_label', host_status)
+
+ @catch_notimplementederror
+ def test_update_host_status(self):
+ host_status = self.connection.update_host_status()
+ self._check_host_status_fields(host_status)
+
+ @catch_notimplementederror
+ def test_get_host_stats(self):
+ host_status = self.connection.get_host_stats()
+ self._check_host_status_fields(host_status)
+
+ @catch_notimplementederror
+ def test_set_host_enabled(self):
+ self.connection.set_host_enabled('a useless argument?', True)
+
+ @catch_notimplementederror
+ def test_host_power_action_reboot(self):
+ self.connection.host_power_action('a useless argument?', 'reboot')
+
+ @catch_notimplementederror
+ def test_host_power_action_shutdown(self):
+ self.connection.host_power_action('a useless argument?', 'shutdown')
+
+ @catch_notimplementederror
+ def test_host_power_action_startup(self):
+ self.connection.host_power_action('a useless argument?', 'startup')
+
+
+class AbstractDriverTestCase(_VirtDriverTestCase):
+ def setUp(self):
+ import nova.virt.driver
+
+ self.driver_module = nova.virt.driver
+
+ def get_driver_connection(_):
+ return nova.virt.driver.ComputeDriver()
+
+ self.driver_module.get_connection = get_driver_connection
+ super(AbstractDriverTestCase, self).setUp()
+
+
+class FakeConnectionTestCase(_VirtDriverTestCase):
+ def setUp(self):
+ import nova.virt.fake
+ self.driver_module = nova.virt.fake
+ super(FakeConnectionTestCase, self).setUp()
+
+# Before long, we'll add the real hypervisor drivers here as well
+# with whatever instrumentation they need to work independently of
+# their hypervisor. This way, we can verify that they all act the
+# same.
diff --git a/nova/tests/test_volume_types.py b/nova/tests/test_volume_types.py
new file mode 100644
index 000000000..1e190805c
--- /dev/null
+++ b/nova/tests/test_volume_types.py
@@ -0,0 +1,207 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 Zadara Storage Inc.
+# Copyright (c) 2011 OpenStack 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.
+"""
+Unit Tests for volume types code
+"""
+import time
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import test
+from nova import utils
+from nova.volume import volume_types
+from nova.db.sqlalchemy.session import get_session
+from nova.db.sqlalchemy import models
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.test_volume_types')
+
+
+class VolumeTypeTestCase(test.TestCase):
+ """Test cases for volume type code"""
+ def setUp(self):
+ super(VolumeTypeTestCase, self).setUp()
+
+ self.ctxt = context.get_admin_context()
+ self.vol_type1_name = str(int(time.time()))
+ self.vol_type1_specs = dict(
+ type="physical drive",
+ drive_type="SAS",
+ size="300",
+ rpm="7200",
+ visible="True")
+ self.vol_type1 = dict(name=self.vol_type1_name,
+ extra_specs=self.vol_type1_specs)
+
+ def test_volume_type_create_then_destroy(self):
+ """Ensure volume types can be created and deleted"""
+ prev_all_vtypes = volume_types.get_all_types(self.ctxt)
+
+ volume_types.create(self.ctxt,
+ self.vol_type1_name,
+ self.vol_type1_specs)
+ new = volume_types.get_volume_type_by_name(self.ctxt,
+ self.vol_type1_name)
+
+ LOG.info(_("Given data: %s"), self.vol_type1_specs)
+ LOG.info(_("Result data: %s"), new)
+
+ for k, v in self.vol_type1_specs.iteritems():
+ self.assertEqual(v, new['extra_specs'][k],
+ 'one of fields doesnt match')
+
+ new_all_vtypes = volume_types.get_all_types(self.ctxt)
+ self.assertEqual(len(prev_all_vtypes) + 1,
+ len(new_all_vtypes),
+ 'drive type was not created')
+
+ volume_types.destroy(self.ctxt, self.vol_type1_name)
+ new_all_vtypes = volume_types.get_all_types(self.ctxt)
+ self.assertEqual(prev_all_vtypes,
+ new_all_vtypes,
+ 'drive type was not deleted')
+
+ def test_volume_type_create_then_purge(self):
+ """Ensure volume types can be created and deleted"""
+ prev_all_vtypes = volume_types.get_all_types(self.ctxt, inactive=1)
+
+ volume_types.create(self.ctxt,
+ self.vol_type1_name,
+ self.vol_type1_specs)
+ new = volume_types.get_volume_type_by_name(self.ctxt,
+ self.vol_type1_name)
+
+ for k, v in self.vol_type1_specs.iteritems():
+ self.assertEqual(v, new['extra_specs'][k],
+ 'one of fields doesnt match')
+
+ new_all_vtypes = volume_types.get_all_types(self.ctxt, inactive=1)
+ self.assertEqual(len(prev_all_vtypes) + 1,
+ len(new_all_vtypes),
+ 'drive type was not created')
+
+ volume_types.destroy(self.ctxt, self.vol_type1_name)
+ new_all_vtypes2 = volume_types.get_all_types(self.ctxt, inactive=1)
+ self.assertEqual(len(new_all_vtypes),
+ len(new_all_vtypes2),
+ 'drive type was incorrectly deleted')
+
+ volume_types.purge(self.ctxt, self.vol_type1_name)
+ new_all_vtypes2 = volume_types.get_all_types(self.ctxt, inactive=1)
+ self.assertEqual(len(new_all_vtypes) - 1,
+ len(new_all_vtypes2),
+ 'drive type was not purged')
+
+ def test_get_all_volume_types(self):
+ """Ensures that all volume types can be retrieved"""
+ session = get_session()
+ total_volume_types = session.query(models.VolumeTypes).\
+ count()
+ vol_types = volume_types.get_all_types(self.ctxt)
+ self.assertEqual(total_volume_types, len(vol_types))
+
+ def test_non_existant_inst_type_shouldnt_delete(self):
+ """Ensures that volume type creation fails with invalid args"""
+ self.assertRaises(exception.ApiError,
+ volume_types.destroy, self.ctxt, "sfsfsdfdfs")
+
+ def test_repeated_vol_types_should_raise_api_error(self):
+ """Ensures that volume duplicates raises ApiError"""
+ new_name = self.vol_type1_name + "dup"
+ volume_types.create(self.ctxt, new_name)
+ volume_types.destroy(self.ctxt, new_name)
+ self.assertRaises(
+ exception.ApiError,
+ volume_types.create, self.ctxt, new_name)
+
+ def test_invalid_volume_types_params(self):
+ """Ensures that volume type creation fails with invalid args"""
+ self.assertRaises(exception.InvalidVolumeType,
+ volume_types.destroy, self.ctxt, None)
+ self.assertRaises(exception.InvalidVolumeType,
+ volume_types.purge, self.ctxt, None)
+ self.assertRaises(exception.InvalidVolumeType,
+ volume_types.get_volume_type, self.ctxt, None)
+ self.assertRaises(exception.InvalidVolumeType,
+ volume_types.get_volume_type_by_name,
+ self.ctxt, None)
+
+ def test_volume_type_get_by_id_and_name(self):
+ """Ensure volume types get returns same entry"""
+ volume_types.create(self.ctxt,
+ self.vol_type1_name,
+ self.vol_type1_specs)
+ new = volume_types.get_volume_type_by_name(self.ctxt,
+ self.vol_type1_name)
+
+ new2 = volume_types.get_volume_type(self.ctxt, new['id'])
+ self.assertEqual(new, new2)
+
+ def test_volume_type_search_by_extra_spec(self):
+ """Ensure volume types get by extra spec returns correct type"""
+ volume_types.create(self.ctxt, "type1", {"key1": "val1",
+ "key2": "val2"})
+ volume_types.create(self.ctxt, "type2", {"key2": "val2",
+ "key3": "val3"})
+ volume_types.create(self.ctxt, "type3", {"key3": "another_value",
+ "key4": "val4"})
+
+ vol_types = volume_types.get_all_types(self.ctxt,
+ search_opts={'extra_specs': {"key1": "val1"}})
+ LOG.info("vol_types: %s" % vol_types)
+ self.assertEqual(len(vol_types), 1)
+ self.assertTrue("type1" in vol_types.keys())
+ self.assertEqual(vol_types['type1']['extra_specs'],
+ {"key1": "val1", "key2": "val2"})
+
+ vol_types = volume_types.get_all_types(self.ctxt,
+ search_opts={'extra_specs': {"key2": "val2"}})
+ LOG.info("vol_types: %s" % vol_types)
+ self.assertEqual(len(vol_types), 2)
+ self.assertTrue("type1" in vol_types.keys())
+ self.assertTrue("type2" in vol_types.keys())
+
+ vol_types = volume_types.get_all_types(self.ctxt,
+ search_opts={'extra_specs': {"key3": "val3"}})
+ LOG.info("vol_types: %s" % vol_types)
+ self.assertEqual(len(vol_types), 1)
+ self.assertTrue("type2" in vol_types.keys())
+
+ def test_volume_type_search_by_extra_spec_multiple(self):
+ """Ensure volume types get by extra spec returns correct type"""
+ volume_types.create(self.ctxt, "type1", {"key1": "val1",
+ "key2": "val2",
+ "key3": "val3"})
+ volume_types.create(self.ctxt, "type2", {"key2": "val2",
+ "key3": "val3"})
+ volume_types.create(self.ctxt, "type3", {"key1": "val1",
+ "key3": "val3",
+ "key4": "val4"})
+
+ vol_types = volume_types.get_all_types(self.ctxt,
+ search_opts={'extra_specs': {"key1": "val1",
+ "key3": "val3"}})
+ LOG.info("vol_types: %s" % vol_types)
+ self.assertEqual(len(vol_types), 2)
+ self.assertTrue("type1" in vol_types.keys())
+ self.assertTrue("type3" in vol_types.keys())
+ self.assertEqual(vol_types['type1']['extra_specs'],
+ {"key1": "val1", "key2": "val2", "key3": "val3"})
+ self.assertEqual(vol_types['type3']['extra_specs'],
+ {"key1": "val1", "key3": "val3", "key4": "val4"})
diff --git a/nova/tests/test_volume_types_extra_specs.py b/nova/tests/test_volume_types_extra_specs.py
new file mode 100644
index 000000000..017b187a1
--- /dev/null
+++ b/nova/tests/test_volume_types_extra_specs.py
@@ -0,0 +1,132 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 Zadara Storage Inc.
+# Copyright (c) 2011 OpenStack LLC.
+# Copyright 2011 University of Southern California
+# 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.
+"""
+Unit Tests for volume types extra specs code
+"""
+
+from nova import context
+from nova import db
+from nova import test
+from nova.db.sqlalchemy.session import get_session
+from nova.db.sqlalchemy import models
+
+
+class VolumeTypeExtraSpecsTestCase(test.TestCase):
+
+ def setUp(self):
+ super(VolumeTypeExtraSpecsTestCase, self).setUp()
+ self.context = context.get_admin_context()
+ self.vol_type1 = dict(name="TEST: Regular volume test")
+ self.vol_type1_specs = dict(vol_extra1="value1",
+ vol_extra2="value2",
+ vol_extra3=3)
+ self.vol_type1['extra_specs'] = self.vol_type1_specs
+ ref = db.api.volume_type_create(self.context, self.vol_type1)
+ self.volume_type1_id = ref.id
+ for k, v in self.vol_type1_specs.iteritems():
+ self.vol_type1_specs[k] = str(v)
+
+ self.vol_type2_noextra = dict(name="TEST: Volume type without extra")
+ ref = db.api.volume_type_create(self.context, self.vol_type2_noextra)
+ self.vol_type2_id = ref.id
+
+ def tearDown(self):
+ # Remove the instance type from the database
+ db.api.volume_type_purge(context.get_admin_context(),
+ self.vol_type1['name'])
+ db.api.volume_type_purge(context.get_admin_context(),
+ self.vol_type2_noextra['name'])
+ super(VolumeTypeExtraSpecsTestCase, self).tearDown()
+
+ def test_volume_type_specs_get(self):
+ expected_specs = self.vol_type1_specs.copy()
+ actual_specs = db.api.volume_type_extra_specs_get(
+ context.get_admin_context(),
+ self.volume_type1_id)
+ self.assertEquals(expected_specs, actual_specs)
+
+ def test_volume_type_extra_specs_delete(self):
+ expected_specs = self.vol_type1_specs.copy()
+ del expected_specs['vol_extra2']
+ db.api.volume_type_extra_specs_delete(context.get_admin_context(),
+ self.volume_type1_id,
+ 'vol_extra2')
+ actual_specs = db.api.volume_type_extra_specs_get(
+ context.get_admin_context(),
+ self.volume_type1_id)
+ self.assertEquals(expected_specs, actual_specs)
+
+ def test_volume_type_extra_specs_update(self):
+ expected_specs = self.vol_type1_specs.copy()
+ expected_specs['vol_extra3'] = "4"
+ db.api.volume_type_extra_specs_update_or_create(
+ context.get_admin_context(),
+ self.volume_type1_id,
+ dict(vol_extra3=4))
+ actual_specs = db.api.volume_type_extra_specs_get(
+ context.get_admin_context(),
+ self.volume_type1_id)
+ self.assertEquals(expected_specs, actual_specs)
+
+ def test_volume_type_extra_specs_create(self):
+ expected_specs = self.vol_type1_specs.copy()
+ expected_specs['vol_extra4'] = 'value4'
+ expected_specs['vol_extra5'] = 'value5'
+ db.api.volume_type_extra_specs_update_or_create(
+ context.get_admin_context(),
+ self.volume_type1_id,
+ dict(vol_extra4="value4",
+ vol_extra5="value5"))
+ actual_specs = db.api.volume_type_extra_specs_get(
+ context.get_admin_context(),
+ self.volume_type1_id)
+ self.assertEquals(expected_specs, actual_specs)
+
+ def test_volume_type_get_with_extra_specs(self):
+ volume_type = db.api.volume_type_get(
+ context.get_admin_context(),
+ self.volume_type1_id)
+ self.assertEquals(volume_type['extra_specs'],
+ self.vol_type1_specs)
+
+ volume_type = db.api.volume_type_get(
+ context.get_admin_context(),
+ self.vol_type2_id)
+ self.assertEquals(volume_type['extra_specs'], {})
+
+ def test_volume_type_get_by_name_with_extra_specs(self):
+ volume_type = db.api.volume_type_get_by_name(
+ context.get_admin_context(),
+ self.vol_type1['name'])
+ self.assertEquals(volume_type['extra_specs'],
+ self.vol_type1_specs)
+
+ volume_type = db.api.volume_type_get_by_name(
+ context.get_admin_context(),
+ self.vol_type2_noextra['name'])
+ self.assertEquals(volume_type['extra_specs'], {})
+
+ def test_volume_type_get_all(self):
+ expected_specs = self.vol_type1_specs.copy()
+
+ types = db.api.volume_type_get_all(context.get_admin_context())
+
+ self.assertEquals(
+ types[self.vol_type1['name']]['extra_specs'], expected_specs)
+
+ self.assertEquals(
+ types[self.vol_type2_noextra['name']]['extra_specs'], {})
diff --git a/nova/tests/test_vsa.py b/nova/tests/test_vsa.py
new file mode 100644
index 000000000..3d2d2de13
--- /dev/null
+++ b/nova/tests/test_vsa.py
@@ -0,0 +1,182 @@
+# Copyright 2011 OpenStack LLC.
+# 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 base64
+import stubout
+
+from xml.etree import ElementTree
+from xml.etree.ElementTree import Element, SubElement
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import test
+from nova import vsa
+from nova import volume
+from nova.volume import volume_types
+from nova.vsa import utils as vsa_utils
+
+import nova.image.fake
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.vsa')
+
+
+class VsaTestCase(test.TestCase):
+
+ def setUp(self):
+ super(VsaTestCase, self).setUp()
+ self.stubs = stubout.StubOutForTesting()
+ self.vsa_api = vsa.API()
+ self.volume_api = volume.API()
+
+ FLAGS.quota_volumes = 100
+ FLAGS.quota_gigabytes = 10000
+
+ self.context = context.get_admin_context()
+
+ volume_types.create(self.context,
+ 'SATA_500_7200',
+ extra_specs={'type': 'vsa_drive',
+ 'drive_name': 'SATA_500_7200',
+ 'drive_type': 'SATA',
+ 'drive_size': '500',
+ 'drive_rpm': '7200'})
+
+ def fake_show_by_name(meh, context, name):
+ if name == 'wrong_image_name':
+ LOG.debug(_("Test: Emulate wrong VSA name. Raise"))
+ raise exception.ImageNotFound
+ return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}}
+
+ self.stubs.Set(nova.image.fake._FakeImageService,
+ 'show_by_name',
+ fake_show_by_name)
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ super(VsaTestCase, self).tearDown()
+
+ def test_vsa_create_delete_defaults(self):
+ param = {'display_name': 'VSA name test'}
+ vsa_ref = self.vsa_api.create(self.context, **param)
+ self.assertEqual(vsa_ref['display_name'], param['display_name'])
+ self.vsa_api.delete(self.context, vsa_ref['id'])
+
+ def test_vsa_create_delete_check_in_db(self):
+ vsa_list1 = self.vsa_api.get_all(self.context)
+ vsa_ref = self.vsa_api.create(self.context)
+ vsa_list2 = self.vsa_api.get_all(self.context)
+ self.assertEqual(len(vsa_list2), len(vsa_list1) + 1)
+
+ self.vsa_api.delete(self.context, vsa_ref['id'])
+ vsa_list3 = self.vsa_api.get_all(self.context)
+ self.assertEqual(len(vsa_list3), len(vsa_list2) - 1)
+
+ def test_vsa_create_delete_high_vc_count(self):
+ param = {'vc_count': FLAGS.max_vcs_in_vsa + 1}
+ vsa_ref = self.vsa_api.create(self.context, **param)
+ self.assertEqual(vsa_ref['vc_count'], FLAGS.max_vcs_in_vsa)
+ self.vsa_api.delete(self.context, vsa_ref['id'])
+
+ def test_vsa_create_wrong_image_name(self):
+ param = {'image_name': 'wrong_image_name'}
+ self.assertRaises(exception.ApiError,
+ self.vsa_api.create, self.context, **param)
+
+ def test_vsa_create_db_error(self):
+
+ def fake_vsa_create(context, options):
+ LOG.debug(_("Test: Emulate DB error. Raise"))
+ raise exception.Error
+
+ self.stubs.Set(nova.db.api, 'vsa_create', fake_vsa_create)
+ self.assertRaises(exception.ApiError,
+ self.vsa_api.create, self.context)
+
+ def test_vsa_create_wrong_storage_params(self):
+ vsa_list1 = self.vsa_api.get_all(self.context)
+ param = {'storage': [{'stub': 1}]}
+ self.assertRaises(exception.ApiError,
+ self.vsa_api.create, self.context, **param)
+ vsa_list2 = self.vsa_api.get_all(self.context)
+ self.assertEqual(len(vsa_list2), len(vsa_list1))
+
+ param = {'storage': [{'drive_name': 'wrong name'}]}
+ self.assertRaises(exception.ApiError,
+ self.vsa_api.create, self.context, **param)
+
+ def test_vsa_create_with_storage(self, multi_vol_creation=True):
+ """Test creation of VSA with BE storage"""
+
+ FLAGS.vsa_multi_vol_creation = multi_vol_creation
+
+ param = {'storage': [{'drive_name': 'SATA_500_7200',
+ 'num_drives': 3}]}
+ vsa_ref = self.vsa_api.create(self.context, **param)
+ self.assertEqual(vsa_ref['vol_count'], 3)
+ self.vsa_api.delete(self.context, vsa_ref['id'])
+
+ param = {'storage': [{'drive_name': 'SATA_500_7200',
+ 'num_drives': 3}],
+ 'shared': True}
+ vsa_ref = self.vsa_api.create(self.context, **param)
+ self.assertEqual(vsa_ref['vol_count'], 15)
+ self.vsa_api.delete(self.context, vsa_ref['id'])
+
+ def test_vsa_create_with_storage_single_volumes(self):
+ self.test_vsa_create_with_storage(multi_vol_creation=False)
+
+ def test_vsa_update(self):
+ vsa_ref = self.vsa_api.create(self.context)
+
+ param = {'vc_count': FLAGS.max_vcs_in_vsa + 1}
+ vsa_ref = self.vsa_api.update(self.context, vsa_ref['id'], **param)
+ self.assertEqual(vsa_ref['vc_count'], FLAGS.max_vcs_in_vsa)
+
+ param = {'vc_count': 2}
+ vsa_ref = self.vsa_api.update(self.context, vsa_ref['id'], **param)
+ self.assertEqual(vsa_ref['vc_count'], 2)
+
+ self.vsa_api.delete(self.context, vsa_ref['id'])
+
+ def test_vsa_generate_user_data(self):
+
+ FLAGS.vsa_multi_vol_creation = False
+ param = {'display_name': 'VSA name test',
+ 'display_description': 'VSA desc test',
+ 'vc_count': 2,
+ 'storage': [{'drive_name': 'SATA_500_7200',
+ 'num_drives': 3}]}
+ vsa_ref = self.vsa_api.create(self.context, **param)
+ volumes = self.vsa_api.get_all_vsa_drives(self.context,
+ vsa_ref['id'])
+
+ user_data = vsa_utils.generate_user_data(vsa_ref, volumes)
+ user_data = base64.b64decode(user_data)
+
+ LOG.debug(_("Test: user_data = %s"), user_data)
+
+ elem = ElementTree.fromstring(user_data)
+ self.assertEqual(elem.findtext('name'),
+ param['display_name'])
+ self.assertEqual(elem.findtext('description'),
+ param['display_description'])
+ self.assertEqual(elem.findtext('vc_count'),
+ str(param['vc_count']))
+
+ self.vsa_api.delete(self.context, vsa_ref['id'])
diff --git a/nova/tests/test_vsa_volumes.py b/nova/tests/test_vsa_volumes.py
new file mode 100644
index 000000000..b7cd4e840
--- /dev/null
+++ b/nova/tests/test_vsa_volumes.py
@@ -0,0 +1,136 @@
+# Copyright 2011 OpenStack LLC.
+# 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 stubout
+
+from nova import exception
+from nova import flags
+from nova import vsa
+from nova import volume
+from nova import db
+from nova import context
+from nova import test
+from nova import log as logging
+import nova.image.fake
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.vsa.volumes')
+
+
+class VsaVolumesTestCase(test.TestCase):
+
+ def setUp(self):
+ super(VsaVolumesTestCase, self).setUp()
+ self.stubs = stubout.StubOutForTesting()
+ self.vsa_api = vsa.API()
+ self.volume_api = volume.API()
+ self.context = context.get_admin_context()
+
+ self.default_vol_type = self.vsa_api.get_vsa_volume_type(self.context)
+
+ def fake_show_by_name(meh, context, name):
+ return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}}
+
+ self.stubs.Set(nova.image.fake._FakeImageService,
+ 'show_by_name',
+ fake_show_by_name)
+
+ param = {'display_name': 'VSA name test'}
+ vsa_ref = self.vsa_api.create(self.context, **param)
+ self.vsa_id = vsa_ref['id']
+
+ def tearDown(self):
+ if self.vsa_id:
+ self.vsa_api.delete(self.context, self.vsa_id)
+ self.stubs.UnsetAll()
+ super(VsaVolumesTestCase, self).tearDown()
+
+ def _default_volume_param(self):
+ return {
+ 'size': 1,
+ 'snapshot_id': None,
+ 'name': 'Test volume name',
+ 'description': 'Test volume desc name',
+ 'volume_type': self.default_vol_type,
+ 'metadata': {'from_vsa_id': self.vsa_id}
+ }
+
+ def _get_all_volumes_by_vsa(self):
+ return self.volume_api.get_all(self.context,
+ search_opts={'metadata': {"from_vsa_id": str(self.vsa_id)}})
+
+ def test_vsa_volume_create_delete(self):
+ """ Check if volume properly created and deleted. """
+ volume_param = self._default_volume_param()
+ volume_ref = self.volume_api.create(self.context, **volume_param)
+
+ self.assertEqual(volume_ref['display_name'],
+ volume_param['name'])
+ self.assertEqual(volume_ref['display_description'],
+ volume_param['description'])
+ self.assertEqual(volume_ref['size'],
+ volume_param['size'])
+ self.assertEqual(volume_ref['status'],
+ 'creating')
+
+ vols2 = self._get_all_volumes_by_vsa()
+ self.assertEqual(1, len(vols2))
+ volume_ref = vols2[0]
+
+ self.assertEqual(volume_ref['display_name'],
+ volume_param['name'])
+ self.assertEqual(volume_ref['display_description'],
+ volume_param['description'])
+ self.assertEqual(volume_ref['size'],
+ volume_param['size'])
+ self.assertEqual(volume_ref['status'],
+ 'creating')
+
+ self.volume_api.update(self.context,
+ volume_ref['id'], {'status': 'available'})
+ self.volume_api.delete(self.context, volume_ref['id'])
+
+ vols3 = self._get_all_volumes_by_vsa()
+ self.assertEqual(1, len(vols2))
+ volume_ref = vols3[0]
+ self.assertEqual(volume_ref['status'],
+ 'deleting')
+
+ def test_vsa_volume_delete_nonavail_volume(self):
+ """ Check volume deleton in different states. """
+ volume_param = self._default_volume_param()
+ volume_ref = self.volume_api.create(self.context, **volume_param)
+
+ self.volume_api.update(self.context,
+ volume_ref['id'], {'status': 'in-use'})
+ self.assertRaises(exception.ApiError,
+ self.volume_api.delete,
+ self.context, volume_ref['id'])
+
+ def test_vsa_volume_delete_vsa_with_volumes(self):
+ """ Check volume deleton in different states. """
+
+ vols1 = self._get_all_volumes_by_vsa()
+ for i in range(3):
+ volume_param = self._default_volume_param()
+ volume_ref = self.volume_api.create(self.context, **volume_param)
+
+ vols2 = self._get_all_volumes_by_vsa()
+ self.assertEqual(len(vols1) + 3, len(vols2))
+
+ self.vsa_api.delete(self.context, self.vsa_id)
+
+ vols3 = self._get_all_volumes_by_vsa()
+ self.assertEqual(len(vols1), len(vols3))
diff --git a/nova/tests/utils.py b/nova/tests/utils.py
new file mode 100644
index 000000000..e0cacadb4
--- /dev/null
+++ b/nova/tests/utils.py
@@ -0,0 +1,68 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2011 OpenStack 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
+#
+
+import nova.context
+import nova.db
+import nova.flags
+
+FLAGS = nova.flags.FLAGS
+
+
+def get_test_admin_context():
+ return nova.context.get_admin_context()
+
+
+def get_test_instance(context=None):
+ if not context:
+ context = get_test_admin_context()
+
+ test_instance = {'memory_kb': '1024000',
+ 'basepath': '/some/path',
+ 'bridge_name': 'br100',
+ 'vcpus': 2,
+ 'project_id': 'fake',
+ 'bridge': 'br101',
+ 'image_ref': '1',
+ 'instance_type_id': '5'} # m1.small
+
+ instance_ref = nova.db.instance_create(context, test_instance)
+ return instance_ref
+
+
+def get_test_network_info(count=1):
+ ipv6 = FLAGS.use_ipv6
+ fake = 'fake'
+ fake_ip = '0.0.0.0/0'
+ fake_ip_2 = '0.0.0.1/0'
+ fake_ip_3 = '0.0.0.1/0'
+ fake_vlan = 100
+ fake_bridge_interface = 'eth0'
+ network = {'bridge': fake,
+ 'cidr': fake_ip,
+ 'cidr_v6': fake_ip,
+ 'vlan': fake_vlan,
+ 'bridge_interface': fake_bridge_interface,
+ 'injected': False}
+ mapping = {'mac': fake,
+ 'dhcp_server': fake,
+ 'gateway': fake,
+ 'gateway6': fake,
+ 'ips': [{'ip': fake_ip}, {'ip': fake_ip}]}
+ if ipv6:
+ mapping['ip6s'] = [{'ip': fake_ip},
+ {'ip': fake_ip_2},
+ {'ip': fake_ip_3}]
+ return [(network, mapping) for x in xrange(0, count)]