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
5c0672bc
authored
Jul 23, 2014
by
Őry Máté
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'nostate-operation' into 'master'
Nostate Operation
parents
46a12f1e
865a8ba5
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
39 additions
and
36 deletions
+39
-36
circle/dashboard/tests/test_views.py
+3
-5
circle/dashboard/views.py
+3
-0
circle/vm/models/activity.py
+13
-12
circle/vm/models/instance.py
+3
-17
circle/vm/operations.py
+15
-0
circle/vm/tests/test_models.py
+2
-2
No files found.
circle/dashboard/tests/test_views.py
View file @
5c0672bc
...
...
@@ -536,7 +536,7 @@ class VmDetailTest(LoginMixin, TestCase):
with
patch
.
object
(
WakeUpOperation
,
'async'
)
as
mock_method
:
inst
=
Instance
.
objects
.
get
(
pk
=
1
)
mock_method
.
side_effect
=
inst
.
wake_up
inst
.
manual_state_change
(
'RUNNING'
)
inst
.
status
=
'RUNNING'
inst
.
set_level
(
self
.
u2
,
'owner'
)
with
patch
(
'dashboard.views.messages'
)
as
msg
:
c
.
post
(
"/dashboard/vm/1/op/wake_up/"
)
...
...
@@ -554,7 +554,7 @@ class VmDetailTest(LoginMixin, TestCase):
inst
=
Instance
.
objects
.
get
(
pk
=
1
)
new_wake_up
.
side_effect
=
inst
.
wake_up
inst
.
get_remote_queue_name
=
Mock
(
return_value
=
'test'
)
inst
.
manual_state_change
(
'SUSPENDED'
)
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/"
)
...
...
@@ -568,12 +568,10 @@ class VmDetailTest(LoginMixin, TestCase):
c
=
Client
()
self
.
login
(
c
,
"user2"
)
inst
=
Instance
.
objects
.
get
(
pk
=
1
)
inst
.
manual_state_change
(
'SUSPENDED'
)
inst
.
status
=
'SUSPENDED'
inst
.
set_level
(
self
.
u2
,
'user'
)
response
=
c
.
post
(
"/dashboard/vm/1/op/wake_up/"
)
self
.
assertEqual
(
response
.
status_code
,
403
)
inst
=
Instance
.
objects
.
get
(
pk
=
1
)
self
.
assertEqual
(
inst
.
status
,
'SUSPENDED'
)
def
test_non_existing_template_get
(
self
):
c
=
Client
()
...
...
circle/dashboard/views.py
View file @
5c0672bc
...
...
@@ -335,6 +335,7 @@ class VmDetailView(CheckedDetailView):
for
k
,
v
in
options
.
iteritems
():
if
request
.
POST
.
get
(
k
)
is
not
None
:
return
v
(
request
)
raise
Http404
()
raise
Http404
()
...
...
@@ -866,6 +867,8 @@ vm_ops = OrderedDict([
op
=
'shut_off'
,
icon
=
'ban'
,
effect
=
'warning'
)),
(
'recover'
,
VmOperationView
.
factory
(
op
=
'recover'
,
icon
=
'medkit'
,
effect
=
'warning'
)),
(
'nostate'
,
VmOperationView
.
factory
(
op
=
'emergency_change_state'
,
icon
=
'legal'
,
effect
=
'danger'
)),
(
'destroy'
,
VmOperationView
.
factory
(
extra_bases
=
[
TokenOperationView
],
op
=
'destroy'
,
icon
=
'times'
,
effect
=
'danger'
)),
...
...
circle/vm/models/activity.py
View file @
5c0672bc
...
...
@@ -30,7 +30,7 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop
from
common.models
import
(
ActivityModel
,
activitycontextimpl
,
create_readable
,
join_activity_code
,
HumanReadableObject
,
HumanReadableObject
,
HumanReadableException
,
)
from
manager.mancelery
import
celery
...
...
@@ -39,16 +39,17 @@ from manager.mancelery import celery
logger
=
getLogger
(
__name__
)
class
ActivityInProgressError
(
Exception
):
class
ActivityInProgressError
(
HumanReadable
Exception
):
def
__init__
(
self
,
activity
,
message
=
None
):
if
message
is
None
:
message
=
(
"Another activity is currently in progress: '
%
s'."
%
activity
.
activity_code
)
Exception
.
__init__
(
self
,
message
)
self
.
activity
=
activity
@classmethod
def
create
(
cls
,
activity
):
obj
=
super
(
ActivityInProgressError
,
cls
)
.
create
(
ugettext_noop
(
"
%(activity)
s activity is currently in progress."
),
ugettext_noop
(
"
%(activity)
s (
%(pk)
s) activity is currently "
"in progress."
),
activity
=
activity
.
readable_name
,
pk
=
activity
.
pk
)
obj
.
activity
=
activity
return
obj
def
_normalize_readable_name
(
name
,
default
=
None
):
...
...
@@ -95,7 +96,7 @@ class InstanceActivity(ActivityModel):
# Check for concurrent activities
active_activities
=
instance
.
activity_log
.
filter
(
finished__isnull
=
True
)
if
concurrency_check
and
active_activities
.
exists
():
raise
ActivityInProgressError
(
active_activities
[
0
])
raise
ActivityInProgressError
.
create
(
active_activities
[
0
])
activity_code
=
join_activity_code
(
cls
.
ACTIVITY_CODE_BASE
,
code_suffix
)
act
=
cls
(
activity_code
=
activity_code
,
instance
=
instance
,
parent
=
None
,
...
...
@@ -112,7 +113,7 @@ class InstanceActivity(ActivityModel):
# Check for concurrent activities
active_children
=
self
.
children
.
filter
(
finished__isnull
=
True
)
if
concurrency_check
and
active_children
.
exists
():
raise
ActivityInProgressError
(
active_children
[
0
])
raise
ActivityInProgressError
.
create
(
active_children
[
0
])
act
=
InstanceActivity
(
activity_code
=
join_activity_code
(
self
.
activity_code
,
code_suffix
),
...
...
circle/vm/models/instance.py
View file @
5c0672bc
...
...
@@ -271,6 +271,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
(
'create_vm'
,
_
(
'Can create a new VM.'
)),
(
'config_ports'
,
_
(
'Can configure port forwards.'
)),
(
'recover'
,
_
(
'Can recover a destroyed VM.'
)),
(
'emergency_change_state'
,
_
(
'Can change VM state to NOSTATE.'
)),
)
verbose_name
=
_
(
'instance'
)
verbose_name_plural
=
_
(
'instances'
)
...
...
@@ -444,27 +445,12 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
self
.
time_of_suspend
,
self
.
time_of_delete
=
self
.
get_renew_times
()
super
(
Instance
,
self
)
.
clean
(
*
args
,
**
kwargs
)
def
manual_state_change
(
self
,
new_state
=
"NOSTATE"
,
reason
=
None
,
user
=
None
):
""" Manually change state of an Instance.
Can be used to recover VM after administrator fixed problems.
"""
# TODO cancel concurrent activity (if exists)
act
=
InstanceActivity
.
create
(
code_suffix
=
'manual_state_change'
,
instance
=
self
,
user
=
user
,
readable_name
=
create_readable
(
ugettext_noop
(
"force
%(state)
s state"
),
state
=
new_state
))
act
.
finished
=
act
.
started
act
.
result
=
reason
act
.
resultant_state
=
new_state
act
.
succeeded
=
True
act
.
save
()
def
vm_state_changed
(
self
,
new_state
):
# log state change
try
:
act
=
InstanceActivity
.
create
(
code_suffix
=
'vm_state_changed'
,
instance
=
self
)
instance
=
self
,
readable_name
=
"vm state changed"
)
except
ActivityInProgressError
:
pass
# discard state change if another activity is in progress.
else
:
...
...
circle/vm/operations.py
View file @
5c0672bc
...
...
@@ -722,6 +722,21 @@ class RenewOperation(InstanceOperation):
register_operation
(
RenewOperation
)
class
ChangeStateOperation
(
InstanceOperation
):
activity_code_suffix
=
'emergency_change_state'
id
=
'emergency_change_state'
name
=
_
(
"emergency change state"
)
description
=
_
(
"Change the virtual machine state to NOSTATE"
)
acl_level
=
"owner"
required_perms
=
(
'vm.emergency_change_state'
,
)
def
_operation
(
self
,
user
,
activity
,
new_state
=
"NOSTATE"
):
activity
.
resultant_state
=
new_state
register_operation
(
ChangeStateOperation
)
class
NodeOperation
(
Operation
):
async_operation
=
abortable_async_node_operation
host_cls
=
Node
...
...
circle/vm/tests/test_models.py
View file @
5c0672bc
...
...
@@ -66,8 +66,8 @@ class InstanceTestCase(TestCase):
inst
=
MagicMock
(
spec
=
Instance
,
node
=
node
,
vnc_port
=
port
)
inst
.
save
.
side_effect
=
AssertionError
with
patch
(
'vm.models.instance.InstanceActivity'
)
as
ia
:
ia
.
create
.
side_effect
=
ActivityInProgressError
(
MagicMock
())
Instance
.
vm_state_changed
(
inst
,
'STOPPED'
)
ia
.
create
.
side_effect
=
ActivityInProgressError
.
create
(
MagicMock
())
Instance
.
status
=
'STOPPED'
self
.
assertEquals
(
inst
.
node
,
node
)
self
.
assertEquals
(
inst
.
vnc_port
,
port
)
...
...
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