Commit 96364f6a by Kálmán Viktor

Merge branch 'master' into issue-sliders

Conflicts:
	circle/common/models.py
parents 478df358 d6891337
...@@ -35,7 +35,11 @@ SOUTH_TESTS_MIGRATE = False ...@@ -35,7 +35,11 @@ SOUTH_TESTS_MIGRATE = False
INSTALLED_APPS += ( INSTALLED_APPS += (
'acl.tests', 'acl.tests',
'django_nose',
) )
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
NOSE_ARGS = ['--with-doctest']
PASSWORD_HASHERS = ['django.contrib.auth.hashers.MD5PasswordHasher']
CACHES = { CACHES = {
'default': { 'default': {
......
...@@ -47,17 +47,24 @@ class WorkerNotFound(Exception): ...@@ -47,17 +47,24 @@ class WorkerNotFound(Exception):
def activitycontextimpl(act, on_abort=None, on_commit=None): def activitycontextimpl(act, on_abort=None, on_commit=None):
try: try:
try:
yield act yield act
except HumanReadableException as e:
result = e
raise
except BaseException as e: except BaseException as e:
# BaseException is the common parent of Exception and # BaseException is the common parent of Exception and
# system-exiting exceptions, e.g. KeyboardInterrupt # system-exiting exceptions, e.g. KeyboardInterrupt
handler = None if on_abort is None else lambda a: on_abort(a, e) result = create_readable(
result = create_readable(ugettext_noop("Failure."), ugettext_noop("Failure."),
ugettext_noop("Unhandled exception: " ugettext_noop("Unhandled exception: %(error)s"),
"%(error)s"),
error=unicode(e)) error=unicode(e))
raise
except:
logger.exception("Failed activity %s" % unicode(act))
handler = None if on_abort is None else lambda a: on_abort(a, e)
act.finish(succeeded=False, result=result, event_handler=handler) act.finish(succeeded=False, result=result, event_handler=handler)
raise e raise
else: else:
act.finish(succeeded=True, event_handler=on_commit) act.finish(succeeded=True, event_handler=on_commit)
...@@ -71,11 +78,11 @@ activity_code_separator = '.' ...@@ -71,11 +78,11 @@ activity_code_separator = '.'
def has_prefix(activity_code, *prefixes): def has_prefix(activity_code, *prefixes):
"""Determine whether the activity code has the specified prefix. """Determine whether the activity code has the specified prefix.
E.g.: has_prefix('foo.bar.buz', 'foo.bar') == True >>> assert has_prefix('foo.bar.buz', 'foo.bar')
has_prefix('foo.bar.buz', 'foo', 'bar') == True >>> assert has_prefix('foo.bar.buz', 'foo', 'bar')
has_prefix('foo.bar.buz', 'foo.bar', 'buz') == True >>> assert has_prefix('foo.bar.buz', 'foo.bar', 'buz')
has_prefix('foo.bar.buz', 'foo', 'bar', 'buz') == True >>> assert has_prefix('foo.bar.buz', 'foo', 'bar', 'buz')
has_prefix('foo.bar.buz', 'foo', 'buz') == False >>> assert not has_prefix('foo.bar.buz', 'foo', 'buz')
""" """
equal = lambda a, b: a == b equal = lambda a, b: a == b
act_code_parts = split_activity_code(activity_code) act_code_parts = split_activity_code(activity_code)
...@@ -86,11 +93,11 @@ def has_prefix(activity_code, *prefixes): ...@@ -86,11 +93,11 @@ def has_prefix(activity_code, *prefixes):
def has_suffix(activity_code, *suffixes): def has_suffix(activity_code, *suffixes):
"""Determine whether the activity code has the specified suffix. """Determine whether the activity code has the specified suffix.
E.g.: has_suffix('foo.bar.buz', 'bar.buz') == True >>> assert has_suffix('foo.bar.buz', 'bar.buz')
has_suffix('foo.bar.buz', 'bar', 'buz') == True >>> assert has_suffix('foo.bar.buz', 'bar', 'buz')
has_suffix('foo.bar.buz', 'foo.bar', 'buz') == True >>> assert has_suffix('foo.bar.buz', 'foo.bar', 'buz')
has_suffix('foo.bar.buz', 'foo', 'bar', 'buz') == True >>> assert has_suffix('foo.bar.buz', 'foo', 'bar', 'buz')
has_suffix('foo.bar.buz', 'foo', 'buz') == False >>> assert not has_suffix('foo.bar.buz', 'foo', 'buz')
""" """
equal = lambda a, b: a == b equal = lambda a, b: a == b
act_code_parts = split_activity_code(activity_code) act_code_parts = split_activity_code(activity_code)
...@@ -197,6 +204,10 @@ class ActivityModel(TimeStampedModel): ...@@ -197,6 +204,10 @@ class ActivityModel(TimeStampedModel):
DeprecationWarning, stacklevel=2) DeprecationWarning, stacklevel=2)
value = create_readable(user_text_template="", value = create_readable(user_text_template="",
admin_text_template=value) admin_text_template=value)
elif not hasattr(value, "to_dict"):
warn("Use HumanReadableObject.", DeprecationWarning, stacklevel=2)
value = create_readable(user_text_template="",
admin_text_template=unicode(value))
self.result_data = None if value is None else value.to_dict() self.result_data = None if value is None else value.to_dict()
...@@ -362,8 +373,9 @@ class HumanReadableObject(object): ...@@ -362,8 +373,9 @@ class HumanReadableObject(object):
@classmethod @classmethod
def create(cls, user_text_template, admin_text_template=None, **params): def create(cls, user_text_template, admin_text_template=None, **params):
return cls(user_text_template, return cls(user_text_template=user_text_template,
admin_text_template or user_text_template, params) admin_text_template=(admin_text_template
or user_text_template), params=params)
def set(self, user_text_template, admin_text_template=None, **params): def set(self, user_text_template, admin_text_template=None, **params):
self._set_values(user_text_template, self._set_values(user_text_template,
...@@ -408,14 +420,26 @@ create_readable = HumanReadableObject.create ...@@ -408,14 +420,26 @@ create_readable = HumanReadableObject.create
class HumanReadableException(HumanReadableObject, Exception): class HumanReadableException(HumanReadableObject, Exception):
"""HumanReadableObject that is an Exception so can used in except clause. """HumanReadableObject that is an Exception so can used in except clause.
""" """
level = "error"
def send_message(self, request): def __init__(self, level=None, *args, **kwargs):
super(HumanReadableException, self).__init__(*args, **kwargs)
if level is not None:
if hasattr(messages, level):
self.level = level
else:
raise ValueError(
"Level should be the name of an attribute of django."
"contrib.messages (and it should be callable with "
"(request, message)). Like 'error', 'warning'.")
else:
self.level = "error"
def send_message(self, request, level=None):
if request.user and request.user.is_superuser: if request.user and request.user.is_superuser:
msg = self.get_admin_text() msg = self.get_admin_text()
else: else:
msg = self.get_user_text() msg = self.get_user_text()
getattr(messages, self.level)(request, msg) getattr(messages, level or self.level)(request, msg)
def humanize_exception(message, exception=None, level=None, **params): def humanize_exception(message, exception=None, level=None, **params):
...@@ -428,10 +452,10 @@ def humanize_exception(message, exception=None, level=None, **params): ...@@ -428,10 +452,10 @@ def humanize_exception(message, exception=None, level=None, **params):
Welcome! Welcome!
""" """
if level is not None:
exception.level = level
Ex = type("HumanReadable" + type(exception).__name__, Ex = type("HumanReadable" + type(exception).__name__,
(HumanReadableException, type(exception)), (HumanReadableException, type(exception)),
exception.__dict__) exception.__dict__)
return Ex.create(message, **params) ex = Ex.create(message, **params)
if level:
ex.level = level
return ex
...@@ -1322,7 +1322,7 @@ ...@@ -1322,7 +1322,7 @@
"user_permissions": [ "user_permissions": [
115 115
], ],
"password": "pbkdf2_sha256$10000$KIoeMs78MiOj$PnVXn3YJMehbOciBO32CMzqL0ZnQrzrdb7+b5dE13os=", "password": "md5$qLN4mQMOrsUJ$f07129fd1a289a0afb4e09f7a6816a4f",
"email": "test@example.org", "email": "test@example.org",
"date_joined": "2013-09-04T15:29:49.914Z" "date_joined": "2013-09-04T15:29:49.914Z"
} }
......
...@@ -529,24 +529,25 @@ class VmDetailTest(LoginMixin, TestCase): ...@@ -529,24 +529,25 @@ class VmDetailTest(LoginMixin, TestCase):
def test_permitted_wake_up_wrong_state(self): def test_permitted_wake_up_wrong_state(self):
c = Client() c = Client()
self.login(c, "user2") self.login(c, "user2")
with patch.object(WakeUpOperation, 'async') as mock_method: with patch.object(WakeUpOperation, 'async') as mock_method, \
patch.object(Instance.WrongStateError, 'send_message') as wro:
inst = Instance.objects.get(pk=1) inst = Instance.objects.get(pk=1)
mock_method.side_effect = inst.wake_up mock_method.side_effect = inst.wake_up
inst.status = 'RUNNING' inst.status = 'RUNNING'
inst.set_level(self.u2, 'owner') inst.set_level(self.u2, 'owner')
with patch('dashboard.views.messages') as msg:
c.post("/dashboard/vm/1/op/wake_up/") c.post("/dashboard/vm/1/op/wake_up/")
assert msg.error.called
inst = Instance.objects.get(pk=1) inst = Instance.objects.get(pk=1)
self.assertEqual(inst.status, 'RUNNING') # mocked anyway self.assertEqual(inst.status, 'RUNNING') # mocked anyway
assert mock_method.called assert mock_method.called
assert wro.called
def test_permitted_wake_up(self): def test_permitted_wake_up(self):
c = Client() c = Client()
self.login(c, "user2") self.login(c, "user2")
with patch.object(Instance, 'select_node', return_value=None): with patch.object(Instance, 'select_node', return_value=None), \
with patch.object(WakeUpOperation, 'async') as new_wake_up: patch.object(WakeUpOperation, 'async') as new_wake_up, \
with patch('vm.tasks.vm_tasks.wake_up.apply_async') as wuaa: patch('vm.tasks.vm_tasks.wake_up.apply_async') as wuaa, \
patch.object(Instance.WrongStateError, 'send_message') as wro:
inst = Instance.objects.get(pk=1) inst = Instance.objects.get(pk=1)
new_wake_up.side_effect = inst.wake_up new_wake_up.side_effect = inst.wake_up
inst.get_remote_queue_name = Mock(return_value='test') inst.get_remote_queue_name = Mock(return_value='test')
...@@ -559,6 +560,7 @@ class VmDetailTest(LoginMixin, TestCase): ...@@ -559,6 +560,7 @@ class VmDetailTest(LoginMixin, TestCase):
self.assertEqual(inst.status, 'RUNNING') self.assertEqual(inst.status, 'RUNNING')
assert new_wake_up.called assert new_wake_up.called
assert wuaa.called assert wuaa.called
assert not wro.called
def test_unpermitted_wake_up(self): def test_unpermitted_wake_up(self):
c = Client() c = Client()
......
...@@ -71,7 +71,7 @@ from .tables import ( ...@@ -71,7 +71,7 @@ from .tables import (
NodeListTable, NodeVmListTable, TemplateListTable, LeaseListTable, NodeListTable, NodeVmListTable, TemplateListTable, LeaseListTable,
GroupListTable, UserKeyListTable GroupListTable, UserKeyListTable
) )
from common.models import HumanReadableObject from common.models import HumanReadableObject, HumanReadableException
from vm.models import ( from vm.models import (
Instance, instance_activity, InstanceActivity, InstanceTemplate, Interface, Instance, instance_activity, InstanceActivity, InstanceTemplate, Interface,
InterfaceTemplate, Lease, Node, NodeActivity, Trait, InterfaceTemplate, Lease, Node, NodeActivity, Trait,
...@@ -562,9 +562,13 @@ class OperationView(RedirectToLoginMixin, DetailView): ...@@ -562,9 +562,13 @@ class OperationView(RedirectToLoginMixin, DetailView):
done = False done = False
try: try:
task = self.get_op().async(user=request.user, **extra) task = self.get_op().async(user=request.user, **extra)
except HumanReadableException as e:
e.send_message(request)
logger.exception("Could not start operation")
result = e
except Exception as e: except Exception as e:
messages.error(request, _('Could not start operation.')) messages.error(request, _('Could not start operation.'))
logger.exception(e) logger.exception("Could not start operation")
result = e result = e
else: else:
wait = self.wait_for_result wait = self.wait_for_result
...@@ -575,6 +579,10 @@ class OperationView(RedirectToLoginMixin, DetailView): ...@@ -575,6 +579,10 @@ class OperationView(RedirectToLoginMixin, DetailView):
except TimeoutError: except TimeoutError:
logger.debug("Result didn't arrive in %ss", logger.debug("Result didn't arrive in %ss",
self.wait_for_result, exc_info=True) self.wait_for_result, exc_info=True)
except HumanReadableException as e:
e.send_message(request)
logger.exception(e)
result = e
except Exception as e: except Exception as e:
messages.error(request, _('Operation failed.')) messages.error(request, _('Operation failed.'))
logger.debug("Operation failed.", exc_info=True) logger.debug("Operation failed.", exc_info=True)
......
#!/bin/echo Usage: fab --list -f
import contextlib import contextlib
import datetime import datetime
...@@ -9,14 +10,14 @@ from fabric.decorators import roles, parallel ...@@ -9,14 +10,14 @@ from fabric.decorators import roles, parallel
env.roledefs['portal'] = ['localhost'] env.roledefs['portal'] = ['localhost']
try: try:
from vm.models import Node from vm.models import Node as _Node
from storage.models import DataStore from storage.models import DataStore as _DataStore
except Exception as e: except Exception as e:
print e print e
else: else:
env.roledefs['node'] = [unicode(n.host.ipv4) env.roledefs['node'] = [unicode(n.host.ipv4)
for n in Node.objects.filter(enabled=True)] for n in _Node.objects.filter(enabled=True)]
env.roledefs['storage'] = [DataStore.objects.get().hostname] env.roledefs['storage'] = [_DataStore.objects.get().hostname]
def update_all(): def update_all():
......
[General]
LangCode=hu
MailingList=cloud@ik.bme.hu
PotBaseDir=./
ProjectID=circle-hu
TargetLangCode=hu
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -6,7 +6,7 @@ msgid "" ...@@ -6,7 +6,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-05-07 14:25+0200\n" "POT-Creation-Date: 2014-07-29 12:56+0200\n"
"PO-Revision-Date: 2014-05-07 15:32+0200\n" "PO-Revision-Date: 2014-05-07 15:32+0200\n"
"Last-Translator: Mate Ory <orymate@ik.bme.hu>\n" "Last-Translator: Mate Ory <orymate@ik.bme.hu>\n"
"Language-Team: Hungarian <cloud@ik.bme.hu>\n" "Language-Team: Hungarian <cloud@ik.bme.hu>\n"
...@@ -17,105 +17,144 @@ msgstr "" ...@@ -17,105 +17,144 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 1.5\n" "X-Generator: Lokalize 1.5\n"
#: dashboard/static/dashboard/dashboard.js:54 #: dashboard/static/dashboard/dashboard.js:68
#: static_collected/dashboard/dashboard.js:68
msgid "Select an option to proceed!" msgid "Select an option to proceed!"
msgstr "Válasszon a folytatáshoz." msgstr "Válasszon a folytatáshoz."
#: dashboard/static/dashboard/dashboard.js:257
#: dashboard/static/dashboard/dashboard.js:304
#: dashboard/static/dashboard/dashboard.js:314
#: static_collected/dashboard/dashboard.js:257
#: static_collected/dashboard/dashboard.js:304
#: static_collected/dashboard/dashboard.js:314
msgid "No result"
msgstr ""
#: dashboard/static/dashboard/profile.js:18
#: static_collected/dashboard/profile.js:18
msgid "You have no permission to change this profile."
msgstr ""
#: dashboard/static/dashboard/profile.js:20
#: static_collected/dashboard/profile.js:20
msgid "Unknown error."
msgstr ""
#: dashboard/static/dashboard/vm-tour.js:20 #: dashboard/static/dashboard/vm-tour.js:20
#: static_collected/dashboard/vm-tour.js:20
msgid "Prev" msgid "Prev"
msgstr "Vissza" msgstr "Vissza"
#: dashboard/static/dashboard/vm-tour.js:22 #: dashboard/static/dashboard/vm-tour.js:22
#: static_collected/dashboard/vm-tour.js:22
msgid "Next" msgid "Next"
msgstr "Tovább" msgstr "Tovább"
#: dashboard/static/dashboard/vm-tour.js:26 #: dashboard/static/dashboard/vm-tour.js:26
#: static_collected/dashboard/vm-tour.js:26
msgid "End tour" msgid "End tour"
msgstr "Befejezés" msgstr "Befejezés"
#: dashboard/static/dashboard/vm-tour.js:33 #: dashboard/static/dashboard/vm-tour.js:33
#: static_collected/dashboard/vm-tour.js:33
msgid "Template Tutorial Tour" msgid "Template Tutorial Tour"
msgstr "Sablon-kalauz" msgstr "Sablon-kalauz"
#: dashboard/static/dashboard/vm-tour.js:34 #: dashboard/static/dashboard/vm-tour.js:34
#: static_collected/dashboard/vm-tour.js:34
msgid "" msgid ""
"Welcome to the template tutorial. In this quick tour, we gonna show you how " "Welcome to the template tutorial. In this quick tour, we gonna show you how "
"to do the steps described above." "to do the steps described above."
msgstr "" msgstr ""
"Üdvözöli a sablon-kalauz. A túra során bemutatjuk, hogyan végezze el " "Üdvözöli a sablon-kalauz. A túra során bemutatjuk, hogyan végezze el a fenti "
"a fenti lépéseket." "lépéseket."
#: dashboard/static/dashboard/vm-tour.js:35 #: dashboard/static/dashboard/vm-tour.js:35
#: static_collected/dashboard/vm-tour.js:35
msgid "" msgid ""
"For the next tour step press the \"Next\" button or the right arrow (or " "For the next tour step press the \"Next\" button or the right arrow (or "
"\"Back\" button/left arrow for the previous step)." "\"Back\" button/left arrow for the previous step)."
msgstr "" msgstr ""
"A következő lépéshez kattintson a \"Tovább\" gombra vagy használja " "A következő lépéshez kattintson a \"Tovább\" gombra vagy használja a "
"a nyílbillentyűket." "nyílbillentyűket."
#: dashboard/static/dashboard/vm-tour.js:36 #: dashboard/static/dashboard/vm-tour.js:36
#: static_collected/dashboard/vm-tour.js:36
msgid "" msgid ""
"During the tour please don't try the functions because it may lead to " "During the tour please don't try the functions because it may lead to "
"graphical glitches, however " "graphical glitches, however "
msgstr "A túra során még ne próbálja ki a bemutatott funkciókat." msgstr "A túra során még ne próbálja ki a bemutatott funkciókat."
#: dashboard/static/dashboard/vm-tour.js:45 #: dashboard/static/dashboard/vm-tour.js:45
#: static_collected/dashboard/vm-tour.js:45
msgid "Home tab" msgid "Home tab"
msgstr "Kezdőoldal" msgstr "Kezdőoldal"
#: dashboard/static/dashboard/vm-tour.js:46 #: dashboard/static/dashboard/vm-tour.js:46
#: static_collected/dashboard/vm-tour.js:46
msgid "" msgid ""
"In this tab you can tag your virtual machine and modify the name and " "In this tab you can tag your virtual machine and modify the name and "
"description." "description."
msgstr "" msgstr ""
"Ezen a lapon címkéket adhat a virtuális géphez, vagy módosíthatja " "Ezen a lapon címkéket adhat a virtuális géphez, vagy módosíthatja a nevét, "
"a nevét, leírását." "leírását."
#: dashboard/static/dashboard/vm-tour.js:55 #: dashboard/static/dashboard/vm-tour.js:55
#: static_collected/dashboard/vm-tour.js:55
msgid "Resources tab" msgid "Resources tab"
msgstr "Erőforrások lap" msgstr "Erőforrások lap"
#: dashboard/static/dashboard/vm-tour.js:58 #: dashboard/static/dashboard/vm-tour.js:58
#: static_collected/dashboard/vm-tour.js:58
msgid "" msgid ""
"On the resources tab you can edit the CPU/RAM options and add/remove disks!" "On the resources tab you can edit the CPU/RAM options and add/remove disks!"
msgstr "" msgstr ""
"Az erőforrások lapon szerkesztheti a CPU/memória-beállításokat, valamint " "Az erőforrások lapon szerkesztheti a CPU/memória-beállításokat, valamint "
"hozzáadhat " "hozzáadhat és törölhet lemezeket."
"és törölhet lemezeket."
#: dashboard/static/dashboard/vm-tour.js:68 #: dashboard/static/dashboard/vm-tour.js:68
#: static_collected/dashboard/vm-tour.js:68
msgid "Resources" msgid "Resources"
msgstr "Erőforrások" msgstr "Erőforrások"
#: dashboard/static/dashboard/vm-tour.js:69 #: dashboard/static/dashboard/vm-tour.js:69
#: static_collected/dashboard/vm-tour.js:69
msgid "CPU priority" msgid "CPU priority"
msgstr "CPU prioritás" msgstr "CPU prioritás"
#: dashboard/static/dashboard/vm-tour.js:69 #: dashboard/static/dashboard/vm-tour.js:69
#: static_collected/dashboard/vm-tour.js:69
msgid "higher is better" msgid "higher is better"
msgstr "a nagyobb érték a jobb" msgstr "a nagyobb érték a jobb"
#: dashboard/static/dashboard/vm-tour.js:70 #: dashboard/static/dashboard/vm-tour.js:70
#: static_collected/dashboard/vm-tour.js:70
msgid "CPU count" msgid "CPU count"
msgstr "CPU-k száma" msgstr "CPU-k száma"
#: dashboard/static/dashboard/vm-tour.js:70 #: dashboard/static/dashboard/vm-tour.js:70
#: static_collected/dashboard/vm-tour.js:70
msgid "number of CPU cores." msgid "number of CPU cores."
msgstr "A CPU-magok száma." msgstr "A CPU-magok száma."
#: dashboard/static/dashboard/vm-tour.js:71 #: dashboard/static/dashboard/vm-tour.js:71
#: static_collected/dashboard/vm-tour.js:71
msgid "RAM amount" msgid "RAM amount"
msgstr "RAM mennyiség" msgstr "RAM mennyiség"
#: dashboard/static/dashboard/vm-tour.js:71 #: dashboard/static/dashboard/vm-tour.js:71
#: static_collected/dashboard/vm-tour.js:71
msgid "amount of RAM." msgid "amount of RAM."
msgstr "a memória mennyisége." msgstr "a memória mennyisége."
#: dashboard/static/dashboard/vm-tour.js:81 #: dashboard/static/dashboard/vm-tour.js:81
#: static_collected/dashboard/vm-tour.js:81
msgid "Disks" msgid "Disks"
msgstr "Lemezek" msgstr "Lemezek"
#: dashboard/static/dashboard/vm-tour.js:82 #: dashboard/static/dashboard/vm-tour.js:82
#: static_collected/dashboard/vm-tour.js:82
msgid "" msgid ""
"You can add empty disks, download new ones and remove existing ones here." "You can add empty disks, download new ones and remove existing ones here."
msgstr "" msgstr ""
...@@ -123,65 +162,75 @@ msgstr "" ...@@ -123,65 +162,75 @@ msgstr ""
"meglévőket." "meglévőket."
#: dashboard/static/dashboard/vm-tour.js:92 #: dashboard/static/dashboard/vm-tour.js:92
#: static_collected/dashboard/vm-tour.js:92
msgid "Network tab" msgid "Network tab"
msgstr "Hálózat lap" msgstr "Hálózat lap"
#: dashboard/static/dashboard/vm-tour.js:93 #: dashboard/static/dashboard/vm-tour.js:93
#: static_collected/dashboard/vm-tour.js:93
msgid "You can add new network interfaces or remove existing ones here." msgid "You can add new network interfaces or remove existing ones here."
msgstr "Hozzáadhat új hálózati interfészeket, vagy törölheti a meglévőket." msgstr "Hozzáadhat új hálózati interfészeket, vagy törölheti a meglévőket."
#: dashboard/static/dashboard/vm-tour.js:102 #: dashboard/static/dashboard/vm-tour.js:102
#: static_collected/dashboard/vm-tour.js:102
msgid "Deploy" msgid "Deploy"
msgstr "Indítás" msgstr "Indítás"
#: dashboard/static/dashboard/vm-tour.js:105 #: dashboard/static/dashboard/vm-tour.js:105
#: static_collected/dashboard/vm-tour.js:105
msgid "Deploy the virtual machine." msgid "Deploy the virtual machine."
msgstr "A virtuális gép elindítása." msgstr "A virtuális gép elindítása."
#: dashboard/static/dashboard/vm-tour.js:110 #: dashboard/static/dashboard/vm-tour.js:110
#: static_collected/dashboard/vm-tour.js:110
msgid "Connect" msgid "Connect"
msgstr "Csatlakozás" msgstr "Csatlakozás"
#: dashboard/static/dashboard/vm-tour.js:113 #: dashboard/static/dashboard/vm-tour.js:113
#: static_collected/dashboard/vm-tour.js:113
msgid "Use the connection string or connect with your choice of client!" msgid "Use the connection string or connect with your choice of client!"
msgstr "Használja a megadott parancsot, vagy kedvenc kliensét." msgstr "Használja a megadott parancsot, vagy kedvenc kliensét."
#: dashboard/static/dashboard/vm-tour.js:120 #: dashboard/static/dashboard/vm-tour.js:120
#: static_collected/dashboard/vm-tour.js:120
msgid "Customize the virtual machine" msgid "Customize the virtual machine"
msgstr "Szabja testre a gépet" msgstr "Szabja testre a gépet"
#: dashboard/static/dashboard/vm-tour.js:121 #: dashboard/static/dashboard/vm-tour.js:121
#: static_collected/dashboard/vm-tour.js:121
msgid "" msgid ""
"After you have connected to the virtual machine do your modifications then " "After you have connected to the virtual machine do your modifications then "
"log off." "log off."
msgstr "" msgstr ""
"Miután csatlakozott, végezze el a szükséges módosításokat, majd " "Miután csatlakozott, végezze el a szükséges módosításokat, majd jelentkezzen "
"jelentkezzen ki." "ki."
#: dashboard/static/dashboard/vm-tour.js:126 #: dashboard/static/dashboard/vm-tour.js:126
#: static_collected/dashboard/vm-tour.js:126
msgid "Save as" msgid "Save as"
msgstr "Mentés sablonként" msgstr "Mentés sablonként"
#: dashboard/static/dashboard/vm-tour.js:129 #: dashboard/static/dashboard/vm-tour.js:129
#: static_collected/dashboard/vm-tour.js:129
msgid "" msgid ""
"Press the \"Save as template\" button and wait until the activity finishes." "Press the \"Save as template\" button and wait until the activity finishes."
msgstr "" msgstr ""
"Kattintson a „mentés sablonként” gombra, majd várjon, amíg a lemez " "Kattintson a „mentés sablonként” gombra, majd várjon, amíg a lemez mentése "
"mentése elkészül." "elkészül."
#: dashboard/static/dashboard/vm-tour.js:135 #: dashboard/static/dashboard/vm-tour.js:135
#: static_collected/dashboard/vm-tour.js:135
msgid "Finish" msgid "Finish"
msgstr "Befejezés" msgstr "Befejezés"
#: dashboard/static/dashboard/vm-tour.js:138 #: dashboard/static/dashboard/vm-tour.js:138
#: static_collected/dashboard/vm-tour.js:138
msgid "" msgid ""
"This is the last message, if something is not clear you can do the the tour " "This is the last message, if something is not clear you can do the the tour "
"again!" "again!"
msgstr "" msgstr "A túra véget ért. Ha valami nem érthető, újrakezdheti az útmutatót."
"A túra véget ért. Ha valami nem érthető, újrakezdheti az "
"útmutatót."
#: network/static/js/host.js:10 #: network/static/js/host.js:10 static_collected/js/host.js:10
msgid "" msgid ""
"Are you sure you want to remove host group <strong>\"%(group)s\"</strong> " "Are you sure you want to remove host group <strong>\"%(group)s\"</strong> "
"from <strong>\"%(host)s\"</strong>?" "from <strong>\"%(host)s\"</strong>?"
...@@ -189,19 +238,172 @@ msgstr "" ...@@ -189,19 +238,172 @@ msgstr ""
"Biztosan törli a(z)<strong>„%(host)s”</strong> gépet a(z) " "Biztosan törli a(z)<strong>„%(host)s”</strong> gépet a(z) "
"<strong>„%(group)s”</strong> gépcsoportból?" "<strong>„%(group)s”</strong> gépcsoportból?"
#: network/static/js/host.js:13 #: network/static/js/host.js:13 static_collected/js/host.js:13
msgid "Are you sure you want to delete this rule?" msgid "Are you sure you want to delete this rule?"
msgstr "Biztosan törli ezt a szabályt?" msgstr "Biztosan törli ezt a szabályt?"
#: network/static/js/host.js:20 network/static/js/switch-port.js:14 #: network/static/js/host.js:20 network/static/js/switch-port.js:14
#: static_collected/admin/js/admin/DateTimeShortcuts.js:95
#: static_collected/admin/js/admin/DateTimeShortcuts.js:208
#: static_collected/js/host.js:20 static_collected/js/switch-port.js:14
msgid "Cancel" msgid "Cancel"
msgstr "Mégsem" msgstr "Mégsem"
#: network/static/js/host.js:25 network/static/js/switch-port.js:19 #: network/static/js/host.js:25 network/static/js/switch-port.js:19
#: static_collected/admin/js/SelectFilter2.js:69
#: static_collected/js/host.js:25 static_collected/js/switch-port.js:19
msgid "Remove" msgid "Remove"
msgstr "Eltávolítás" msgstr "Eltávolítás"
#: network/static/js/switch-port.js:8 #: network/static/js/switch-port.js:8 static_collected/js/switch-port.js:8
msgid "Are you sure you want to delete this device?" msgid "Are you sure you want to delete this device?"
msgstr "Biztosan törli ezt az eszközt?" msgstr "Biztosan törli ezt az eszközt?"
#: static_collected/admin/js/SelectFilter2.js:45
#, c-format
msgid "Available %s"
msgstr ""
#: static_collected/admin/js/SelectFilter2.js:46
#, c-format
msgid ""
"This is the list of available %s. You may choose some by selecting them in "
"the box below and then clicking the \"Choose\" arrow between the two boxes."
msgstr ""
#: static_collected/admin/js/SelectFilter2.js:53
#, c-format
msgid "Type into this box to filter down the list of available %s."
msgstr ""
#: static_collected/admin/js/SelectFilter2.js:57
msgid "Filter"
msgstr ""
#: static_collected/admin/js/SelectFilter2.js:61
msgid "Choose all"
msgstr ""
#: static_collected/admin/js/SelectFilter2.js:61
#, c-format
msgid "Click to choose all %s at once."
msgstr ""
#: static_collected/admin/js/SelectFilter2.js:67
msgid "Choose"
msgstr ""
#: static_collected/admin/js/SelectFilter2.js:75
#, c-format
msgid "Chosen %s"
msgstr ""
#: static_collected/admin/js/SelectFilter2.js:76
#, c-format
msgid ""
"This is the list of chosen %s. You may remove some by selecting them in the "
"box below and then clicking the \"Remove\" arrow between the two boxes."
msgstr ""
#: static_collected/admin/js/SelectFilter2.js:80
#, fuzzy
msgid "Remove all"
msgstr "Eltávolítás"
#: static_collected/admin/js/SelectFilter2.js:80
#, c-format
msgid "Click to remove all chosen %s at once."
msgstr ""
#: static_collected/admin/js/actions.js:18
#: static_collected/admin/js/actions.min.js:1
msgid "%(sel)s of %(cnt)s selected"
msgid_plural "%(sel)s of %(cnt)s selected"
msgstr[0] ""
msgstr[1] ""
#: static_collected/admin/js/actions.js:109
#: static_collected/admin/js/actions.min.js:5
msgid ""
"You have unsaved changes on individual editable fields. If you run an "
"action, your unsaved changes will be lost."
msgstr ""
#: static_collected/admin/js/actions.js:121
#: static_collected/admin/js/actions.min.js:5
msgid ""
"You have selected an action, but you haven't saved your changes to "
"individual fields yet. Please click OK to save. You'll need to re-run the "
"action."
msgstr ""
#: static_collected/admin/js/actions.js:123
#: static_collected/admin/js/actions.min.js:6
msgid ""
"You have selected an action, and you haven't made any changes on individual "
"fields. You're probably looking for the Go button rather than the Save "
"button."
msgstr ""
#: static_collected/admin/js/calendar.js:8
msgid ""
"January February March April May June July August September October November "
"December"
msgstr ""
#: static_collected/admin/js/calendar.js:9
msgid "S M T W T F S"
msgstr ""
#: static_collected/admin/js/collapse.js:8
#: static_collected/admin/js/collapse.js:19
#: static_collected/admin/js/collapse.min.js:1
msgid "Show"
msgstr ""
#: static_collected/admin/js/collapse.js:16
#: static_collected/admin/js/collapse.min.js:1
msgid "Hide"
msgstr ""
#: static_collected/admin/js/admin/DateTimeShortcuts.js:52
#: static_collected/admin/js/admin/DateTimeShortcuts.js:88
msgid "Now"
msgstr ""
#: static_collected/admin/js/admin/DateTimeShortcuts.js:56
msgid "Clock"
msgstr ""
#: static_collected/admin/js/admin/DateTimeShortcuts.js:84
msgid "Choose a time"
msgstr ""
#: static_collected/admin/js/admin/DateTimeShortcuts.js:89
msgid "Midnight"
msgstr ""
#: static_collected/admin/js/admin/DateTimeShortcuts.js:90
msgid "6 a.m."
msgstr ""
#: static_collected/admin/js/admin/DateTimeShortcuts.js:91
msgid "Noon"
msgstr ""
#: static_collected/admin/js/admin/DateTimeShortcuts.js:148
#: static_collected/admin/js/admin/DateTimeShortcuts.js:201
msgid "Today"
msgstr ""
#: static_collected/admin/js/admin/DateTimeShortcuts.js:152
msgid "Calendar"
msgstr ""
#: static_collected/admin/js/admin/DateTimeShortcuts.js:199
msgid "Yesterday"
msgstr ""
#: static_collected/admin/js/admin/DateTimeShortcuts.js:203
msgid "Tomorrow"
msgstr ""
<!--
Collection name attribute represents the name of the menu, e.g., to use menu "File" use "file" or "Help" use "help". You can add new menus.
If you type a relative script file beware the this script is located in $KDEHOME/share/apps/applicationname/
The following example adds an action with the text "Export..." into the "File" menu
<KrossScripting>
<collection name="file" text="File" comment="File menu">
<script name="export" text="Export..." comment="Export content" file="export.py" />
</collection>
</KrossScripting>
-->
...@@ -41,7 +41,7 @@ from model_utils.models import TimeStampedModel, StatusModel ...@@ -41,7 +41,7 @@ from model_utils.models import TimeStampedModel, StatusModel
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
from acl.models import AclBase from acl.models import AclBase
from common.models import create_readable from common.models import create_readable, HumanReadableException
from common.operations import OperatedMixin from common.operations import OperatedMixin
from ..tasks import vm_tasks, agent_tasks from ..tasks import vm_tasks, agent_tasks
from .activity import (ActivityInProgressError, instance_activity, from .activity import (ActivityInProgressError, instance_activity,
...@@ -276,28 +276,26 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, ...@@ -276,28 +276,26 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
verbose_name = _('instance') verbose_name = _('instance')
verbose_name_plural = _('instances') verbose_name_plural = _('instances')
class InstanceDestroyedError(Exception): class InstanceError(HumanReadableException):
def __init__(self, instance, message=None): def __init__(self, instance, params=None, level=None, **kwargs):
if message is None: kwargs.update(params or {})
message = ("The instance (%s) has already been destroyed." self.instance = kwargs["instance"] = instance
% instance) super(Instance.InstanceError, self).__init__(
level, self.message, self.message, kwargs)
Exception.__init__(self, message) class InstanceDestroyedError(InstanceError):
message = ugettext_noop(
"Instance %(instance)s has already been destroyed.")
self.instance = instance class WrongStateError(InstanceError):
message = ugettext_noop(
"Current state (%(state)s) of instance %(instance)s is "
"inappropriate for the invoked operation.")
class WrongStateError(Exception): def __init__(self, instance, params=None, **kwargs):
super(Instance.WrongStateError, self).__init__(
def __init__(self, instance, message=None): instance, params, state=instance.status)
if message is None:
message = ("The instance's current state (%s) is "
"inappropriate for the invoked operation."
% instance.status)
Exception.__init__(self, message)
self.instance = instance
def __unicode__(self): def __unicode__(self):
parts = (self.name, "(" + str(self.id) + ")") parts = (self.name, "(" + str(self.id) + ")")
......
...@@ -26,7 +26,7 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop ...@@ -26,7 +26,7 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop
from celery.exceptions import TimeLimitExceeded from celery.exceptions import TimeLimitExceeded
from common.models import create_readable from common.models import create_readable, humanize_exception
from common.operations import Operation, register_operation from common.operations import Operation, register_operation
from .tasks.local_tasks import ( from .tasks.local_tasks import (
abortable_async_instance_operation, abortable_async_node_operation, abortable_async_instance_operation, abortable_async_node_operation,
...@@ -45,6 +45,8 @@ class InstanceOperation(Operation): ...@@ -45,6 +45,8 @@ class InstanceOperation(Operation):
async_operation = abortable_async_instance_operation async_operation = abortable_async_instance_operation
host_cls = Instance host_cls = Instance
concurrency_check = True concurrency_check = True
accept_states = None
deny_states = None
def __init__(self, instance): def __init__(self, instance):
super(InstanceOperation, self).__init__(subject=instance) super(InstanceOperation, self).__init__(subject=instance)
...@@ -53,11 +55,26 @@ class InstanceOperation(Operation): ...@@ -53,11 +55,26 @@ class InstanceOperation(Operation):
def check_precond(self): def check_precond(self):
if self.instance.destroyed_at: if self.instance.destroyed_at:
raise self.instance.InstanceDestroyedError(self.instance) raise self.instance.InstanceDestroyedError(self.instance)
if self.accept_states:
if self.instance.status not in self.accept_states:
logger.debug("precond failed for %s: %s not in %s",
unicode(self.__class__),
unicode(self.instance.status),
unicode(self.accept_states))
raise self.instance.WrongStateError(self.instance)
if self.deny_states:
if self.instance.status in self.deny_states:
logger.debug("precond failed for %s: %s in %s",
unicode(self.__class__),
unicode(self.instance.status),
unicode(self.accept_states))
raise self.instance.WrongStateError(self.instance)
def check_auth(self, user): def check_auth(self, user):
if not self.instance.has_level(user, self.acl_level): if not self.instance.has_level(user, self.acl_level):
raise PermissionDenied("%s doesn't have the required ACL level." % raise humanize_exception(ugettext_noop(
user) "%(acl_level)s level is required for this operation."),
PermissionDenied(), acl_level=self.acl_level)
super(InstanceOperation, self).check_auth(user=user) super(InstanceOperation, self).check_auth(user=user)
...@@ -94,6 +111,7 @@ class AddInterfaceOperation(InstanceOperation): ...@@ -94,6 +111,7 @@ class AddInterfaceOperation(InstanceOperation):
description = _("Add a new network interface for the specified VLAN to " description = _("Add a new network interface for the specified VLAN to "
"the VM.") "the VM.")
required_perms = () required_perms = ()
accept_states = ('STOPPED', 'PENDING', 'RUNNING')
def rollback(self, net, activity): def rollback(self, net, activity):
with activity.sub_activity( with activity.sub_activity(
...@@ -102,14 +120,11 @@ class AddInterfaceOperation(InstanceOperation): ...@@ -102,14 +120,11 @@ class AddInterfaceOperation(InstanceOperation):
net.destroy() net.destroy()
net.delete() net.delete()
def check_precond(self):
super(AddInterfaceOperation, self).check_precond()
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, activity, user, system, vlan, managed=None): def _operation(self, activity, user, system, vlan, managed=None):
if not vlan.has_level(user, 'user'): if not vlan.has_level(user, 'user'):
raise PermissionDenied() raise humanize_exception(ugettext_noop(
"User acces to vlan %(vlan)s is required."),
PermissionDenied(), vlan=vlan)
if managed is None: if managed is None:
managed = vlan.managed managed = vlan.managed
...@@ -141,11 +156,7 @@ class CreateDiskOperation(InstanceOperation): ...@@ -141,11 +156,7 @@ class CreateDiskOperation(InstanceOperation):
name = _("create disk") name = _("create disk")
description = _("Create empty disk for the VM.") description = _("Create empty disk for the VM.")
required_perms = ('storage.create_empty_disk', ) required_perms = ('storage.create_empty_disk', )
accept_states = ('STOPPED', 'PENDING', 'RUNNING')
def check_precond(self):
super(CreateDiskOperation, self).check_precond()
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, user, size, activity, name=None): def _operation(self, user, size, activity, name=None):
from storage.models import Disk from storage.models import Disk
...@@ -183,11 +194,7 @@ class DownloadDiskOperation(InstanceOperation): ...@@ -183,11 +194,7 @@ class DownloadDiskOperation(InstanceOperation):
abortable = True abortable = True
has_percentage = True has_percentage = True
required_perms = ('storage.download_disk', ) required_perms = ('storage.download_disk', )
accept_states = ('STOPPED', 'PENDING', 'RUNNING')
def check_precond(self):
super(DownloadDiskOperation, self).check_precond()
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, user, url, task, activity, name=None): def _operation(self, user, url, task, activity, name=None):
activity.result = url activity.result = url
...@@ -218,11 +225,7 @@ class DeployOperation(InstanceOperation): ...@@ -218,11 +225,7 @@ class DeployOperation(InstanceOperation):
name = _("deploy") name = _("deploy")
description = _("Deploy new virtual machine with network.") description = _("Deploy new virtual machine with network.")
required_perms = () required_perms = ()
deny_states = ('SUSPENDED', 'RUNNING')
def check_precond(self):
super(DeployOperation, self).check_precond()
if self.instance.status in ['RUNNING', 'SUSPENDED']:
raise self.instance.WrongStateError(self.instance)
def is_preferred(self): def is_preferred(self):
return self.instance.status in (self.instance.STATUS.STOPPED, return self.instance.status in (self.instance.STATUS.STOPPED,
...@@ -323,6 +326,7 @@ class MigrateOperation(InstanceOperation): ...@@ -323,6 +326,7 @@ class MigrateOperation(InstanceOperation):
name = _("migrate") name = _("migrate")
description = _("Live migrate running VM to another node.") description = _("Live migrate running VM to another node.")
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def rollback(self, activity): def rollback(self, activity):
with activity.sub_activity( with activity.sub_activity(
...@@ -330,11 +334,6 @@ class MigrateOperation(InstanceOperation): ...@@ -330,11 +334,6 @@ class MigrateOperation(InstanceOperation):
"redeploy network (rollback)")): "redeploy network (rollback)")):
self.instance.deploy_net() self.instance.deploy_net()
def check_precond(self):
super(MigrateOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def check_auth(self, user): def check_auth(self, user):
if not user.is_superuser: if not user.is_superuser:
raise PermissionDenied() raise PermissionDenied()
...@@ -384,11 +383,7 @@ class RebootOperation(InstanceOperation): ...@@ -384,11 +383,7 @@ class RebootOperation(InstanceOperation):
name = _("reboot") name = _("reboot")
description = _("Reboot virtual machine with Ctrl+Alt+Del signal.") description = _("Reboot virtual machine with Ctrl+Alt+Del signal.")
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def check_precond(self):
super(RebootOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, timeout=5): def _operation(self, timeout=5):
self.instance.reboot_vm(timeout=timeout) self.instance.reboot_vm(timeout=timeout)
...@@ -403,11 +398,7 @@ class RemoveInterfaceOperation(InstanceOperation): ...@@ -403,11 +398,7 @@ class RemoveInterfaceOperation(InstanceOperation):
name = _("remove interface") name = _("remove interface")
description = _("Remove the specified network interface from the VM.") description = _("Remove the specified network interface from the VM.")
required_perms = () required_perms = ()
accept_states = ('STOPPED', 'PENDING', 'RUNNING')
def check_precond(self):
super(RemoveInterfaceOperation, self).check_precond()
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, activity, user, system, interface): def _operation(self, activity, user, system, interface):
if self.instance.is_running: if self.instance.is_running:
...@@ -428,11 +419,7 @@ class RemoveDiskOperation(InstanceOperation): ...@@ -428,11 +419,7 @@ class RemoveDiskOperation(InstanceOperation):
name = _("remove disk") name = _("remove disk")
description = _("Remove the specified disk from the VM.") description = _("Remove the specified disk from the VM.")
required_perms = () required_perms = ()
accept_states = ('STOPPED', 'PENDING', 'RUNNING')
def check_precond(self):
super(RemoveDiskOperation, self).check_precond()
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, activity, user, system, disk): def _operation(self, activity, user, system, disk):
if self.instance.is_running and disk.type not in ["iso"]: if self.instance.is_running and disk.type not in ["iso"]:
...@@ -450,11 +437,7 @@ class ResetOperation(InstanceOperation): ...@@ -450,11 +437,7 @@ class ResetOperation(InstanceOperation):
name = _("reset") name = _("reset")
description = _("Reset virtual machine (reset button).") description = _("Reset virtual machine (reset button).")
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def check_precond(self):
super(ResetOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, timeout=5): def _operation(self, timeout=5):
self.instance.reset_vm(timeout=timeout) self.instance.reset_vm(timeout=timeout)
...@@ -473,6 +456,7 @@ class SaveAsTemplateOperation(InstanceOperation): ...@@ -473,6 +456,7 @@ class SaveAsTemplateOperation(InstanceOperation):
""") """)
abortable = True abortable = True
required_perms = ('vm.create_template', ) required_perms = ('vm.create_template', )
accept_states = ('RUNNING', 'PENDING', 'STOPPED')
def is_preferred(self): def is_preferred(self):
return (self.instance.is_base and return (self.instance.is_base and
...@@ -493,11 +477,6 @@ class SaveAsTemplateOperation(InstanceOperation): ...@@ -493,11 +477,6 @@ class SaveAsTemplateOperation(InstanceOperation):
for disk in self.disks: for disk in self.disks:
disk.destroy() disk.destroy()
def check_precond(self):
super(SaveAsTemplateOperation, self).check_precond()
if self.instance.status not in ['RUNNING', 'PENDING', 'STOPPED']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, activity, user, system, timeout=300, name=None, def _operation(self, activity, user, system, timeout=300, name=None,
with_shutdown=True, task=None, **kwargs): with_shutdown=True, task=None, **kwargs):
if with_shutdown: if with_shutdown:
...@@ -567,11 +546,7 @@ class ShutdownOperation(InstanceOperation): ...@@ -567,11 +546,7 @@ class ShutdownOperation(InstanceOperation):
description = _("Shutdown virtual machine with ACPI signal.") description = _("Shutdown virtual machine with ACPI signal.")
abortable = True abortable = True
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def check_precond(self):
super(ShutdownOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def on_commit(self, activity): def on_commit(self, activity):
activity.resultant_state = 'STOPPED' activity.resultant_state = 'STOPPED'
...@@ -591,11 +566,7 @@ class ShutOffOperation(InstanceOperation): ...@@ -591,11 +566,7 @@ class ShutOffOperation(InstanceOperation):
name = _("shut off") name = _("shut off")
description = _("Shut off VM (plug-out).") description = _("Shut off VM (plug-out).")
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def check_precond(self):
super(ShutOffOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def on_commit(self, activity): def on_commit(self, activity):
activity.resultant_state = 'STOPPED' activity.resultant_state = 'STOPPED'
...@@ -623,16 +594,12 @@ class SleepOperation(InstanceOperation): ...@@ -623,16 +594,12 @@ class SleepOperation(InstanceOperation):
name = _("sleep") name = _("sleep")
description = _("Suspend virtual machine with memory dump.") description = _("Suspend virtual machine with memory dump.")
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def is_preferred(self): def is_preferred(self):
return (not self.instance.is_base and return (not self.instance.is_base and
self.instance.status == self.instance.STATUS.RUNNING) self.instance.status == self.instance.STATUS.RUNNING)
def check_precond(self):
super(SleepOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def on_abort(self, activity, error): def on_abort(self, activity, error):
if isinstance(error, TimeLimitExceeded): if isinstance(error, TimeLimitExceeded):
activity.resultant_state = None activity.resultant_state = None
...@@ -670,15 +637,11 @@ class WakeUpOperation(InstanceOperation): ...@@ -670,15 +637,11 @@ class WakeUpOperation(InstanceOperation):
Power on Virtual Machine and load its memory from dump. Power on Virtual Machine and load its memory from dump.
""") """)
required_perms = () required_perms = ()
accept_states = ('SUSPENDED', )
def is_preferred(self): def is_preferred(self):
return self.instance.status == self.instance.STATUS.SUSPENDED return self.instance.status == self.instance.STATUS.SUSPENDED
def check_precond(self):
super(WakeUpOperation, self).check_precond()
if self.instance.status not in ['SUSPENDED']:
raise self.instance.WrongStateError(self.instance)
def on_abort(self, activity, error): def on_abort(self, activity, error):
activity.resultant_state = 'ERROR' activity.resultant_state = 'ERROR'
...@@ -718,11 +681,6 @@ class RenewOperation(InstanceOperation): ...@@ -718,11 +681,6 @@ class RenewOperation(InstanceOperation):
required_perms = () required_perms = ()
concurrency_check = False concurrency_check = False
def check_precond(self):
super(RenewOperation, self).check_precond()
if self.instance.status == 'DESTROYED':
raise self.instance.WrongStateError(self.instance)
def _operation(self, lease=None): def _operation(self, lease=None):
(self.instance.time_of_suspend, (self.instance.time_of_suspend,
self.instance.time_of_delete) = self.instance.get_renew_times(lease) self.instance.time_of_delete) = self.instance.get_renew_times(lease)
...@@ -790,7 +748,8 @@ class FlushOperation(NodeOperation): ...@@ -790,7 +748,8 @@ class FlushOperation(NodeOperation):
def check_auth(self, user): def check_auth(self, user):
if not user.is_superuser: if not user.is_superuser:
raise PermissionDenied() raise humanize_exception(ugettext_noop(
"Superuser privileges are required."), PermissionDenied())
super(FlushOperation, self).check_auth(user=user) super(FlushOperation, self).check_auth(user=user)
...@@ -815,11 +774,7 @@ class ScreenshotOperation(InstanceOperation): ...@@ -815,11 +774,7 @@ class ScreenshotOperation(InstanceOperation):
description = _("Get screenshot") description = _("Get screenshot")
acl_level = "owner" acl_level = "owner"
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def check_precond(self):
super(ScreenshotOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self): def _operation(self):
return self.instance.get_screenshot(timeout=20) return self.instance.get_screenshot(timeout=20)
...@@ -835,10 +790,13 @@ class RecoverOperation(InstanceOperation): ...@@ -835,10 +790,13 @@ class RecoverOperation(InstanceOperation):
description = _("Recover virtual machine from destroyed state.") description = _("Recover virtual machine from destroyed state.")
acl_level = "owner" acl_level = "owner"
required_perms = ('vm.recover', ) required_perms = ('vm.recover', )
accept_states = ('DESTROYED', )
def check_precond(self): def check_precond(self):
if not self.instance.destroyed_at: try:
raise self.instance.WrongStateError(self.instance) super(RecoverOperation, self).check_precond()
except Instance.InstanceDestroyedError:
pass
def on_commit(self, activity): def on_commit(self, activity):
activity.resultant_state = 'PENDING' activity.resultant_state = 'PENDING'
...@@ -862,11 +820,7 @@ class ResourcesOperation(InstanceOperation): ...@@ -862,11 +820,7 @@ class ResourcesOperation(InstanceOperation):
description = _("Change resources") description = _("Change resources")
acl_level = "owner" acl_level = "owner"
required_perms = ('vm.change_resources', ) required_perms = ('vm.change_resources', )
accept_states = ('STOPPED', 'PENDING', )
def check_precond(self):
super(ResourcesOperation, self).check_precond()
if self.instance.status not in ["STOPPED", "PENDING"]:
raise self.instance.WrongStateError(self.instance)
def _operation(self, user, activity, def _operation(self, user, activity,
num_cores, ram_size, max_ram_size, priority): num_cores, ram_size, max_ram_size, priority):
...@@ -896,11 +850,7 @@ class PasswordResetOperation(InstanceOperation): ...@@ -896,11 +850,7 @@ class PasswordResetOperation(InstanceOperation):
description = _("Password reset") description = _("Password reset")
acl_level = "owner" acl_level = "owner"
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def check_precond(self):
super(PasswordResetOperation, self).check_precond()
if self.instance.status not in ["RUNNING"]:
raise self.instance.WrongStateError(self.instance)
def _operation(self): def _operation(self):
self.instance.pw = pwgen() self.instance.pw = pwgen()
......
...@@ -210,7 +210,7 @@ class NodeTestCase(TestCase): ...@@ -210,7 +210,7 @@ class NodeTestCase(TestCase):
node.enabled = True node.enabled = True
node.STATES = Node.STATES node.STATES = Node.STATES
self.assertEqual(Node.get_state(node), "ONLINE") self.assertEqual(Node.get_state(node), "ONLINE")
assert isinstance(Node.get_status_display(node), _("").__class__) assert isinstance(Node.get_status_display(node), _("x").__class__)
class InstanceActivityTestCase(TestCase): class InstanceActivityTestCase(TestCase):
......
...@@ -21,7 +21,6 @@ kombu==3.0.15 ...@@ -21,7 +21,6 @@ kombu==3.0.15
logutils==0.3.3 logutils==0.3.3
MarkupSafe==0.21 MarkupSafe==0.21
netaddr==0.7.11 netaddr==0.7.11
nose==1.3.1
pip-tools==0.3.4 pip-tools==0.3.4
psycopg2==2.5.2 psycopg2==2.5.2
Pygments==1.6 Pygments==1.6
......
...@@ -3,3 +3,5 @@ ...@@ -3,3 +3,5 @@
coverage==3.7.1 coverage==3.7.1
factory-boy==2.3.1 factory-boy==2.3.1
mock==1.0.1 mock==1.0.1
django-nose==1.2
nose==1.3.3
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment