diff --git a/django-openstack/django_openstack/dash/views/instances.py b/django-openstack/django_openstack/dash/views/instances.py index cbce172e9..6a73f48c1 100644 --- a/django-openstack/django_openstack/dash/views/instances.py +++ b/django-openstack/django_openstack/dash/views/instances.py @@ -25,15 +25,16 @@ import datetime import logging from django import http +from django import shortcuts from django import template from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.shortcuts import redirect, render_to_response from django.utils.translation import ugettext as _ from django_openstack import api from django_openstack import forms +from django_openstack import utils import openstack.compute.servers import openstackx.api.exceptions as api_exceptions @@ -61,7 +62,7 @@ class TerminateInstance(forms.SelfHandlingForm): LOG.info(msg) messages.success(request, msg) - return redirect(request.build_absolute_uri()) + return shortcuts.redirect(request.build_absolute_uri()) class RebootInstance(forms.SelfHandlingForm): @@ -83,7 +84,7 @@ class RebootInstance(forms.SelfHandlingForm): LOG.info(msg) messages.success(request, msg) - return redirect(request.build_absolute_uri()) + return shortcuts.redirect(request.build_absolute_uri()) @login_required @@ -97,7 +98,7 @@ def index(request, tenant_id): try: instances = api.server_list(request) # TODO(markgius): Why isn't this an apiexception? - except Exception as e: + except api_exceptions.ApiException as e: LOG.error('Exception in instance index', exc_info=True) messages.error(request, 'Unable to get instance list: %s' % e.message) @@ -106,7 +107,7 @@ def index(request, tenant_id): terminate_form = TerminateInstance() reboot_form = RebootInstance() - return render_to_response('dash_instances.html', { + return shortcuts.render_to_response('dash_instances.html', { 'instances': instances, 'terminate_form': terminate_form, 'reboot_form': reboot_form, @@ -115,10 +116,10 @@ def index(request, tenant_id): @login_required def usage(request, tenant_id=None): - today = datetime.date.today() + today = utils.today() date_start = datetime.date(today.year, today.month, 1) - datetime_start = datetime.datetime.combine(date_start, datetime.time()) - datetime_end = datetime.datetime.utcnow() + datetime_start = datetime.datetime.combine(date_start, utils.time()) + datetime_end = utils.utcnow() usage = {} if not tenant_id: @@ -130,7 +131,7 @@ def usage(request, tenant_id=None): LOG.error('ApiException in instance usage', exc_info=True) messages.error(request, 'Unable to get usage info: %s' % e.message) - return render_to_response('dash_usage.html', { + return shortcuts.render_to_response('dash_usage.html', { 'usage': usage, }, context_instance=template.RequestContext(request)) @@ -150,14 +151,14 @@ def console(request, tenant_id, instance_id): messages.error(request, 'Unable to get log for instance %s: %s' % (instance_id, e.message)) - return redirect('dash_instances', tenant_id) + return shortcuts.redirect('dash_instances', tenant_id) @login_required def vnc(request, tenant_id, instance_id): try: console = api.console_create(request, instance_id, 'vnc') - return redirect(console.output) + return shortcuts.redirect(console.output) except api_exceptions.ApiException, e: LOG.error('ApiException while fetching instance vnc connection', exc_info=True) @@ -165,4 +166,4 @@ def vnc(request, tenant_id, instance_id): messages.error(request, 'Unable to get vnc console for instance %s: %s' % (instance_id, e.message)) - return redirect('dash_instances', tenant_id) + return shortcuts.redirect('dash_instances', tenant_id) diff --git a/django-openstack/django_openstack/tests/testsettings.py b/django-openstack/django_openstack/tests/testsettings.py index 6020f090b..da2878522 100644 --- a/django-openstack/django_openstack/tests/testsettings.py +++ b/django-openstack/django_openstack/tests/testsettings.py @@ -47,7 +47,7 @@ MIDDLEWARE_CLASSES = ( 'django_openstack.middleware.keystone.AuthenticationMiddleware', ) -ROOT_URLCONF = 'django_openstack.urls' +ROOT_URLCONF = 'django_openstack.tests.testurls' TEMPLATE_DIRS = ( os.path.join(ROOT_PATH, 'tests', 'templates') ) diff --git a/django-openstack/django_openstack/tests/testurls.py b/django-openstack/django_openstack/tests/testurls.py index d07a5e644..870ff6b93 100644 --- a/django-openstack/django_openstack/tests/testurls.py +++ b/django-openstack/django_openstack/tests/testurls.py @@ -24,13 +24,14 @@ URL patterns for testing django-openstack views. from django.conf.urls.defaults import * +from django_openstack import urls as django_openstack_urls + urlpatterns = patterns('', - url(r'^projects/', include('django_openstack.nova.urls.project')), - url(r'^region/', include('django_openstack.nova.urls.region')), - #url(r'^admin/projects/', include('django_openstack.nova.urls.admin_project')), - #url(r'^admin/roles/', include('django_openstack.nova.urls.admin_roles')), - url(r'^credentials/download/(?P\w+)/$', - 'django_openstack.nova.views.credentials.authorize_credentials', - name='nova_credentials_authorize'), + url(r'^dash/$', 'django_openstack.dash.views.instances.usage', name='dash_overview'), + url(r'^syspanel/$', 'django_openstack.syspanel.views.instances.usage', name='syspanel_overview') ) + + +# NOTE(termie): just append them since we want the routes at the root +urlpatterns += django_openstack_urls.urlpatterns diff --git a/django-openstack/django_openstack/tests/view_tests/dash/instance_tests.py b/django-openstack/django_openstack/tests/view_tests/dash/instance_tests.py new file mode 100644 index 000000000..ba5025d85 --- /dev/null +++ b/django-openstack/django_openstack/tests/view_tests/dash/instance_tests.py @@ -0,0 +1,317 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +import datetime + +from django import http +from django.contrib import messages +from django.core.urlresolvers import reverse +from django_openstack import api +from django_openstack import utils +from django_openstack.tests.view_tests import base +from openstackx.api import exceptions as api_exceptions +from mox import IsA + + +class InstanceViewTests(base.BaseViewTests): + def setUp(self): + super(InstanceViewTests, self).setUp() + server_inner = base.Object() + server_inner.id = 1 + server_inner.name = 'serverName' + self.servers = (api.Server(server_inner, None),) + + def test_index(self): + self.mox.StubOutWithMock(api, 'server_list') + api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers) + + self.mox.ReplayAll() + + res = self.client.get(reverse('dash_instances', + args=[self.TEST_TENANT])) + + self.assertTemplateUsed(res, 'dash_instances.html') + self.assertItemsEqual(res.context['instances'], self.servers) + + self.mox.VerifyAll() + + def test_index_server_list_exception(self): + self.mox.StubOutWithMock(api, 'server_list') + exception = api_exceptions.ApiException('apiException') + api.server_list(IsA(http.HttpRequest)).AndRaise(exception) + + self.mox.ReplayAll() + + res = self.client.get(reverse('dash_instances', + args=[self.TEST_TENANT])) + + self.assertTemplateUsed(res, 'dash_instances.html') + self.assertEqual(len(res.context['instances']), 0) + + self.mox.VerifyAll() + + def test_terminate_instance(self): + formData = {'method': 'TerminateInstance', + 'instance': self.servers[0].id, + } + + self.mox.StubOutWithMock(api, 'server_get') + api.server_get(IsA(http.HttpRequest), + str(self.servers[0].id)).AndReturn(self.servers[0]) + self.mox.StubOutWithMock(api, 'server_delete') + api.server_delete(IsA(http.HttpRequest), + self.servers[0]) + + self.mox.ReplayAll() + + res = self.client.post(reverse('dash_instances', + args=[self.TEST_TENANT]), + formData) + + self.assertRedirectsNoFollow(res, reverse('dash_instances', + args=[self.TEST_TENANT])) + + self.mox.VerifyAll() + + def test_terminate_instance_exception(self): + formData = {'method': 'TerminateInstance', + 'instance': self.servers[0].id, + } + + self.mox.StubOutWithMock(api, 'server_get') + api.server_get(IsA(http.HttpRequest), + str(self.servers[0].id)).AndReturn(self.servers[0]) + + exception = api_exceptions.ApiException('ApiException', + message='apiException') + self.mox.StubOutWithMock(api, 'server_delete') + api.server_delete(IsA(http.HttpRequest), + self.servers[0]).AndRaise(exception) + + self.mox.StubOutWithMock(messages, 'error') + messages.error(IsA(http.HttpRequest), IsA(unicode)) + + self.mox.ReplayAll() + + res = self.client.post(reverse('dash_instances', + args=[self.TEST_TENANT]), + formData) + + self.assertRedirectsNoFollow(res, reverse('dash_instances', + args=[self.TEST_TENANT])) + + self.mox.VerifyAll() + + def test_reboot_instance(self): + formData = {'method': 'RebootInstance', + 'instance': self.servers[0].id, + } + + self.mox.StubOutWithMock(api, 'server_reboot') + api.server_reboot(IsA(http.HttpRequest), unicode(self.servers[0].id)) + + self.mox.ReplayAll() + + res = self.client.post(reverse('dash_instances', + args=[self.TEST_TENANT]), + formData) + + self.assertRedirectsNoFollow(res, reverse('dash_instances', + args=[self.TEST_TENANT])) + + self.mox.VerifyAll() + + def test_reboot_instance_exception(self): + formData = {'method': 'RebootInstance', + 'instance': self.servers[0].id, + } + + self.mox.StubOutWithMock(api, 'server_reboot') + exception = api_exceptions.ApiException('ApiException', + message='apiException') + api.server_reboot(IsA(http.HttpRequest), + unicode(self.servers[0].id)).AndRaise(exception) + + self.mox.StubOutWithMock(messages, 'error') + messages.error(IsA(http.HttpRequest), IsA(str)) + + self.mox.ReplayAll() + + res = self.client.post(reverse('dash_instances', + args=[self.TEST_TENANT]), + formData) + + self.assertRedirectsNoFollow(res, reverse('dash_instances', + args=[self.TEST_TENANT])) + + self.mox.VerifyAll() + + def override_times(self, time=datetime.datetime.now): + now = datetime.datetime.utcnow() + utils.time.override_time = \ + datetime.time(now.hour, now.minute, now.second) + utils.today.override_time = datetime.date(now.year, now.month, now.day) + utils.utcnow.override_time = now + + return now + + def reset_times(self): + utils.time.override_time = None + utils.today.override_time = None + utils.utcnow.override_time = None + + def test_instance_usage(self): + TEST_RETURN = 'testReturn' + + now = self.override_times() + + self.mox.StubOutWithMock(api, 'usage_get') + api.usage_get(IsA(http.HttpRequest), self.TEST_TENANT, + datetime.datetime(now.year, now.month, 1, + now.hour, now.minute, now.second), + now).AndReturn(TEST_RETURN) + + self.mox.ReplayAll() + + res = self.client.get(reverse('dash_usage', args=[self.TEST_TENANT])) + + self.assertTemplateUsed(res, 'dash_usage.html') + + self.assertEqual(res.context['usage'], TEST_RETURN) + + self.mox.VerifyAll() + + self.reset_times() + + def test_instance_usage_exception(self): + now = self.override_times() + + exception = api_exceptions.ApiException('apiException', + message='apiException') + self.mox.StubOutWithMock(api, 'usage_get') + api.usage_get(IsA(http.HttpRequest), self.TEST_TENANT, + datetime.datetime(now.year, now.month, 1, + now.hour, now.minute, now.second), + now).AndRaise(exception) + + self.mox.StubOutWithMock(messages, 'error') + messages.error(IsA(http.HttpRequest), IsA(str)) + + self.mox.ReplayAll() + + res = self.client.get(reverse('dash_usage', args=[self.TEST_TENANT])) + + self.assertTemplateUsed(res, 'dash_usage.html') + + self.assertEqual(res.context['usage'], {}) + + self.mox.VerifyAll() + + self.reset_times() + + def test_instance_usage_default_tenant(self): + TEST_RETURN = 'testReturn' + + now = self.override_times() + + self.mox.StubOutWithMock(api, 'usage_get') + api.usage_get(IsA(http.HttpRequest), self.TEST_TENANT, + datetime.datetime(now.year, now.month, 1, + now.hour, now.minute, now.second), + now).AndReturn(TEST_RETURN) + + self.mox.ReplayAll() + + res = self.client.get(reverse('dash_overview')) + + self.assertTemplateUsed(res, 'dash_usage.html') + + self.assertEqual(res.context['usage'], TEST_RETURN) + + self.mox.VerifyAll() + + self.reset_times() + + def test_instance_console(self): + CONSOLE_OUTPUT = 'output' + INSTANCE_ID = self.servers[0].id + + console_mock = self.mox.CreateMock(api.Console) + console_mock.output = CONSOLE_OUTPUT + + self.mox.StubOutWithMock(api, 'console_create') + api.console_create(IsA(http.HttpRequest), + unicode(INSTANCE_ID)).AndReturn(console_mock) + + self.mox.ReplayAll() + + res = self.client.get(reverse('dash_instances_console', + args=[self.TEST_TENANT, INSTANCE_ID])) + + self.assertIsInstance(res, http.HttpResponse) + self.assertContains(res, CONSOLE_OUTPUT) + + self.mox.VerifyAll() + + def test_instance_console_exception(self): + INSTANCE_ID = self.servers[0].id + + exception = api_exceptions.ApiException('apiException', + message='apiException') + + self.mox.StubOutWithMock(api, 'console_create') + api.console_create(IsA(http.HttpRequest), + unicode(INSTANCE_ID)).AndRaise(exception) + + self.mox.StubOutWithMock(messages, 'error') + messages.error(IsA(http.HttpRequest), IsA(unicode)) + + self.mox.ReplayAll() + + res = self.client.get(reverse('dash_instances_console', + args=[self.TEST_TENANT, INSTANCE_ID])) + + self.assertRedirectsNoFollow(res, reverse('dash_instances', + args=[self.TEST_TENANT])) + + self.mox.VerifyAll() + + def test_instance_vnc(self): + INSTANCE_ID = self.servers[0].id + CONSOLE_OUTPUT = '/vncserver' + + console_mock = self.mox.CreateMock(api.Console) + console_mock.output = CONSOLE_OUTPUT + + self.mox.StubOutWithMock(api, 'console_create') + api.console_create(IsA(http.HttpRequest), + unicode(INSTANCE_ID), + 'vnc').AndReturn(console_mock) + + self.mox.ReplayAll() + + res = self.client.get(reverse('dash_instances_vnc', + args=[self.TEST_TENANT, INSTANCE_ID])) + + self.assertRedirectsNoFollow(res, CONSOLE_OUTPUT) + + self.mox.VerifyAll() + + def test_instance_vnc_exception(self): + INSTANCE_ID = self.servers[0].id + + exception = api_exceptions.ApiException('apiException', + message='apiException') + + self.mox.StubOutWithMock(api, 'console_create') + api.console_create(IsA(http.HttpRequest), + unicode(INSTANCE_ID), + 'vnc').AndRaise(exception) + + self.mox.ReplayAll() + + res = self.client.get(reverse('dash_instances_vnc', + args=[self.TEST_TENANT, INSTANCE_ID])) + + self.assertRedirectsNoFollow(res, reverse('dash_instances', + args=[self.TEST_TENANT])) + + self.mox.VerifyAll() diff --git a/django-openstack/django_openstack/utils.py b/django-openstack/django_openstack/utils.py index 53b0be677..65fa39d0c 100644 --- a/django-openstack/django_openstack/utils.py +++ b/django-openstack/django_openstack/utils.py @@ -20,6 +20,25 @@ import datetime + +def time(): + '''Overrideable version of datetime.datetime.today''' + if time.override_time: + return time.override_time + return datetime.time() + +time.override_time = None + + +def today(): + '''Overridable version of datetime.datetime.today''' + if today.override_time: + return today.override_time + return datetime.datetime.today() + +today.override_time = None + + def utcnow(): '''Overridable version of datetime.datetime.utcnow''' if utcnow.override_time: