Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
Gelencsér Szabolcs
/
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
0bfdb2a3
authored
Oct 13, 2014
by
Bach Dániel
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feature-offline-migration' into 'master'
Feature offline migration See merge request !239
parents
a7e9781e
860300db
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
78 additions
and
45 deletions
+78
-45
circle/dashboard/forms.py
+14
-0
circle/dashboard/templates/dashboard/_vm-mass-migrate.html
+5
-3
circle/dashboard/templates/dashboard/_vm-migrate.html
+5
-3
circle/dashboard/tests/test_mockedviews.py
+30
-15
circle/dashboard/views/vm.py
+17
-18
circle/vm/operations.py
+5
-5
circle/vm/tests/test_operations.py
+2
-1
No files found.
circle/dashboard/forms.py
View file @
0bfdb2a3
...
@@ -752,6 +752,20 @@ class VmRenewForm(forms.Form):
...
@@ -752,6 +752,20 @@ class VmRenewForm(forms.Form):
return
helper
return
helper
class
VmMigrateForm
(
forms
.
Form
):
live_migration
=
forms
.
BooleanField
(
required
=
False
,
initial
=
True
,
label
=
_
(
"live migration"
))
def
__init__
(
self
,
*
args
,
**
kwargs
):
choices
=
kwargs
.
pop
(
'choices'
)
default
=
kwargs
.
pop
(
'default'
)
super
(
VmMigrateForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
fields
.
insert
(
0
,
'to_node'
,
forms
.
ModelChoiceField
(
queryset
=
choices
,
initial
=
default
,
required
=
False
,
widget
=
forms
.
RadioSelect
(),
label
=
_
(
"Node"
)))
class
VmStateChangeForm
(
forms
.
Form
):
class
VmStateChangeForm
(
forms
.
Form
):
interrupt
=
forms
.
BooleanField
(
required
=
False
,
label
=
_
(
interrupt
=
forms
.
BooleanField
(
required
=
False
,
label
=
_
(
...
...
circle/dashboard/templates/dashboard/_vm-mass-migrate.html
View file @
0bfdb2a3
{% extends "dashboard/mass-operate.html" %}
{% extends "dashboard/mass-operate.html" %}
{% load i18n %}
{% load i18n %}
{% load sizefieldtags %}
{% load sizefieldtags %}
{% load crispy_forms_tags %}
{% block formfields %}
{% block formfields %}
...
@@ -11,20 +12,20 @@
...
@@ -11,20 +12,20 @@
<label
for=
"migrate-to-none"
>
<label
for=
"migrate-to-none"
>
<strong>
{% trans "Reschedule" %}
</strong>
<strong>
{% trans "Reschedule" %}
</strong>
</label>
</label>
<input
id=
"migrate-to-none"
type=
"radio"
name=
"node"
value=
""
style=
"float: right;"
checked=
"checked"
>
<input
id=
"migrate-to-none"
type=
"radio"
name=
"
to_
node"
value=
""
style=
"float: right;"
checked=
"checked"
>
<span
class=
"vm-migrate-node-property"
>
<span
class=
"vm-migrate-node-property"
>
{% trans "This option will reschedule each virtual machine to the optimal node." %}
{% trans "This option will reschedule each virtual machine to the optimal node." %}
</span>
</span>
<div
style=
"clear: both;"
></div>
<div
style=
"clear: both;"
></div>
</div>
</div>
</li>
</li>
{% for n in
nodes
%}
{% for n in
form.fields.to_node.queryset.all
%}
<li
class=
"panel panel-default mass-migrate-node"
>
<li
class=
"panel panel-default mass-migrate-node"
>
<div
class=
"panel-body"
>
<div
class=
"panel-body"
>
<label
for=
"migrate-to-{{n.pk}}"
>
<label
for=
"migrate-to-{{n.pk}}"
>
<strong>
{{ n }}
</strong>
<strong>
{{ n }}
</strong>
</label>
</label>
<input
id=
"migrate-to-{{n.pk}}"
type=
"radio"
name=
"node"
value=
"{{ n.pk }}"
style=
"float: right;"
/>
<input
id=
"migrate-to-{{n.pk}}"
type=
"radio"
name=
"
to_
node"
value=
"{{ n.pk }}"
style=
"float: right;"
/>
<span
class=
"vm-migrate-node-property"
>
{% trans "CPU load" %}: {{ n.cpu_usage }}
</span>
<span
class=
"vm-migrate-node-property"
>
{% trans "CPU load" %}: {{ n.cpu_usage }}
</span>
<span
class=
"vm-migrate-node-property"
>
{% trans "RAM usage" %}: {{ n.byte_ram_usage|filesize }}/{{ n.ram_size|filesize }}
</span>
<span
class=
"vm-migrate-node-property"
>
{% trans "RAM usage" %}: {{ n.byte_ram_usage|filesize }}/{{ n.ram_size|filesize }}
</span>
<div
style=
"clear: both;"
></div>
<div
style=
"clear: both;"
></div>
...
@@ -32,5 +33,6 @@
...
@@ -32,5 +33,6 @@
</li>
</li>
{% endfor %}
{% endfor %}
</ul>
</ul>
{{ form.live_migration|as_crispy_field }}
<hr
/>
<hr
/>
{% endblock %}
{% endblock %}
circle/dashboard/templates/dashboard/_vm-migrate.html
View file @
0bfdb2a3
{% extends "dashboard/operate.html" %}
{% extends "dashboard/operate.html" %}
{% load i18n %}
{% load i18n %}
{% load sizefieldtags %}
{% load sizefieldtags %}
{% load crispy_forms_tags %}
{% block question %}
{% block question %}
<p>
<p>
...
@@ -13,8 +14,8 @@ Choose a compute node to migrate {{obj}} to.
...
@@ -13,8 +14,8 @@ Choose a compute node to migrate {{obj}} to.
{% block formfields %}
{% block formfields %}
<ul
id=
"vm-migrate-node-list"
class=
"list-unstyled"
>
<ul
id=
"vm-migrate-node-list"
class=
"list-unstyled"
>
{% with current=object.node.pk %}
{% with current=object.node.pk
recommended=form.fields.to_node.initial.pk
%}
{% for n in
nodes
%}
{% for n in
form.fields.to_node.queryset.all
%}
<li
class=
"panel panel-default"
><div
class=
"panel-body"
>
<li
class=
"panel panel-default"
><div
class=
"panel-body"
>
<label
for=
"migrate-to-{{n.pk}}"
>
<label
for=
"migrate-to-{{n.pk}}"
>
<strong>
{{ n }}
</strong>
<strong>
{{ n }}
</strong>
...
@@ -23,7 +24,7 @@ Choose a compute node to migrate {{obj}} to.
...
@@ -23,7 +24,7 @@ Choose a compute node to migrate {{obj}} to.
{% if current == n.pk %}
<div
class=
"label label-info"
>
{% trans "current" %}
</div>
{% endif %}
{% if current == n.pk %}
<div
class=
"label label-info"
>
{% trans "current" %}
</div>
{% endif %}
{% if recommended == n.pk %}
<div
class=
"label label-success"
>
{% trans "recommended" %}
</div>
{% endif %}
{% if recommended == n.pk %}
<div
class=
"label label-success"
>
{% trans "recommended" %}
</div>
{% endif %}
</label>
</label>
<input
id=
"migrate-to-{{n.pk}}"
type=
"radio"
name=
"node"
value=
"{{ n.pk }}"
style=
"float: right;"
<input
id=
"migrate-to-{{n.pk}}"
type=
"radio"
name=
"
to_
node"
value=
"{{ n.pk }}"
style=
"float: right;"
{%
if
current =
=
n
.
pk
%}
disabled=
"disabled"
{%
endif
%}
{%
if
current =
=
n
.
pk
%}
disabled=
"disabled"
{%
endif
%}
{%
if
recommended =
=
n
.
pk
%}
checked=
"checked"
{%
endif
%}
{%
if
recommended =
=
n
.
pk
%}
checked=
"checked"
{%
endif
%}
/>
/>
...
@@ -35,4 +36,5 @@ Choose a compute node to migrate {{obj}} to.
...
@@ -35,4 +36,5 @@ Choose a compute node to migrate {{obj}} to.
{% endfor %}
{% endfor %}
{% endwith %}
{% endwith %}
</ul>
</ul>
{{ form.live_migration|as_crispy_field }}
{% endblock %}
{% endblock %}
circle/dashboard/tests/test_mockedviews.py
View file @
0bfdb2a3
...
@@ -34,6 +34,13 @@ from ..views import AclUpdateView
...
@@ -34,6 +34,13 @@ from ..views import AclUpdateView
from
..
import
views
from
..
import
views
class
QuerySet
(
list
):
model
=
MagicMock
()
def
get
(
self
,
*
args
,
**
kwargs
):
return
self
.
pop
()
class
ViewUserTestCase
(
unittest
.
TestCase
):
class
ViewUserTestCase
(
unittest
.
TestCase
):
def
test_404
(
self
):
def
test_404
(
self
):
...
@@ -145,58 +152,66 @@ class VmOperationViewTestCase(unittest.TestCase):
...
@@ -145,58 +152,66 @@ class VmOperationViewTestCase(unittest.TestCase):
view
.
as_view
()(
request
,
pk
=
1234
)
.
render
()
view
.
as_view
()(
request
,
pk
=
1234
)
.
render
()
def
test_migrate
(
self
):
def
test_migrate
(
self
):
request
=
FakeRequestFactory
(
POST
=
{
'node'
:
1
},
superuser
=
True
)
request
=
FakeRequestFactory
(
POST
=
{
'to_node'
:
1
,
'live_migration'
:
True
},
superuser
=
True
)
view
=
vm_ops
[
'migrate'
]
view
=
vm_ops
[
'migrate'
]
node
=
MagicMock
(
pk
=
1
,
name
=
'node1'
)
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
patch
(
'dashboard.views.util.messages'
)
as
msg
,
\
patch
(
'dashboard.views.util.messages'
)
as
msg
,
\
patch
(
'dashboard.views.vm.get_object_or_404'
)
as
go4
:
patch
.
object
(
view
,
'get_form_kwargs'
)
as
form_kwargs
:
inst
=
MagicMock
(
spec
=
Instance
)
inst
=
MagicMock
(
spec
=
Instance
)
inst
.
_meta
.
object_name
=
"Instance"
inst
.
_meta
.
object_name
=
"Instance"
inst
.
migrate
=
Instance
.
_ops
[
'migrate'
](
inst
)
inst
.
migrate
=
Instance
.
_ops
[
'migrate'
](
inst
)
inst
.
migrate
.
async
=
MagicMock
()
inst
.
migrate
.
async
=
MagicMock
()
inst
.
has_level
.
return_value
=
True
inst
.
has_level
.
return_value
=
True
form_kwargs
.
return_value
=
{
'default'
:
100
,
'choices'
:
QuerySet
([
node
])}
go
.
return_value
=
inst
go
.
return_value
=
inst
go4
.
return_value
=
MagicMock
()
assert
view
.
as_view
()(
request
,
pk
=
1234
)[
'location'
]
assert
view
.
as_view
()(
request
,
pk
=
1234
)[
'location'
]
assert
not
msg
.
error
.
called
assert
not
msg
.
error
.
called
assert
go4
.
called
inst
.
migrate
.
async
.
assert_called_once_with
(
to_node
=
node
,
live_migration
=
True
,
user
=
request
.
user
)
def
test_migrate_failed
(
self
):
def
test_migrate_failed
(
self
):
request
=
FakeRequestFactory
(
POST
=
{
'node'
:
1
},
superuser
=
True
)
request
=
FakeRequestFactory
(
POST
=
{
'
to_
node'
:
1
},
superuser
=
True
)
view
=
vm_ops
[
'migrate'
]
view
=
vm_ops
[
'migrate'
]
node
=
MagicMock
(
pk
=
1
,
name
=
'node1'
)
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
patch
(
'dashboard.views.util.messages'
)
as
msg
,
\
patch
(
'dashboard.views.util.messages'
)
as
msg
,
\
patch
(
'dashboard.views.vm.get_object_or_404'
)
as
go4
:
patch
.
object
(
view
,
'get_form_kwargs'
)
as
form_kwargs
:
inst
=
MagicMock
(
spec
=
Instance
)
inst
=
MagicMock
(
spec
=
Instance
)
inst
.
_meta
.
object_name
=
"Instance"
inst
.
_meta
.
object_name
=
"Instance"
inst
.
migrate
=
Instance
.
_ops
[
'migrate'
](
inst
)
inst
.
migrate
=
Instance
.
_ops
[
'migrate'
](
inst
)
inst
.
migrate
.
async
=
MagicMock
()
inst
.
migrate
.
async
=
MagicMock
()
inst
.
migrate
.
async
.
side_effect
=
Exception
inst
.
migrate
.
async
.
side_effect
=
Exception
inst
.
has_level
.
return_value
=
True
inst
.
has_level
.
return_value
=
True
form_kwargs
.
return_value
=
{
'default'
:
100
,
'choices'
:
QuerySet
([
node
])}
go
.
return_value
=
inst
go
.
return_value
=
inst
go4
.
return_value
=
MagicMock
()
assert
view
.
as_view
()(
request
,
pk
=
1234
)[
'location'
]
assert
view
.
as_view
()(
request
,
pk
=
1234
)[
'location'
]
assert
inst
.
migrate
.
async
.
called
assert
msg
.
error
.
called
assert
msg
.
error
.
called
assert
go4
.
called
def
test_migrate_wo_permission
(
self
):
def
test_migrate_wo_permission
(
self
):
request
=
FakeRequestFactory
(
POST
=
{
'node'
:
1
},
superuser
=
False
)
request
=
FakeRequestFactory
(
POST
=
{
'
to_
node'
:
1
},
superuser
=
False
)
view
=
vm_ops
[
'migrate'
]
view
=
vm_ops
[
'migrate'
]
node
=
MagicMock
(
pk
=
1
,
name
=
'node1'
)
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
patch
(
'dashboard.views.vm.get_object_or_404'
)
as
go4
:
patch
.
object
(
view
,
'get_form_kwargs'
)
as
form_kwargs
:
inst
=
MagicMock
(
spec
=
Instance
)
inst
=
MagicMock
(
spec
=
Instance
)
inst
.
_meta
.
object_name
=
"Instance"
inst
.
_meta
.
object_name
=
"Instance"
inst
.
migrate
=
Instance
.
_ops
[
'migrate'
](
inst
)
inst
.
migrate
=
Instance
.
_ops
[
'migrate'
](
inst
)
inst
.
migrate
.
async
=
MagicMock
()
inst
.
migrate
.
async
=
MagicMock
()
inst
.
has_level
.
return_value
=
True
inst
.
has_level
.
return_value
=
True
form_kwargs
.
return_value
=
{
'default'
:
100
,
'choices'
:
QuerySet
([
node
])}
go
.
return_value
=
inst
go
.
return_value
=
inst
go4
.
return_value
=
MagicMock
()
with
self
.
assertRaises
(
PermissionDenied
):
with
self
.
assertRaises
(
PermissionDenied
):
assert
view
.
as_view
()(
request
,
pk
=
1234
)[
'location'
]
assert
view
.
as_view
()(
request
,
pk
=
1234
)[
'location'
]
assert
go4
.
called
assert
not
inst
.
migrate
.
async
.
called
def
test_migrate_template
(
self
):
def
test_migrate_template
(
self
):
"""check if GET dialog's template can be rendered"""
"""check if GET dialog's template can be rendered"""
...
@@ -303,7 +318,7 @@ class VmMassOperationViewTestCase(unittest.TestCase):
...
@@ -303,7 +318,7 @@ class VmMassOperationViewTestCase(unittest.TestCase):
view
.
as_view
()(
request
,
pk
=
1234
)
.
render
()
view
.
as_view
()(
request
,
pk
=
1234
)
.
render
()
def
test_migrate
(
self
):
def
test_migrate
(
self
):
request
=
FakeRequestFactory
(
POST
=
{
'node'
:
1
},
superuser
=
True
)
request
=
FakeRequestFactory
(
POST
=
{
'
to_
node'
:
1
},
superuser
=
True
)
view
=
vm_mass_ops
[
'migrate'
]
view
=
vm_mass_ops
[
'migrate'
]
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
...
@@ -320,7 +335,7 @@ class VmMassOperationViewTestCase(unittest.TestCase):
...
@@ -320,7 +335,7 @@ class VmMassOperationViewTestCase(unittest.TestCase):
assert
not
msg2
.
error
.
called
assert
not
msg2
.
error
.
called
def
test_migrate_failed
(
self
):
def
test_migrate_failed
(
self
):
request
=
FakeRequestFactory
(
POST
=
{
'node'
:
1
},
superuser
=
True
)
request
=
FakeRequestFactory
(
POST
=
{
'
to_
node'
:
1
},
superuser
=
True
)
view
=
vm_mass_ops
[
'migrate'
]
view
=
vm_mass_ops
[
'migrate'
]
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
with
patch
.
object
(
view
,
'get_object'
)
as
go
,
\
...
@@ -336,7 +351,7 @@ class VmMassOperationViewTestCase(unittest.TestCase):
...
@@ -336,7 +351,7 @@ class VmMassOperationViewTestCase(unittest.TestCase):
assert
msg
.
error
.
called
assert
msg
.
error
.
called
def
test_migrate_wo_permission
(
self
):
def
test_migrate_wo_permission
(
self
):
request
=
FakeRequestFactory
(
POST
=
{
'node'
:
1
},
superuser
=
False
)
request
=
FakeRequestFactory
(
POST
=
{
'
to_
node'
:
1
},
superuser
=
False
)
view
=
vm_mass_ops
[
'migrate'
]
view
=
vm_mass_ops
[
'migrate'
]
with
patch
.
object
(
view
,
'get_object'
)
as
go
:
with
patch
.
object
(
view
,
'get_object'
)
as
go
:
...
...
circle/dashboard/views/vm.py
View file @
0bfdb2a3
...
@@ -60,6 +60,7 @@ from ..forms import (
...
@@ -60,6 +60,7 @@ from ..forms import (
VmAddInterfaceForm
,
VmCreateDiskForm
,
VmDownloadDiskForm
,
VmSaveForm
,
VmAddInterfaceForm
,
VmCreateDiskForm
,
VmDownloadDiskForm
,
VmSaveForm
,
VmRenewForm
,
VmStateChangeForm
,
VmListSearchForm
,
VmCustomizeForm
,
VmRenewForm
,
VmStateChangeForm
,
VmListSearchForm
,
VmCustomizeForm
,
TransferOwnershipForm
,
VmDiskResizeForm
,
RedeployForm
,
VmDiskRemoveForm
,
TransferOwnershipForm
,
VmDiskResizeForm
,
RedeployForm
,
VmDiskRemoveForm
,
VmMigrateForm
,
)
)
from
..models
import
Favourite
,
Profile
from
..models
import
Favourite
,
Profile
...
@@ -421,36 +422,28 @@ class VmDownloadDiskView(FormOperationMixin, VmOperationView):
...
@@ -421,36 +422,28 @@ class VmDownloadDiskView(FormOperationMixin, VmOperationView):
with_reload
=
True
with_reload
=
True
class
VmMigrateView
(
VmOperationView
):
class
VmMigrateView
(
FormOperationMixin
,
VmOperationView
):
op
=
'migrate'
op
=
'migrate'
icon
=
'truck'
icon
=
'truck'
effect
=
'info'
effect
=
'info'
template_name
=
'dashboard/_vm-migrate.html'
template_name
=
'dashboard/_vm-migrate.html'
form_class
=
VmMigrateForm
def
get_context_data
(
self
,
**
kwargs
):
def
get_form_kwargs
(
self
):
ctx
=
super
(
VmMigrateView
,
self
)
.
get_context_data
(
**
kwargs
)
online
=
(
n
.
pk
for
n
in
Node
.
objects
.
filter
(
enabled
=
True
)
if
n
.
online
)
ctx
[
'nodes'
]
=
[
n
for
n
in
Node
.
objects
.
filter
(
enabled
=
True
)
choices
=
Node
.
objects
.
filter
(
pk__in
=
online
)
if
n
.
online
]
default
=
None
inst
=
self
.
get_object
()
inst
=
self
.
get_object
()
ctx
[
"recommended"
]
=
None
try
:
try
:
if
isinstance
(
inst
,
Instance
):
if
isinstance
(
inst
,
Instance
):
ctx
[
"recommended"
]
=
inst
.
select_node
()
.
pk
default
=
inst
.
select_node
()
except
SchedulerError
:
except
SchedulerError
:
logger
.
exception
(
"scheduler error:"
)
logger
.
exception
(
"scheduler error:"
)
return
ctx
val
=
super
(
VmMigrateView
,
self
)
.
get_form_kwargs
()
val
.
update
({
'choices'
:
choices
,
'default'
:
default
})
def
post
(
self
,
request
,
extra
=
None
,
*
args
,
**
kwargs
):
return
val
if
extra
is
None
:
extra
=
{}
node
=
self
.
request
.
POST
.
get
(
"node"
)
if
node
:
node
=
get_object_or_404
(
Node
,
pk
=
node
)
extra
[
"to_node"
]
=
node
return
super
(
VmMigrateView
,
self
)
.
post
(
request
,
extra
,
*
args
,
**
kwargs
)
class
VmSaveView
(
FormOperationMixin
,
VmOperationView
):
class
VmSaveView
(
FormOperationMixin
,
VmOperationView
):
...
@@ -769,6 +762,12 @@ class MassOperationView(OperationView):
...
@@ -769,6 +762,12 @@ class MassOperationView(OperationView):
self
.
check_auth
()
self
.
check_auth
()
if
extra
is
None
:
if
extra
is
None
:
extra
=
{}
extra
=
{}
if
hasattr
(
self
,
'form_class'
):
form
=
self
.
form_class
(
self
.
request
.
POST
,
**
self
.
get_form_kwargs
())
if
form
.
is_valid
():
extra
.
update
(
form
.
cleaned_data
)
self
.
_call_operations
(
extra
)
self
.
_call_operations
(
extra
)
if
request
.
is_ajax
():
if
request
.
is_ajax
():
store
=
messages
.
get_messages
(
request
)
store
=
messages
.
get_messages
(
request
)
...
...
circle/vm/operations.py
View file @
0bfdb2a3
...
@@ -473,10 +473,9 @@ class MigrateOperation(RemoteInstanceOperation):
...
@@ -473,10 +473,9 @@ class MigrateOperation(RemoteInstanceOperation):
remote_queue
=
(
"vm"
,
"slow"
)
remote_queue
=
(
"vm"
,
"slow"
)
remote_timeout
=
1000
remote_timeout
=
1000
def
_get_remote_args
(
self
,
to_node
,
**
kwargs
):
def
_get_remote_args
(
self
,
to_node
,
live_migration
,
**
kwargs
):
return
(
super
(
MigrateOperation
,
self
)
.
_get_remote_args
(
**
kwargs
)
return
(
super
(
MigrateOperation
,
self
)
.
_get_remote_args
(
**
kwargs
)
+
[
to_node
.
host
.
hostname
,
True
])
+
[
to_node
.
host
.
hostname
,
live_migration
])
# TODO handle non-live migration
def
rollback
(
self
,
activity
):
def
rollback
(
self
,
activity
):
with
activity
.
sub_activity
(
with
activity
.
sub_activity
(
...
@@ -484,7 +483,7 @@ class MigrateOperation(RemoteInstanceOperation):
...
@@ -484,7 +483,7 @@ class MigrateOperation(RemoteInstanceOperation):
"redeploy network (rollback)"
)):
"redeploy network (rollback)"
)):
self
.
instance
.
deploy_net
()
self
.
instance
.
deploy_net
()
def
_operation
(
self
,
activity
,
to_node
=
None
):
def
_operation
(
self
,
activity
,
to_node
=
None
,
live_migration
=
True
):
if
not
to_node
:
if
not
to_node
:
with
activity
.
sub_activity
(
'scheduling'
,
with
activity
.
sub_activity
(
'scheduling'
,
readable_name
=
ugettext_noop
(
readable_name
=
ugettext_noop
(
...
@@ -496,7 +495,8 @@ class MigrateOperation(RemoteInstanceOperation):
...
@@ -496,7 +495,8 @@ class MigrateOperation(RemoteInstanceOperation):
with
activity
.
sub_activity
(
with
activity
.
sub_activity
(
'migrate_vm'
,
readable_name
=
create_readable
(
'migrate_vm'
,
readable_name
=
create_readable
(
ugettext_noop
(
"migrate to
%(node)
s"
),
node
=
to_node
)):
ugettext_noop
(
"migrate to
%(node)
s"
),
node
=
to_node
)):
super
(
MigrateOperation
,
self
)
.
_operation
(
to_node
=
to_node
)
super
(
MigrateOperation
,
self
)
.
_operation
(
to_node
=
to_node
,
live_migration
=
live_migration
)
except
Exception
as
e
:
except
Exception
as
e
:
if
hasattr
(
e
,
'libvirtError'
):
if
hasattr
(
e
,
'libvirtError'
):
self
.
rollback
(
activity
)
self
.
rollback
(
activity
)
...
...
circle/vm/tests/test_operations.py
View file @
0bfdb2a3
...
@@ -59,7 +59,8 @@ class MigrateOperationTestCase(TestCase):
...
@@ -59,7 +59,8 @@ class MigrateOperationTestCase(TestCase):
MigrateException
,
op
.
_operation
,
MigrateException
,
op
.
_operation
,
act
,
to_node
=
None
)
act
,
to_node
=
None
)
assert
inst
.
select_node
.
called
assert
inst
.
select_node
.
called
op
.
_get_remote_args
.
assert_called_once_with
(
to_node
=
'test'
)
op
.
_get_remote_args
.
assert_called_once_with
(
to_node
=
'test'
,
live_migration
=
True
)
class
RebootOperationTestCase
(
TestCase
):
class
RebootOperationTestCase
(
TestCase
):
...
...
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