Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
Gutyán Gábor
/
circlestack
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
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):
...
@@ -536,7 +536,7 @@ class VmDetailTest(LoginMixin, TestCase):
with
patch
.
object
(
WakeUpOperation
,
'async'
)
as
mock_method
:
with
patch
.
object
(
WakeUpOperation
,
'async'
)
as
mock_method
:
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
.
manual_state_change
(
'RUNNING'
)
inst
.
status
=
'RUNNING'
inst
.
set_level
(
self
.
u2
,
'owner'
)
inst
.
set_level
(
self
.
u2
,
'owner'
)
with
patch
(
'dashboard.views.messages'
)
as
msg
:
with
patch
(
'dashboard.views.messages'
)
as
msg
:
c
.
post
(
"/dashboard/vm/1/op/wake_up/"
)
c
.
post
(
"/dashboard/vm/1/op/wake_up/"
)
...
@@ -554,7 +554,7 @@ class VmDetailTest(LoginMixin, TestCase):
...
@@ -554,7 +554,7 @@ class VmDetailTest(LoginMixin, TestCase):
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'
)
inst
.
manual_state_change
(
'SUSPENDED'
)
inst
.
status
=
'SUSPENDED'
inst
.
set_level
(
self
.
u2
,
'owner'
)
inst
.
set_level
(
self
.
u2
,
'owner'
)
with
patch
(
'dashboard.views.messages'
)
as
msg
:
with
patch
(
'dashboard.views.messages'
)
as
msg
:
response
=
c
.
post
(
"/dashboard/vm/1/op/wake_up/"
)
response
=
c
.
post
(
"/dashboard/vm/1/op/wake_up/"
)
...
@@ -568,12 +568,10 @@ class VmDetailTest(LoginMixin, TestCase):
...
@@ -568,12 +568,10 @@ class VmDetailTest(LoginMixin, TestCase):
c
=
Client
()
c
=
Client
()
self
.
login
(
c
,
"user2"
)
self
.
login
(
c
,
"user2"
)
inst
=
Instance
.
objects
.
get
(
pk
=
1
)
inst
=
Instance
.
objects
.
get
(
pk
=
1
)
inst
.
manual_state_change
(
'SUSPENDED'
)
inst
.
status
=
'SUSPENDED'
inst
.
set_level
(
self
.
u2
,
'user'
)
inst
.
set_level
(
self
.
u2
,
'user'
)
response
=
c
.
post
(
"/dashboard/vm/1/op/wake_up/"
)
response
=
c
.
post
(
"/dashboard/vm/1/op/wake_up/"
)
self
.
assertEqual
(
response
.
status_code
,
403
)
self
.
assertEqual
(
response
.
status_code
,
403
)
inst
=
Instance
.
objects
.
get
(
pk
=
1
)
self
.
assertEqual
(
inst
.
status
,
'SUSPENDED'
)
def
test_non_existing_template_get
(
self
):
def
test_non_existing_template_get
(
self
):
c
=
Client
()
c
=
Client
()
...
...
circle/dashboard/views.py
View file @
5c0672bc
...
@@ -335,6 +335,7 @@ class VmDetailView(CheckedDetailView):
...
@@ -335,6 +335,7 @@ class VmDetailView(CheckedDetailView):
for
k
,
v
in
options
.
iteritems
():
for
k
,
v
in
options
.
iteritems
():
if
request
.
POST
.
get
(
k
)
is
not
None
:
if
request
.
POST
.
get
(
k
)
is
not
None
:
return
v
(
request
)
return
v
(
request
)
raise
Http404
()
raise
Http404
()
raise
Http404
()
...
@@ -866,6 +867,8 @@ vm_ops = OrderedDict([
...
@@ -866,6 +867,8 @@ vm_ops = OrderedDict([
op
=
'shut_off'
,
icon
=
'ban'
,
effect
=
'warning'
)),
op
=
'shut_off'
,
icon
=
'ban'
,
effect
=
'warning'
)),
(
'recover'
,
VmOperationView
.
factory
(
(
'recover'
,
VmOperationView
.
factory
(
op
=
'recover'
,
icon
=
'medkit'
,
effect
=
'warning'
)),
op
=
'recover'
,
icon
=
'medkit'
,
effect
=
'warning'
)),
(
'nostate'
,
VmOperationView
.
factory
(
op
=
'emergency_change_state'
,
icon
=
'legal'
,
effect
=
'danger'
)),
(
'destroy'
,
VmOperationView
.
factory
(
(
'destroy'
,
VmOperationView
.
factory
(
extra_bases
=
[
TokenOperationView
],
extra_bases
=
[
TokenOperationView
],
op
=
'destroy'
,
icon
=
'times'
,
effect
=
'danger'
)),
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
...
@@ -30,7 +30,7 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop
from
common.models
import
(
from
common.models
import
(
ActivityModel
,
activitycontextimpl
,
create_readable
,
join_activity_code
,
ActivityModel
,
activitycontextimpl
,
create_readable
,
join_activity_code
,
HumanReadableObject
,
HumanReadableObject
,
HumanReadableException
,
)
)
from
manager.mancelery
import
celery
from
manager.mancelery
import
celery
...
@@ -39,16 +39,17 @@ from manager.mancelery import celery
...
@@ -39,16 +39,17 @@ from manager.mancelery import celery
logger
=
getLogger
(
__name__
)
logger
=
getLogger
(
__name__
)
class
ActivityInProgressError
(
Exception
):
class
ActivityInProgressError
(
HumanReadable
Exception
):
def
__init__
(
self
,
activity
,
message
=
None
):
@classmethod
if
message
is
None
:
def
create
(
cls
,
activity
):
message
=
(
"Another activity is currently in progress: '
%
s'."
obj
=
super
(
ActivityInProgressError
,
cls
)
.
create
(
%
activity
.
activity_code
)
ugettext_noop
(
"
%(activity)
s activity is currently in progress."
),
ugettext_noop
(
"
%(activity)
s (
%(pk)
s) activity is currently "
Exception
.
__init__
(
self
,
message
)
"in progress."
),
activity
=
activity
.
readable_name
,
pk
=
activity
.
pk
)
self
.
activity
=
activity
obj
.
activity
=
activity
return
obj
def
_normalize_readable_name
(
name
,
default
=
None
):
def
_normalize_readable_name
(
name
,
default
=
None
):
...
@@ -95,7 +96,7 @@ class InstanceActivity(ActivityModel):
...
@@ -95,7 +96,7 @@ class InstanceActivity(ActivityModel):
# Check for concurrent activities
# Check for concurrent activities
active_activities
=
instance
.
activity_log
.
filter
(
finished__isnull
=
True
)
active_activities
=
instance
.
activity_log
.
filter
(
finished__isnull
=
True
)
if
concurrency_check
and
active_activities
.
exists
():
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
)
activity_code
=
join_activity_code
(
cls
.
ACTIVITY_CODE_BASE
,
code_suffix
)
act
=
cls
(
activity_code
=
activity_code
,
instance
=
instance
,
parent
=
None
,
act
=
cls
(
activity_code
=
activity_code
,
instance
=
instance
,
parent
=
None
,
...
@@ -112,7 +113,7 @@ class InstanceActivity(ActivityModel):
...
@@ -112,7 +113,7 @@ class InstanceActivity(ActivityModel):
# Check for concurrent activities
# Check for concurrent activities
active_children
=
self
.
children
.
filter
(
finished__isnull
=
True
)
active_children
=
self
.
children
.
filter
(
finished__isnull
=
True
)
if
concurrency_check
and
active_children
.
exists
():
if
concurrency_check
and
active_children
.
exists
():
raise
ActivityInProgressError
(
active_children
[
0
])
raise
ActivityInProgressError
.
create
(
active_children
[
0
])
act
=
InstanceActivity
(
act
=
InstanceActivity
(
activity_code
=
join_activity_code
(
self
.
activity_code
,
code_suffix
),
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,
...
@@ -271,6 +271,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
(
'create_vm'
,
_
(
'Can create a new VM.'
)),
(
'create_vm'
,
_
(
'Can create a new VM.'
)),
(
'config_ports'
,
_
(
'Can configure port forwards.'
)),
(
'config_ports'
,
_
(
'Can configure port forwards.'
)),
(
'recover'
,
_
(
'Can recover a destroyed VM.'
)),
(
'recover'
,
_
(
'Can recover a destroyed VM.'
)),
(
'emergency_change_state'
,
_
(
'Can change VM state to NOSTATE.'
)),
)
)
verbose_name
=
_
(
'instance'
)
verbose_name
=
_
(
'instance'
)
verbose_name_plural
=
_
(
'instances'
)
verbose_name_plural
=
_
(
'instances'
)
...
@@ -444,27 +445,12 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
...
@@ -444,27 +445,12 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
self
.
time_of_suspend
,
self
.
time_of_delete
=
self
.
get_renew_times
()
self
.
time_of_suspend
,
self
.
time_of_delete
=
self
.
get_renew_times
()
super
(
Instance
,
self
)
.
clean
(
*
args
,
**
kwargs
)
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
):
def
vm_state_changed
(
self
,
new_state
):
# log state change
# log state change
try
:
try
:
act
=
InstanceActivity
.
create
(
code_suffix
=
'vm_state_changed'
,
act
=
InstanceActivity
.
create
(
code_suffix
=
'vm_state_changed'
,
instance
=
self
)
instance
=
self
,
readable_name
=
"vm state changed"
)
except
ActivityInProgressError
:
except
ActivityInProgressError
:
pass
# discard state change if another activity is in progress.
pass
# discard state change if another activity is in progress.
else
:
else
:
...
...
circle/vm/operations.py
View file @
5c0672bc
...
@@ -722,6 +722,21 @@ class RenewOperation(InstanceOperation):
...
@@ -722,6 +722,21 @@ class RenewOperation(InstanceOperation):
register_operation
(
RenewOperation
)
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
):
class
NodeOperation
(
Operation
):
async_operation
=
abortable_async_node_operation
async_operation
=
abortable_async_node_operation
host_cls
=
Node
host_cls
=
Node
...
...
circle/vm/tests/test_models.py
View file @
5c0672bc
...
@@ -66,8 +66,8 @@ class InstanceTestCase(TestCase):
...
@@ -66,8 +66,8 @@ class InstanceTestCase(TestCase):
inst
=
MagicMock
(
spec
=
Instance
,
node
=
node
,
vnc_port
=
port
)
inst
=
MagicMock
(
spec
=
Instance
,
node
=
node
,
vnc_port
=
port
)
inst
.
save
.
side_effect
=
AssertionError
inst
.
save
.
side_effect
=
AssertionError
with
patch
(
'vm.models.instance.InstanceActivity'
)
as
ia
:
with
patch
(
'vm.models.instance.InstanceActivity'
)
as
ia
:
ia
.
create
.
side_effect
=
ActivityInProgressError
(
MagicMock
())
ia
.
create
.
side_effect
=
ActivityInProgressError
.
create
(
MagicMock
())
Instance
.
vm_state_changed
(
inst
,
'STOPPED'
)
Instance
.
status
=
'STOPPED'
self
.
assertEquals
(
inst
.
node
,
node
)
self
.
assertEquals
(
inst
.
node
,
node
)
self
.
assertEquals
(
inst
.
vnc_port
,
port
)
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