diff --git a/horizon/dashboards/nova/instances_and_volumes/instances/tests.py b/horizon/dashboards/nova/instances_and_volumes/instances/tests.py
index ff718518e..eee16c0b5 100644
--- a/horizon/dashboards/nova/instances_and_volumes/instances/tests.py
+++ b/horizon/dashboards/nova/instances_and_volumes/instances/tests.py
@@ -25,6 +25,7 @@ from copy import deepcopy
from horizon import api
from horizon import test
+
from .tabs import InstanceDetailTabs
from .workflows import LaunchInstance
@@ -41,218 +42,251 @@ class InstanceViewTests(test.TestCase):
super(InstanceViewTests, self).tearDown()
self.reset_times()
+ @test.create_stubs({api: ('server_list',
+ 'flavor_list',
+ 'server_delete',
+ 'volume_list',)})
def test_terminate_instance(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
- self.mox.StubOutWithMock(api, 'server_delete')
- self.mox.StubOutWithMock(api, 'volume_list')
+
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
api.server_delete(IsA(http.HttpRequest), server.id)
+
self.mox.ReplayAll()
formData = {'action': 'instances__terminate__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('server_list',
+ 'flavor_list',
+ 'server_delete',
+ 'volume_list',)})
def test_terminate_instance_exception(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
- self.mox.StubOutWithMock(api, 'server_delete')
- self.mox.StubOutWithMock(api, 'volume_list')
+
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
api.server_delete(IsA(http.HttpRequest), server.id) \
.AndRaise(self.exceptions.nova)
+
self.mox.ReplayAll()
formData = {'action': 'instances__terminate__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('server_pause',
+ 'server_list',
+ 'volume_list',
+ 'flavor_list',)})
def test_pause_instance(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_pause')
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'volume_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
+
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.server_pause(IsA(http.HttpRequest), server.id)
+
self.mox.ReplayAll()
formData = {'action': 'instances__pause__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('server_pause',
+ 'server_list',
+ 'volume_list',
+ 'flavor_list',)})
def test_pause_instance_exception(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'volume_list')
- self.mox.StubOutWithMock(api, 'server_pause')
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
+
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.server_pause(IsA(http.HttpRequest), server.id) \
.AndRaise(self.exceptions.nova)
+
self.mox.ReplayAll()
formData = {'action': 'instances__pause__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('volume_list',
+ 'server_unpause',
+ 'server_list',
+ 'flavor_list',)})
def test_unpause_instance(self):
server = self.servers.first()
server.status = "PAUSED"
- self.mox.StubOutWithMock(api, 'volume_list')
- self.mox.StubOutWithMock(api, 'server_unpause')
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
+
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.server_unpause(IsA(http.HttpRequest), server.id)
+
self.mox.ReplayAll()
formData = {'action': 'instances__pause__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('volume_list',
+ 'server_unpause',
+ 'server_list',
+ 'flavor_list',)})
def test_unpause_instance_exception(self):
server = self.servers.first()
server.status = "PAUSED"
- self.mox.StubOutWithMock(api, 'volume_list')
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'server_unpause')
- self.mox.StubOutWithMock(api, 'flavor_list')
+
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.server_unpause(IsA(http.HttpRequest), server.id) \
.AndRaise(self.exceptions.nova)
+
self.mox.ReplayAll()
formData = {'action': 'instances__pause__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('volume_list',
+ 'server_reboot',
+ 'server_list',
+ 'flavor_list',)})
def test_reboot_instance(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_reboot')
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'volume_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
+
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.server_reboot(IsA(http.HttpRequest), server.id)
+
self.mox.ReplayAll()
formData = {'action': 'instances__reboot__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('volume_list',
+ 'server_reboot',
+ 'server_list',
+ 'flavor_list',)})
def test_reboot_instance_exception(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_reboot')
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'volume_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
+
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.server_reboot(IsA(http.HttpRequest), server.id) \
.AndRaise(self.exceptions.nova)
+
self.mox.ReplayAll()
formData = {'action': 'instances__reboot__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('volume_list',
+ 'server_suspend',
+ 'server_list',
+ 'flavor_list',)})
def test_suspend_instance(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_suspend')
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'volume_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
+
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.server_suspend(IsA(http.HttpRequest), unicode(server.id))
+
self.mox.ReplayAll()
formData = {'action': 'instances__suspend__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('volume_list',
+ 'server_suspend',
+ 'server_list',
+ 'flavor_list',)})
def test_suspend_instance_exception(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_suspend')
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'volume_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
+
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.server_suspend(IsA(http.HttpRequest),
unicode(server.id)).AndRaise(self.exceptions.nova)
+
self.mox.ReplayAll()
formData = {'action': 'instances__suspend__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('volume_list',
+ 'server_resume',
+ 'server_list',
+ 'flavor_list',)})
def test_resume_instance(self):
server = self.servers.first()
server.status = "SUSPENDED"
- self.mox.StubOutWithMock(api, 'server_resume')
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'volume_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
+
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.server_resume(IsA(http.HttpRequest), unicode(server.id))
+
self.mox.ReplayAll()
formData = {'action': 'instances__suspend__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('volume_list',
+ 'server_resume',
+ 'server_list',
+ 'flavor_list',)})
def test_resume_instance_exception(self):
server = self.servers.first()
server.status = "SUSPENDED"
- self.mox.StubOutWithMock(api, 'server_resume')
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'volume_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
+
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.server_resume(IsA(http.HttpRequest),
unicode(server.id)).AndRaise(self.exceptions.nova)
+
self.mox.ReplayAll()
formData = {'action': 'instances__suspend__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
- @test.create_stubs({api: ("server_get", "volume_instance_list",
- "flavor_get", "server_security_groups")})
+ @test.create_stubs({api: ("server_get",
+ "volume_instance_list",
+ "flavor_get",
+ "server_security_groups")})
def test_instance_details_volumes(self):
server = self.servers.first()
- volumes = deepcopy(self.volumes.list())
- volumes[0].device = "/dev/hdk"
- second_vol = deepcopy(volumes[0])
- second_vol.id = 2
- second_vol.device = "/dev/hdb"
- volumes.append(second_vol)
+ volumes = [self.volumes.list()[1]]
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
api.volume_instance_list(IsA(http.HttpRequest),
@@ -267,15 +301,41 @@ class InstanceViewTests(test.TestCase):
url = reverse('horizon:nova:instances_and_volumes:instances:detail',
args=[server.id])
res = self.client.get(url)
+
+ self.assertItemsEqual(res.context['instance'].volumes, volumes)
+
+ @test.create_stubs({api: ("server_get",
+ "volume_instance_list",
+ "flavor_get",
+ "server_security_groups")})
+ def test_instance_details_volume_sorting(self):
+ server = self.servers.first()
+ volumes = self.volumes.list()[1:3]
+
+ api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
+ api.volume_instance_list(IsA(http.HttpRequest),
+ server.id).AndReturn(volumes)
+ api.flavor_get(IsA(http.HttpRequest),
+ server.flavor['id']).AndReturn(self.flavors.first())
+ api.server_security_groups(IsA(http.HttpRequest),
+ server.id).AndReturn(self.security_groups.first())
+
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:nova:instances_and_volumes:instances:detail',
+ args=[server.id])
+ res = self.client.get(url)
+
self.assertItemsEqual(res.context['instance'].volumes, volumes)
- # Test device ordering
self.assertEquals(res.context['instance'].volumes[0].device,
- "/dev/hdb")
+ "/dev/hda")
self.assertEquals(res.context['instance'].volumes[1].device,
"/dev/hdk")
- @test.create_stubs({api: ("server_get", "volume_instance_list",
- "flavor_get", "server_security_groups")})
+ @test.create_stubs({api: ("server_get",
+ "volume_instance_list",
+ "flavor_get",
+ "server_security_groups",)})
def test_instance_details_metadata(self):
server = self.servers.first()
@@ -294,26 +354,25 @@ class InstanceViewTests(test.TestCase):
tg = InstanceDetailTabs(self.request, instance=server)
qs = "?%s=%s" % (tg.param_name, tg.get_tab("overview").get_id())
res = self.client.get(url + qs)
- # Key name
+
self.assertContains(res, "
keyName", 1)
- # Meta data
self.assertContains(res, "someMetaLabel", 1)
self.assertContains(res, "someMetaData", 1)
- # Test escaping of html characters in names
self.assertContains(res, "some<b>html</b>label",
1)
self.assertContains(res, "<!--", 1)
self.assertContains(res, "empty", 1)
self.assertContains(res, "N/A", 1)
+ @test.create_stubs({api: ('server_console_output',)})
def test_instance_log(self):
server = self.servers.first()
CONSOLE_OUTPUT = 'output'
- self.mox.StubOutWithMock(api, 'server_console_output')
api.server_console_output(IsA(http.HttpRequest),
server.id, tail_length=None) \
.AndReturn(CONSOLE_OUTPUT)
+
self.mox.ReplayAll()
url = reverse('horizon:nova:instances_and_volumes:instances:console',
@@ -321,17 +380,19 @@ class InstanceViewTests(test.TestCase):
tg = InstanceDetailTabs(self.request, instance=server)
qs = "?%s=%s" % (tg.param_name, tg.get_tab("log").get_id())
res = self.client.get(url + qs)
+
self.assertNoMessages()
self.assertIsInstance(res, http.HttpResponse)
self.assertContains(res, CONSOLE_OUTPUT)
+ @test.create_stubs({api: ('server_console_output',)})
def test_instance_log_exception(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_console_output')
api.server_console_output(IsA(http.HttpRequest),
server.id, tail_length=None) \
.AndRaise(self.exceptions.nova)
+
self.mox.ReplayAll()
url = reverse('horizon:nova:instances_and_volumes:instances:console',
@@ -339,6 +400,7 @@ class InstanceViewTests(test.TestCase):
tg = InstanceDetailTabs(self.request, instance=server)
qs = "?%s=%s" % (tg.param_name, tg.get_tab("log").get_id())
res = self.client.get(url + qs)
+
self.assertContains(res, "Unable to get log for")
def test_instance_vnc(self):
@@ -360,33 +422,36 @@ class InstanceViewTests(test.TestCase):
redirect = CONSOLE_OUTPUT + '&title=%s(1)' % server.name
self.assertRedirectsNoFollow(res, redirect)
+ @test.create_stubs({api: ('server_vnc_console',)})
def test_instance_vnc_exception(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_vnc_console')
api.server_vnc_console(IsA(http.HttpRequest), server.id) \
.AndRaise(self.exceptions.nova)
+
self.mox.ReplayAll()
url = reverse('horizon:nova:instances_and_volumes:instances:vnc',
args=[server.id])
res = self.client.get(url)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('server_get',
+ 'snapshot_create',
+ 'snapshot_list_detailed',
+ 'image_list_detailed',
+ 'volume_snapshot_list',
+ 'server_list',
+ 'flavor_list',
+ 'server_delete',
+ 'volume_list',)})
def test_create_instance_snapshot(self):
server = self.servers.first()
snapshot_server = deepcopy(server)
setattr(snapshot_server, 'OS-EXT-STS:task_state',
"IMAGE_SNAPSHOT")
- self.mox.StubOutWithMock(api, 'server_get')
- self.mox.StubOutWithMock(api, 'snapshot_create')
- self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
- self.mox.StubOutWithMock(api, 'image_list_detailed')
- self.mox.StubOutWithMock(api, 'volume_snapshot_list')
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
- self.mox.StubOutWithMock(api, 'server_delete')
- self.mox.StubOutWithMock(api, 'volume_list')
+
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
api.snapshot_create(IsA(http.HttpRequest),
server.id,
@@ -397,10 +462,10 @@ class InstanceViewTests(test.TestCase):
api.image_list_detailed(IsA(http.HttpRequest),
marker=None).AndReturn([[], False])
api.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
-
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn([snapshot_server])
api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
+
self.mox.ReplayAll()
formData = {'instance_id': server.id,
@@ -412,42 +477,48 @@ class InstanceViewTests(test.TestCase):
redir_url = reverse('horizon:nova:images_and_snapshots:index')
res = self.client.post(url, formData)
self.assertRedirects(res, redir_url)
+
res = self.client.get(INDEX_URL)
self.assertContains(res, ''
'Snapshotting | ', 1)
+ @test.create_stubs({api: ('server_get',)})
def test_instance_update_get(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_get')
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
+
self.mox.ReplayAll()
url = reverse('horizon:nova:instances_and_volumes:instances:update',
args=[server.id])
res = self.client.get(url)
+
self.assertTemplateUsed(res,
'nova/instances_and_volumes/instances/update.html')
+ @test.create_stubs({api: ('server_get',)})
def test_instance_update_get_server_get_exception(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_get')
+
api.server_get(IsA(http.HttpRequest), server.id) \
.AndRaise(self.exceptions.nova)
+
self.mox.ReplayAll()
url = reverse('horizon:nova:instances_and_volumes:instances:update',
args=[server.id])
res = self.client.get(url)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('server_get', 'server_update')})
def test_instance_update_post(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_get')
- self.mox.StubOutWithMock(api, 'server_update')
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
api.server_update(IsA(http.HttpRequest), server.id, server.name)
+
self.mox.ReplayAll()
formData = {'method': 'UpdateInstance',
@@ -457,16 +528,17 @@ class InstanceViewTests(test.TestCase):
url = reverse('horizon:nova:instances_and_volumes:instances:update',
args=[server.id])
res = self.client.post(url, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api: ('server_get', 'server_update')})
def test_instance_update_post_api_exception(self):
server = self.servers.first()
- self.mox.StubOutWithMock(api, 'server_get')
- self.mox.StubOutWithMock(api, 'server_update')
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
api.server_update(IsA(http.HttpRequest), server.id, server.name) \
.AndRaise(self.exceptions.nova)
+
self.mox.ReplayAll()
formData = {'method': 'UpdateInstance',
@@ -476,20 +548,19 @@ class InstanceViewTests(test.TestCase):
url = reverse('horizon:nova:instances_and_volumes:instances:update',
args=[server.id])
res = self.client.post(url, formData)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api.nova: ('tenant_quota_usages',
+ 'flavor_list',
+ 'keypair_list',
+ 'security_group_list',
+ 'volume_snapshot_list',
+ 'volume_list',),
+ api.glance: ('image_list_detailed',)})
def test_launch_get(self):
quota_usages = self.quota_usages.first()
- self.mox.StubOutWithMock(api.glance, 'image_list_detailed')
- self.mox.StubOutWithMock(api.nova, 'tenant_quota_usages')
- # Two flavor_list calls, however, flavor_list is now memoized.
- self.mox.StubOutWithMock(api.nova, 'flavor_list')
- self.mox.StubOutWithMock(api.nova, 'keypair_list')
- self.mox.StubOutWithMock(api.nova, 'security_group_list')
- self.mox.StubOutWithMock(api.nova, 'volume_snapshot_list')
- self.mox.StubOutWithMock(api.nova, 'volume_list')
-
api.nova.volume_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list())
api.nova.volume_snapshot_list(IsA(http.HttpRequest)) \
@@ -510,20 +581,29 @@ class InstanceViewTests(test.TestCase):
.AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
+
self.mox.ReplayAll()
url = reverse('horizon:nova:instances_and_volumes:instances:launch')
res = self.client.get(url)
+
self.assertTemplateUsed(res,
'nova/instances_and_volumes/instances/launch.html')
- workflow = res.context['workflow']
- self.assertEqual(workflow.name, LaunchInstance.name)
- self.assertQuerysetEqual(workflow.steps,
+ self.assertEqual(res.context['workflow'].name, LaunchInstance.name)
+ self.assertQuerysetEqual(res.context['workflow'].steps,
['',
'',
'',
''])
+ @test.create_stubs({api.glance: ('image_list_detailed',),
+ api.nova: ('flavor_list',
+ 'keypair_list',
+ 'security_group_list',
+ 'volume_list',
+ 'volume_snapshot_list',
+ 'tenant_quota_usages',
+ 'server_create',)})
def test_launch_post(self):
flavor = self.flavors.first()
image = self.images.first()
@@ -536,15 +616,6 @@ class InstanceViewTests(test.TestCase):
volume_choice = "%s:vol" % volume.id
block_device_mapping = {device_name: u"%s::0" % volume_choice}
- self.mox.StubOutWithMock(api.glance, 'image_list_detailed')
- self.mox.StubOutWithMock(api.nova, 'flavor_list')
- self.mox.StubOutWithMock(api.nova, 'keypair_list')
- self.mox.StubOutWithMock(api.nova, 'security_group_list')
- self.mox.StubOutWithMock(api.nova, 'volume_list')
- self.mox.StubOutWithMock(api.nova, 'volume_snapshot_list')
- self.mox.StubOutWithMock(api.nova, 'tenant_quota_usages')
- self.mox.StubOutWithMock(api.nova, 'server_create')
-
api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list())
api.nova.keypair_list(IsA(http.HttpRequest)) \
@@ -569,6 +640,7 @@ class InstanceViewTests(test.TestCase):
[sec_group.name],
block_device_mapping,
instance_count=IsA(int))
+
self.mox.ReplayAll()
form_data = {'flavor': flavor.id,
@@ -586,19 +658,19 @@ class InstanceViewTests(test.TestCase):
'count': 1}
url = reverse('horizon:nova:instances_and_volumes:instances:launch')
res = self.client.post(url, form_data)
+
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res,
reverse('horizon:nova:instances_and_volumes:index'))
+ @test.create_stubs({api.glance: ('image_list_detailed',),
+ api.nova: ('tenant_quota_usages',
+ 'flavor_list',
+ 'keypair_list',
+ 'volume_list',
+ 'security_group_list',
+ 'volume_snapshot_list',)})
def test_launch_flavorlist_error(self):
- self.mox.StubOutWithMock(api.glance, 'image_list_detailed')
- self.mox.StubOutWithMock(api.nova, 'tenant_quota_usages')
- self.mox.StubOutWithMock(api.nova, 'flavor_list')
- self.mox.StubOutWithMock(api.nova, 'keypair_list')
- self.mox.StubOutWithMock(api.nova, 'security_group_list')
- self.mox.StubOutWithMock(api.nova, 'volume_snapshot_list')
- self.mox.StubOutWithMock(api.nova, 'volume_list')
-
api.nova.volume_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list())
api.nova.volume_snapshot_list(IsA(http.HttpRequest)) \
@@ -619,13 +691,22 @@ class InstanceViewTests(test.TestCase):
.AndReturn(self.keypairs.list())
api.nova.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
+
self.mox.ReplayAll()
url = reverse('horizon:nova:instances_and_volumes:instances:launch')
res = self.client.get(url)
+
self.assertTemplateUsed(res,
'nova/instances_and_volumes/instances/launch.html')
+ @test.create_stubs({api.glance: ('image_list_detailed',),
+ api.nova: ('flavor_list',
+ 'keypair_list',
+ 'security_group_list',
+ 'volume_list',
+ 'server_create',
+ 'volume_snapshot_list',)})
def test_launch_form_keystone_exception(self):
flavor = self.flavors.first()
image = self.images.first()
@@ -634,14 +715,6 @@ class InstanceViewTests(test.TestCase):
sec_group = self.security_groups.first()
customization_script = 'userData'
- self.mox.StubOutWithMock(api.glance, 'image_list_detailed')
- self.mox.StubOutWithMock(api.nova, 'flavor_list')
- self.mox.StubOutWithMock(api.nova, 'keypair_list')
- self.mox.StubOutWithMock(api.nova, 'security_group_list')
- self.mox.StubOutWithMock(api.nova, 'server_create')
- self.mox.StubOutWithMock(api.nova, 'volume_list')
- self.mox.StubOutWithMock(api.nova, 'volume_snapshot_list')
-
api.nova.volume_snapshot_list(IsA(http.HttpRequest)) \
.AndReturn(self.volumes.list())
api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
@@ -665,6 +738,7 @@ class InstanceViewTests(test.TestCase):
None,
instance_count=IsA(int)) \
.AndRaise(self.exceptions.keystone)
+
self.mox.ReplayAll()
form_data = {'flavor': flavor.id,
@@ -680,8 +754,16 @@ class InstanceViewTests(test.TestCase):
'count': 1}
url = reverse('horizon:nova:instances_and_volumes:instances:launch')
res = self.client.post(url, form_data)
+
self.assertRedirectsNoFollow(res, INDEX_URL)
+ @test.create_stubs({api.glance: ('image_list_detailed',),
+ api.nova: ('flavor_list',
+ 'keypair_list',
+ 'security_group_list',
+ 'volume_list',
+ 'tenant_quota_usages',
+ 'volume_snapshot_list',)})
def test_launch_form_instance_count_error(self):
flavor = self.flavors.first()
image = self.images.first()
@@ -693,14 +775,6 @@ class InstanceViewTests(test.TestCase):
device_name = u'vda'
volume_choice = "%s:vol" % volume.id
- self.mox.StubOutWithMock(api.glance, 'image_list_detailed')
- self.mox.StubOutWithMock(api.nova, 'flavor_list')
- self.mox.StubOutWithMock(api.nova, 'keypair_list')
- self.mox.StubOutWithMock(api.nova, 'security_group_list')
- self.mox.StubOutWithMock(api.nova, 'volume_list')
- self.mox.StubOutWithMock(api.nova, 'volume_snapshot_list')
- self.mox.StubOutWithMock(api.nova, 'tenant_quota_usages')
-
api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list())
api.nova.keypair_list(IsA(http.HttpRequest)) \
@@ -721,6 +795,7 @@ class InstanceViewTests(test.TestCase):
.AndReturn(self.flavors.list())
api.nova.tenant_quota_usages(IsA(http.HttpRequest)) \
.AndReturn(self.quota_usages.first())
+
self.mox.ReplayAll()
form_data = {'flavor': flavor.id,
@@ -738,4 +813,5 @@ class InstanceViewTests(test.TestCase):
'count': 0}
url = reverse('horizon:nova:instances_and_volumes:instances:launch')
res = self.client.post(url, form_data)
+
self.assertContains(res, "greater than or equal to 1")
diff --git a/horizon/dashboards/nova/instances_and_volumes/tests.py b/horizon/dashboards/nova/instances_and_volumes/tests.py
index 169ea86f4..e08fc9fce 100644
--- a/horizon/dashboards/nova/instances_and_volumes/tests.py
+++ b/horizon/dashboards/nova/instances_and_volumes/tests.py
@@ -18,7 +18,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from copy import deepcopy
from django import http
from django.core.urlresolvers import reverse
from mox import IsA
@@ -28,10 +27,8 @@ from horizon import test
class InstancesAndVolumesViewTest(test.TestCase):
+ @test.create_stubs({api: ('flavor_list', 'server_list', 'volume_list',)})
def test_index(self):
- self.mox.StubOutWithMock(api, 'flavor_list')
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'volume_list')
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
@@ -49,23 +46,12 @@ class InstancesAndVolumesViewTest(test.TestCase):
self.assertItemsEqual(instances, self.servers.list())
self.assertItemsEqual(volumes, self.volumes.list())
+ @test.create_stubs({api: ('flavor_list', 'server_list', 'volume_list',)})
def test_attached_volume(self):
- volumes = deepcopy(self.volumes.list())
- attached_volume = deepcopy(self.volumes.list()[0])
- attached_volume.id = "2"
- attached_volume.display_name = "Volume2 name"
- attached_volume.size = "80"
- attached_volume.status = "in-use"
- attached_volume.attachments = [{"server_id": "1",
- "device": "/dev/hdn"}]
- volumes.append(attached_volume)
-
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'volume_list')
- self.mox.StubOutWithMock(api, 'flavor_list')
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
- api.volume_list(IsA(http.HttpRequest)).AndReturn(volumes)
+ api.volume_list(IsA(http.HttpRequest)) \
+ .AndReturn(self.volumes.list()[1:3])
self.mox.ReplayAll()
@@ -78,21 +64,20 @@ class InstancesAndVolumesViewTest(test.TestCase):
resp_volumes = res.context['volumes_table'].data
self.assertItemsEqual(instances, self.servers.list())
- self.assertItemsEqual(resp_volumes, volumes)
+ self.assertItemsEqual(resp_volumes, self.volumes.list()[1:3])
- self.assertContains(res, ">Volume name<", 1, 200)
- self.assertContains(res, ">40GB<", 1, 200)
- self.assertContains(res, ">Available<", 1, 200)
-
- self.assertContains(res, ">Volume2 name<", 1, 200)
- self.assertContains(res, ">80GB<", 1, 200)
- self.assertContains(res, ">In-Use<", 1, 200)
- self.assertContains(res, ">server_1<", 2, 200)
- self.assertContains(res, "on /dev/hdn", 1, 200)
+ self.assertContains(res, ">My Volume<", 1, 200)
+ self.assertContains(res, ">30GB<", 1, 200)
+ self.assertContains(res, ">3b189ac8-9166-ac7f-90c9-16c8bf9e01ac<",
+ 1,
+ 200)
+ self.assertContains(res, ">10GB<", 1, 200)
+ self.assertContains(res, ">In-Use<", 2, 200)
+ self.assertContains(res, "on /dev/hda", 1, 200)
+ self.assertContains(res, "on /dev/hdk", 1, 200)
+ @test.create_stubs({api: ('server_list', 'volume_list',)})
def test_index_server_list_exception(self):
- self.mox.StubOutWithMock(api, 'server_list')
- self.mox.StubOutWithMock(api, 'volume_list')
api.server_list(IsA(http.HttpRequest)).AndRaise(self.exceptions.nova)
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
diff --git a/horizon/dashboards/nova/instances_and_volumes/views.py b/horizon/dashboards/nova/instances_and_volumes/views.py
index 4ba6ab313..5c79d43f8 100644
--- a/horizon/dashboards/nova/instances_and_volumes/views.py
+++ b/horizon/dashboards/nova/instances_and_volumes/views.py
@@ -22,7 +22,6 @@
"""
Views for Instances and Volumes.
"""
-import re
import logging
from django.utils.translation import ugettext_lazy as _
@@ -73,16 +72,12 @@ class IndexView(tables.MultiTableView):
instances = SortedDict([(inst.id, inst) for inst in
self._get_instances()])
for volume in volumes:
- # Truncate the description for proper display.
- if len(getattr(volume, 'display_description', '')) > 33:
- truncated_string = volume.display_description[:30].strip()
- # Remove non-word, and underscore characters, from the end
- # of the string before we add the ellepsis.
- truncated_string = re.sub(ur'[^\w\s]+$',
- '',
- truncated_string)
+ # It is possible to create a volume with no name through the
+ # EC2 API, use the ID in those cases.
+ if not volume.display_name:
+ volume.display_name = volume.id
- volume.display_description = truncated_string + u'...'
+ description = getattr(volume, 'display_description', '')
for att in volume.attachments:
server_id = att.get('server_id', None)
diff --git a/horizon/dashboards/nova/instances_and_volumes/volumes/tables.py b/horizon/dashboards/nova/instances_and_volumes/volumes/tables.py
index e7bca4cd1..0632db149 100644
--- a/horizon/dashboards/nova/instances_and_volumes/volumes/tables.py
+++ b/horizon/dashboards/nova/instances_and_volumes/volumes/tables.py
@@ -135,7 +135,8 @@ class VolumesTableBase(tables.DataTable):
name = tables.Column("display_name", verbose_name=_("Name"),
link="%s:volumes:detail" % URL_PREFIX)
description = tables.Column("display_description",
- verbose_name=_("Description"))
+ verbose_name=_("Description"),
+ truncate=40)
size = tables.Column(get_size, verbose_name=_("Size"))
status = tables.Column("status",
filters=(title,),
diff --git a/horizon/dashboards/nova/instances_and_volumes/volumes/tests.py b/horizon/dashboards/nova/instances_and_volumes/volumes/tests.py
index 3e52458e9..9d90f3f18 100644
--- a/horizon/dashboards/nova/instances_and_volumes/volumes/tests.py
+++ b/horizon/dashboards/nova/instances_and_volumes/volumes/tests.py
@@ -18,7 +18,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from copy import deepcopy
from django import http
from django.core.urlresolvers import reverse
from mox import IsA
@@ -28,14 +27,15 @@ from horizon import test
class VolumeViewTests(test.TestCase):
+ @test.create_stubs({api: ('volume_get',), api.nova: ('server_list',)})
def test_edit_attachments(self):
volume = self.volumes.first()
servers = self.servers.list()
- self.mox.StubOutWithMock(api, 'volume_get')
- self.mox.StubOutWithMock(api.nova, 'server_list')
+
api.volume_get(IsA(http.HttpRequest), volume.id) \
.AndReturn(volume)
api.nova.server_list(IsA(http.HttpRequest)).AndReturn(servers)
+
self.mox.ReplayAll()
url = reverse('horizon:nova:instances_and_volumes:volumes:attach',
@@ -47,54 +47,42 @@ class VolumeViewTests(test.TestCase):
2)
self.assertEqual(res.status_code, 200)
+ @test.create_stubs({api: ('volume_get',),
+ api.nova: ('server_get', 'server_list',)})
def test_edit_attachments_attached_volume(self):
server = self.servers.first()
- servers = deepcopy(self.servers)
- active_server = deepcopy(self.servers.first())
- active_server.status = 'ACTIVE'
- active_server.id = "3"
- servers.add(active_server)
- volumes = deepcopy(self.volumes)
- volume = deepcopy(self.volumes.first())
- volume.id = "2"
- volume.status = "in-use"
- volume.attachments = [{"id": "1", "server_id": server.id,
- "device": "/dev/hdn"}]
- volumes.add(volume)
+ volume = self.volumes.list()[0]
- self.mox.StubOutWithMock(api, 'volume_get')
- self.mox.StubOutWithMock(api.nova, 'server_list')
- self.mox.StubOutWithMock(api.nova, 'server_get')
- api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
api.volume_get(IsA(http.HttpRequest), volume.id) \
.AndReturn(volume)
- api.nova.server_list(IsA(http.HttpRequest)).AndReturn(servers.list())
+ api.nova.server_list(IsA(http.HttpRequest)) \
+ .AndReturn(self.servers.list())
+
self.mox.ReplayAll()
url = reverse('horizon:nova:instances_and_volumes:volumes:attach',
args=[volume.id])
res = self.client.get(url)
- # Asserting length of 1 instance (plus 'Select ..' item).
- # The other instance is already attached to this volume
- self.assertEqual(len(res.context['form'].fields['instance']._choices),
- 2)
+
self.assertEqual(res.context['form'].\
fields['instance']._choices[0][1],
"Select an instance")
- # The instance choice should not be server_id = 3
- self.assertNotEqual(res.context['form'].\
- fields['instance']._choices[1][0],
- volume.attachments[0]['server_id'])
+ self.assertEqual(len(res.context['form'].fields['instance'].choices),
+ 2)
+ self.assertEqual(res.context['form'].fields['instance']._choices[1][0],
+ server.id)
self.assertEqual(res.status_code, 200)
+ @test.create_stubs({api.nova: ('volume_get', 'server_get',)})
def test_detail_view(self):
volume = self.volumes.first()
server = self.servers.first()
+
volume.attachments = [{"server_id": server.id}]
- self.mox.StubOutWithMock(api.nova, 'volume_get')
- self.mox.StubOutWithMock(api.nova, 'server_get')
+
api.nova.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume)
api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
+
self.mox.ReplayAll()
url = reverse('horizon:nova:instances_and_volumes:volumes:detail',
@@ -102,7 +90,8 @@ class VolumeViewTests(test.TestCase):
res = self.client.get(url)
self.assertContains(res, "Volume name", 1, 200)
- self.assertContains(res, "1", 1, 200)
+ self.assertContains(res, "41023e92-8008-4c8b-8059-" \
+ "7f2293ff3775", 1, 200)
self.assertContains(res, "Available", 1, 200)
self.assertContains(res, "40 GB", 1, 200)
self.assertContains(res, "04/01/12 at 10:30:00", 1, 200)
diff --git a/horizon/tables/base.py b/horizon/tables/base.py
index 940fab6cc..178447fcc 100644
--- a/horizon/tables/base.py
+++ b/horizon/tables/base.py
@@ -26,6 +26,7 @@ from django import template
from django.conf import settings
from django.contrib import messages
from django.core import urlresolvers
+from django.template.defaultfilters import truncatechars
from django.template.loader import render_to_string
from django.utils import http
from django.utils.datastructures import SortedDict
@@ -142,6 +143,14 @@ class Column(html.HTMLElement):
A dict of HTML attribute strings which should be added to this column.
Example: ``attrs={"data-foo": "bar"}``.
+
+ .. attribute:: truncate
+
+ An integer for the maximum length of the string in this column. If the
+ data in this column is larger than the supplied number, the data for
+ this column will be truncated and an ellipsis will be appended to the
+ truncated data.
+ Defaults to ``None``.
"""
summation_methods = {
"sum": sum,
@@ -172,31 +181,35 @@ class Column(html.HTMLElement):
def __init__(self, transform, verbose_name=None, sortable=True,
link=None, hidden=False, attrs=None, status=False,
status_choices=None, display_choices=None, empty_value=None,
- filters=None, classes=None, summation=None, auto=None):
+ filters=None, classes=None, summation=None, auto=None,
+ truncate=None):
self.classes = list(classes or getattr(self, "classes", []))
super(Column, self).__init__()
self.attrs.update(attrs or {})
- self.auto = auto
-
if callable(transform):
self.transform = transform
self.name = transform.__name__
else:
self.transform = unicode(transform)
self.name = self.transform
- self.sortable = sortable
+
# Empty string is a valid value for verbose_name
if verbose_name is None:
verbose_name = self.transform.title()
else:
verbose_name = verbose_name
+
+ self.auto = auto
+ self.sortable = sortable
self.verbose_name = verbose_name
self.link = link
self.hidden = hidden
self.status = status
self.empty_value = empty_value or '-'
self.filters = filters or []
+ self.truncate = truncate
+
if status_choices:
self.status_choices = status_choices
self.display_choices = display_choices
@@ -257,20 +270,29 @@ class Column(html.HTMLElement):
method for this column.
"""
datum_id = self.table.get_object_id(datum)
+
if datum_id in self.table._data_cache[self]:
return self.table._data_cache[self][datum_id]
+
data = self.get_raw_data(datum)
display_value = None
+
if self.display_choices:
display_value = [display for (value, display) in
self.display_choices
if value.lower() == (data or '').lower()]
+
if display_value:
data = display_value[0]
else:
for filter_func in self.filters:
data = filter_func(data)
+
+ if data and self.truncate:
+ data = truncatechars(data, self.truncate)
+
self.table._data_cache[self][datum_id] = data
+
return self.table._data_cache[self][datum_id]
def get_link_url(self, datum):
diff --git a/horizon/tests/table_tests.py b/horizon/tests/table_tests.py
index 701fee1fa..b7a108a0c 100644
--- a/horizon/tests/table_tests.py
+++ b/horizon/tests/table_tests.py
@@ -56,6 +56,11 @@ TEST_DATA_4 = (
FakeObject('2', 'object_2', 4, 'up'),
)
+TEST_DATA_5 = (
+ FakeObject('1', 'object_1', 'A Value That is longer than 35 characters!',
+ 'down', 'optional_1'),
+)
+
class MyLinkAction(tables.LinkAction):
name = "login"
@@ -153,7 +158,8 @@ class MyTable(tables.DataTable):
sortable=True,
link='http://example.com/',
attrs={'class': 'green blue'},
- summation="average")
+ summation="average",
+ truncate=35)
status = tables.Column('status', link=get_link)
optional = tables.Column('optional', empty_value='N/A')
excluded = tables.Column('excluded')
@@ -379,6 +385,14 @@ class DataTableTests(test.TestCase):
self.assertEqual(row.cells['status'].get_status_class(cell_status),
'status_up')
+ def test_table_column_truncation(self):
+ self.table = MyTable(self.request, TEST_DATA_5)
+ row = self.table.get_rows()[0]
+
+ self.assertEqual(len(row.cells['value'].data), 35)
+ self.assertEqual(row.cells['value'].data,
+ u'A Value That is longer than 35 c...')
+
def test_table_rendering(self):
self.table = MyTable(self.request, TEST_DATA)
# Table actions
diff --git a/horizon/tests/test_data/nova_data.py b/horizon/tests/test_data/nova_data.py
index 5cdbeb29c..f641628ed 100644
--- a/horizon/tests/test_data/nova_data.py
+++ b/horizon/tests/test_data/nova_data.py
@@ -146,14 +146,38 @@ def data(TEST):
# Volumes
volume = volumes.Volume(volumes.VolumeManager(None),
- dict(id="1",
+ dict(id="41023e92-8008-4c8b-8059-7f2293ff3775",
name='test_volume',
status='available',
size=40,
display_name='Volume name',
created_at='2012-04-01 10:30:00',
- attachments={}))
+ attachments=[]))
+ nameless_volume = volumes.Volume(volumes.VolumeManager(None),
+ dict(id="3b189ac8-9166-ac7f-90c9-16c8bf9e01ac",
+ name='',
+ status='in-use',
+ size=10,
+ display_name='',
+ display_description='',
+ device="/dev/hda",
+ created_at='2010-11-21 18:34:25',
+ attachments=[{"id": "1", "server_id": '1',
+ "device": "/dev/hda"}]))
+ attached_volume = volumes.Volume(volumes.VolumeManager(None),
+ dict(id="8cba67c1-2741-6c79-5ab6-9c2bf8c96ab0",
+ name='my_volume',
+ status='in-use',
+ size=30,
+ display_name='My Volume',
+ display_description='',
+ device="/dev/hdk",
+ created_at='2011-05-01 11:54:33',
+ attachments=[{"id": "2", "server_id": '1',
+ "device": "/dev/hdk"}]))
TEST.volumes.add(volume)
+ TEST.volumes.add(nameless_volume)
+ TEST.volumes.add(attached_volume)
# Flavors
flavor_1 = flavors.Flavor(flavors.FlavorManager(None),