Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
CIRCLE
/
cloud
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
94
Merge Requests
10
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
2c621f0f
authored
Jul 30, 2014
by
Őry Máté
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feature-operations-hre' into 'master'
Handle and use HumanReadableExceptions
parents
8f0c13b8
0385e921
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
96 additions
and
55 deletions
+96
-55
circle/common/models.py
+48
-15
circle/dashboard/tests/test_views.py
+21
-19
circle/dashboard/views.py
+10
-2
circle/vm/models/instance.py
+17
-19
circle/vm/operations.py
+0
-0
No files found.
circle/common/models.py
View file @
2c621f0f
...
...
@@ -23,6 +23,7 @@ from logging import getLogger
from
time
import
time
from
warnings
import
warn
from
django.contrib
import
messages
from
django.contrib.auth.models
import
User
from
django.core.cache
import
cache
from
django.core.serializers.json
import
DjangoJSONEncoder
...
...
@@ -46,17 +47,24 @@ class WorkerNotFound(Exception):
def
activitycontextimpl
(
act
,
on_abort
=
None
,
on_commit
=
None
):
try
:
yield
act
except
BaseException
as
e
:
# BaseException is the common parent of Exception and
# system-exiting exceptions, e.g. KeyboardInterrupt
try
:
yield
act
except
HumanReadableException
as
e
:
result
=
e
raise
except
BaseException
as
e
:
# BaseException is the common parent of Exception and
# system-exiting exceptions, e.g. KeyboardInterrupt
result
=
create_readable
(
ugettext_noop
(
"Failure."
),
ugettext_noop
(
"Unhandled exception:
%(error)
s"
),
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
)
result
=
create_readable
(
ugettext_noop
(
"Failure."
),
ugettext_noop
(
"Unhandled exception: "
"
%(error)
s"
),
error
=
unicode
(
e
))
act
.
finish
(
succeeded
=
False
,
result
=
result
,
event_handler
=
handler
)
raise
e
raise
else
:
act
.
finish
(
succeeded
=
True
,
event_handler
=
on_commit
)
...
...
@@ -196,6 +204,10 @@ class ActivityModel(TimeStampedModel):
DeprecationWarning
,
stacklevel
=
2
)
value
=
create_readable
(
user_text_template
=
""
,
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
()
...
...
@@ -361,8 +373,9 @@ class HumanReadableObject(object):
@classmethod
def
create
(
cls
,
user_text_template
,
admin_text_template
=
None
,
**
params
):
return
cls
(
user_text_template
,
admin_text_template
or
user_text_template
,
params
)
return
cls
(
user_text_template
=
user_text_template
,
admin_text_template
=
(
admin_text_template
or
user_text_template
),
params
=
params
)
def
set
(
self
,
user_text_template
,
admin_text_template
=
None
,
**
params
):
self
.
_set_values
(
user_text_template
,
...
...
@@ -407,10 +420,28 @@ create_readable = HumanReadableObject.create
class
HumanReadableException
(
HumanReadableObject
,
Exception
):
"""HumanReadableObject that is an Exception so can used in except clause.
"""
pass
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
:
msg
=
self
.
get_admin_text
()
else
:
msg
=
self
.
get_user_text
()
getattr
(
messages
,
level
or
self
.
level
)(
request
,
msg
)
def
humanize_exception
(
message
,
exception
=
None
,
**
params
):
def
humanize_exception
(
message
,
exception
=
None
,
level
=
None
,
**
params
):
"""Return new dynamic-class exception which is based on
HumanReadableException and the original class with the dict of exception.
...
...
@@ -419,8 +450,10 @@ def humanize_exception(message, exception=None, **params):
...
Welcome!
"""
Ex
=
type
(
"HumanReadable"
+
type
(
exception
)
.
__name__
,
(
HumanReadableException
,
type
(
exception
)),
exception
.
__dict__
)
return
Ex
.
create
(
message
,
**
params
)
ex
=
Ex
.
create
(
message
,
**
params
)
if
level
:
ex
.
level
=
level
return
ex
circle/dashboard/tests/test_views.py
View file @
2c621f0f
...
...
@@ -529,36 +529,38 @@ class VmDetailTest(LoginMixin, TestCase):
def
test_permitted_wake_up_wrong_state
(
self
):
c
=
Client
()
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
)
mock_method
.
side_effect
=
inst
.
wake_up
inst
.
status
=
'RUNNING'
inst
.
set_level
(
self
.
u2
,
'owner'
)
with
patch
(
'dashboard.views.messages'
)
as
msg
:
c
.
post
(
"/dashboard/vm/1/op/wake_up/"
)
assert
msg
.
error
.
called
c
.
post
(
"/dashboard/vm/1/op/wake_up/"
)
inst
=
Instance
.
objects
.
get
(
pk
=
1
)
self
.
assertEqual
(
inst
.
status
,
'RUNNING'
)
# mocked anyway
assert
mock_method
.
called
assert
wro
.
called
def
test_permitted_wake_up
(
self
):
c
=
Client
()
self
.
login
(
c
,
"user2"
)
with
patch
.
object
(
Instance
,
'select_node'
,
return_value
=
None
):
with
patch
.
object
(
WakeUpOperation
,
'async'
)
as
new_wake_up
:
with
patch
(
'vm.tasks.vm_tasks.wake_up.apply_async'
)
as
wuaa
:
inst
=
Instance
.
objects
.
get
(
pk
=
1
)
new_wake_up
.
side_effect
=
inst
.
wake_up
inst
.
get_remote_queue_name
=
Mock
(
return_value
=
'test'
)
inst
.
status
=
'SUSPENDED'
inst
.
set_level
(
self
.
u2
,
'owner'
)
with
patch
(
'dashboard.views.messages'
)
as
msg
:
response
=
c
.
post
(
"/dashboard/vm/1/op/wake_up/"
)
assert
not
msg
.
error
.
called
self
.
assertEqual
(
response
.
status_code
,
302
)
self
.
assertEqual
(
inst
.
status
,
'RUNNING'
)
assert
new_wake_up
.
called
assert
wuaa
.
called
with
patch
.
object
(
Instance
,
'select_node'
,
return_value
=
None
),
\
patch
.
object
(
WakeUpOperation
,
'async'
)
as
new_wake_up
,
\
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
)
new_wake_up
.
side_effect
=
inst
.
wake_up
inst
.
get_remote_queue_name
=
Mock
(
return_value
=
'test'
)
inst
.
status
=
'SUSPENDED'
inst
.
set_level
(
self
.
u2
,
'owner'
)
with
patch
(
'dashboard.views.messages'
)
as
msg
:
response
=
c
.
post
(
"/dashboard/vm/1/op/wake_up/"
)
assert
not
msg
.
error
.
called
self
.
assertEqual
(
response
.
status_code
,
302
)
self
.
assertEqual
(
inst
.
status
,
'RUNNING'
)
assert
new_wake_up
.
called
assert
wuaa
.
called
assert
not
wro
.
called
def
test_unpermitted_wake_up
(
self
):
c
=
Client
()
...
...
circle/dashboard/views.py
View file @
2c621f0f
...
...
@@ -71,7 +71,7 @@ from .tables import (
NodeListTable
,
NodeVmListTable
,
TemplateListTable
,
LeaseListTable
,
GroupListTable
,
UserKeyListTable
)
from
common.models
import
HumanReadableObject
from
common.models
import
HumanReadableObject
,
HumanReadableException
from
vm.models
import
(
Instance
,
instance_activity
,
InstanceActivity
,
InstanceTemplate
,
Interface
,
InterfaceTemplate
,
Lease
,
Node
,
NodeActivity
,
Trait
,
...
...
@@ -562,9 +562,13 @@ class OperationView(RedirectToLoginMixin, DetailView):
done
=
False
try
:
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
:
messages
.
error
(
request
,
_
(
'Could not start operation.'
))
logger
.
exception
(
e
)
logger
.
exception
(
"Could not start operation"
)
result
=
e
else
:
wait
=
self
.
wait_for_result
...
...
@@ -575,6 +579,10 @@ class OperationView(RedirectToLoginMixin, DetailView):
except
TimeoutError
:
logger
.
debug
(
"Result didn't arrive in
%
ss"
,
self
.
wait_for_result
,
exc_info
=
True
)
except
HumanReadableException
as
e
:
e
.
send_message
(
request
)
logger
.
exception
(
e
)
result
=
e
except
Exception
as
e
:
messages
.
error
(
request
,
_
(
'Operation failed.'
))
logger
.
debug
(
"Operation failed."
,
exc_info
=
True
)
...
...
circle/vm/models/instance.py
View file @
2c621f0f
...
...
@@ -41,7 +41,7 @@ from model_utils.models import TimeStampedModel, StatusModel
from
taggit.managers
import
TaggableManager
from
acl.models
import
AclBase
from
common.models
import
create_readable
from
common.models
import
create_readable
,
HumanReadableException
from
common.operations
import
OperatedMixin
from
..tasks
import
vm_tasks
,
agent_tasks
from
.activity
import
(
ActivityInProgressError
,
instance_activity
,
...
...
@@ -276,28 +276,26 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
verbose_name
=
_
(
'instance'
)
verbose_name_plural
=
_
(
'instances'
)
class
Instance
DestroyedError
(
Exception
):
class
Instance
Error
(
HumanReadable
Exception
):
def
__init__
(
self
,
instance
,
message
=
None
):
if
message
is
None
:
message
=
(
"The instance (
%
s) has already been destroyed."
%
instance
)
def
__init__
(
self
,
instance
,
params
=
None
,
level
=
None
,
**
kwargs
):
kwargs
.
update
(
params
or
{})
self
.
instance
=
kwargs
[
"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
,
message
=
None
):
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
__init__
(
self
,
instance
,
params
=
None
,
**
kwargs
):
super
(
Instance
.
WrongStateError
,
self
)
.
__init__
(
instance
,
params
,
state
=
instance
.
status
)
def
__unicode__
(
self
):
parts
=
(
self
.
name
,
"("
+
str
(
self
.
id
)
+
")"
)
...
...
circle/vm/operations.py
View file @
2c621f0f
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment