Add APIv2 support to make host optional on evacuate
In the event of an unrecoverable hardware failure, support needs to relocate an instance to another compute so it can be rebuilt. The changes involved in this patch are: [*] Add a new v2 extension to determine that the host argument on evacuate is now optional.(Extended_evacuate_find_host) [*] Doc regeneration. DocImpact: The evacuate target host is now optional. If 'host' field is not sent in the request, the scheduler will determine the target host. This will include nova client changes ( on the proper commit ) to support this new optional parameter. Implements: blueprint find-host-and-evacuate-instance Change-Id: Ib34fb3120263b746ad2f8fe89c14137e36a07a53 Co-Authored-By: Juan M. Olle <juan.m.olle@intel.com> Co-Authored-By: Andres Buraschi <andres.buraschi@intel.com> Co-Authored-By: Anuj Mathur <anujm@thoughtworks.com> Co-Authored-By: Navneet Kumar <navneetk@thoughtworks.com> Co-Authored-By: Claxton Correya <claxton@gmail.com>
This commit is contained in:
parent
c5b418893e
commit
d5a70de479
@ -272,6 +272,14 @@
|
||||
"namespace": "http://docs.openstack.org/compute/ext/evacuate/api/v2",
|
||||
"updated": "2013-01-06T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"alias": "os-extended-evacuate-find-host",
|
||||
"description": "Enables server evacuation without target host. Scheduler will select\n one to target.\n ",
|
||||
"links": [],
|
||||
"name": "ExtendedEvacuateFindHost",
|
||||
"namespace": "http://docs.openstack.org/compute/ext/extended_evacuate_find_host/api/v2",
|
||||
"updated": "2014-02-12T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"alias": "os-extended-floating-ips",
|
||||
"description": "Adds optional fixed_address to the add floating IP command.",
|
||||
|
@ -118,6 +118,11 @@
|
||||
<extension alias="os-evacuate" updated="2013-01-06T00:00:00Z" namespace="http://docs.openstack.org/compute/ext/evacuate/api/v2" name="Evacuate">
|
||||
<description>Enables server evacuation.</description>
|
||||
</extension>
|
||||
<extension alias="os-extended-evacuate-find-host" updated="2014-02-12T00:00:00Z" namespace="http://docs.openstack.org/compute/ext/extended_evacuate_find_host/api/v2" name="ExtendedEvacuateFindHost">
|
||||
<description>Enables server evacuation without target host. Scheduler will select
|
||||
one to target.
|
||||
</description>
|
||||
</extension>
|
||||
<extension alias="os-extended-floating-ips" updated="2013-04-19T00:00:00Z" namespace="http://docs.openstack.org/compute/ext/extended_floating_ips/api/v2" name="ExtendedFloatingIps">
|
||||
<description>Adds optional fixed_address to the add floating IP command.</description>
|
||||
</extension>
|
||||
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"evacuate": {
|
||||
"adminPass": "MySecretPass",
|
||||
"onSharedStorage": "False"
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<evacuate xmlns="http://docs.openstack.org/compute/api/v2"
|
||||
adminPass="MySecretPass"
|
||||
onSharedStorage="False"/>
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"adminPass": "MySecretPass"
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<adminPass>MySecretPass</adminPass>
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"server" : {
|
||||
"name" : "new-server-test",
|
||||
"imageRef" : "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"flavorRef" : "http://openstack.example.com/openstack/flavors/1",
|
||||
"metadata" : {
|
||||
"My Server Name" : "Apache1"
|
||||
},
|
||||
"personality" : [
|
||||
{
|
||||
"path" : "/etc/banner.txt",
|
||||
"contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<server xmlns="http://docs.openstack.org/compute/api/v1.1" imageRef="http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b" flavorRef="http://openstack.example.com/openstack/flavors/1" name="new-server-test">
|
||||
<metadata>
|
||||
<meta key="My Server Name">Apache1</meta>
|
||||
</metadata>
|
||||
<personality>
|
||||
<file path="/etc/banner.txt">
|
||||
ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp
|
||||
dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k
|
||||
IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs
|
||||
c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g
|
||||
QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo
|
||||
ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv
|
||||
dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy
|
||||
c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6
|
||||
b25zLiINCg0KLVJpY2hhcmQgQmFjaA==
|
||||
</file>
|
||||
</personality>
|
||||
</server>
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"server": {
|
||||
"adminPass": "y6hsKno56L6R",
|
||||
"id": "1c650ba2-6a76-41d1-805c-64f4e312200e",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/openstack/servers/1c650ba2-6a76-41d1-805c-64f4e312200e",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/servers/1c650ba2-6a76-41d1-805c-64f4e312200e",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<server xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://docs.openstack.org/compute/api/v1.1" id="b7fe468c-c283-4757-ba25-7e313fa55617" adminPass="CiyJLbaJS3j3">
|
||||
<metadata/>
|
||||
<atom:link href="http://openstack.example.com/v2/openstack/servers/b7fe468c-c283-4757-ba25-7e313fa55617" rel="self"/>
|
||||
<atom:link href="http://openstack.example.com/openstack/servers/b7fe468c-c283-4757-ba25-7e313fa55617" rel="bookmark"/>
|
||||
</server>
|
@ -28,15 +28,17 @@ authorize = extensions.extension_authorizer('compute', 'evacuate')
|
||||
|
||||
|
||||
class Controller(wsgi.Controller):
|
||||
def __init__(self, *args, **kwargs):
|
||||
def __init__(self, ext_mgr, *args, **kwargs):
|
||||
super(Controller, self).__init__(*args, **kwargs)
|
||||
self.compute_api = compute.API()
|
||||
self.host_api = compute.HostAPI()
|
||||
self.ext_mgr = ext_mgr
|
||||
|
||||
@wsgi.action('evacuate')
|
||||
def _evacuate(self, req, id, body):
|
||||
"""Permit admins to evacuate a server from a failed host
|
||||
to a new one.
|
||||
If host is empty, the scheduler will select one.
|
||||
"""
|
||||
context = req.environ["nova.context"]
|
||||
authorize(context)
|
||||
@ -45,12 +47,18 @@ class Controller(wsgi.Controller):
|
||||
raise exc.HTTPBadRequest(_("Malformed request body"))
|
||||
evacuate_body = body["evacuate"]
|
||||
|
||||
host = evacuate_body.get("host")
|
||||
|
||||
if (not host and
|
||||
not self.ext_mgr.is_loaded('os-extended-evacuate-find-host')):
|
||||
msg = _("host must be specified.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
try:
|
||||
host = evacuate_body["host"]
|
||||
on_shared_storage = strutils.bool_from_string(
|
||||
evacuate_body["onSharedStorage"])
|
||||
except (TypeError, KeyError):
|
||||
msg = _("host and onSharedStorage must be specified.")
|
||||
msg = _("onSharedStorage must be specified.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
password = None
|
||||
@ -65,11 +73,12 @@ class Controller(wsgi.Controller):
|
||||
elif not on_shared_storage:
|
||||
password = utils.generate_password()
|
||||
|
||||
try:
|
||||
self.host_api.service_get_by_compute_host(context, host)
|
||||
except exception.NotFound:
|
||||
msg = _("Compute host %s not found.") % host
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
if host is not None:
|
||||
try:
|
||||
self.host_api.service_get_by_compute_host(context, host)
|
||||
except exception.NotFound:
|
||||
msg = _("Compute host %s not found.") % host
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
try:
|
||||
instance = self.compute_api.get(context, id, want_objects=True)
|
||||
@ -99,6 +108,6 @@ class Evacuate(extensions.ExtensionDescriptor):
|
||||
updated = "2013-01-06T00:00:00Z"
|
||||
|
||||
def get_controller_extensions(self):
|
||||
controller = Controller()
|
||||
controller = Controller(self.ext_mgr)
|
||||
extension = extensions.ControllerExtension(self, 'servers', controller)
|
||||
return [extension]
|
||||
|
@ -0,0 +1,26 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from nova.api.openstack import extensions
|
||||
|
||||
|
||||
class Extended_evacuate_find_host(extensions.ExtensionDescriptor):
|
||||
"""Enables server evacuation without target host. Scheduler will select
|
||||
one to target.
|
||||
"""
|
||||
name = "ExtendedEvacuateFindHost"
|
||||
alias = "os-extended-evacuate-find-host"
|
||||
namespace = ("http://docs.openstack.org/compute/ext/"
|
||||
"extended_evacuate_find_host/api/v2")
|
||||
updated = "2014-02-12T00:00:00Z"
|
@ -69,6 +69,11 @@ class EvacuateTest(test.NoDBTestCase):
|
||||
for _method in self._methods:
|
||||
self.stubs.Set(compute_api.API, _method, fake_compute_api)
|
||||
|
||||
self.flags(
|
||||
osapi_compute_extension=[
|
||||
'nova.api.openstack.compute.contrib.select_extensions'],
|
||||
osapi_compute_ext_list=['Evacuate'])
|
||||
|
||||
def _get_admin_context(self, user_id='fake', project_id='fake'):
|
||||
ctxt = context.get_admin_context()
|
||||
ctxt.user_id = user_id
|
||||
|
@ -0,0 +1,114 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
import webob
|
||||
|
||||
from nova.compute import vm_states
|
||||
from nova import context
|
||||
from nova.objects import instance as instance_obj
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova import test
|
||||
from nova.tests.api.openstack import fakes
|
||||
from nova.tests import fake_instance
|
||||
|
||||
|
||||
class ExtendedEvacuateFindHostTest(test.NoDBTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ExtendedEvacuateFindHostTest, self).setUp()
|
||||
self.flags(
|
||||
osapi_compute_extension=[
|
||||
'nova.api.openstack.compute.contrib.select_extensions'],
|
||||
osapi_compute_ext_list=['Extended_evacuate_find_host',
|
||||
'Evacuate'])
|
||||
self.UUID = uuid.uuid4()
|
||||
|
||||
def _get_admin_context(self, user_id='fake', project_id='fake'):
|
||||
ctxt = context.get_admin_context()
|
||||
ctxt.user_id = user_id
|
||||
ctxt.project_id = project_id
|
||||
return ctxt
|
||||
|
||||
def _fake_compute_api(*args, **kwargs):
|
||||
return True
|
||||
|
||||
def _fake_compute_api_get(self, context, instance_id, **kwargs):
|
||||
instance = fake_instance.fake_db_instance(id=1, uuid=uuid,
|
||||
task_state=None,
|
||||
host='host1',
|
||||
vm_state=vm_states.ACTIVE)
|
||||
instance = instance_obj.Instance._from_db_object(context,
|
||||
instance_obj.Instance(),
|
||||
instance)
|
||||
return instance
|
||||
|
||||
def _fake_service_get_by_compute_host(self, context, host):
|
||||
return {'host_name': host,
|
||||
'service': 'compute',
|
||||
'zone': 'nova'
|
||||
}
|
||||
|
||||
@mock.patch('nova.compute.api.HostAPI.service_get_by_compute_host')
|
||||
@mock.patch('nova.compute.api.API.get')
|
||||
@mock.patch('nova.compute.api.API.evacuate')
|
||||
def test_evacuate_instance_with_no_target(self, evacuate_mock,
|
||||
api_get_mock,
|
||||
service_get_mock):
|
||||
service_get_mock.side_effects = self._fake_service_get_by_compute_host
|
||||
api_get_mock.side_effects = self._fake_compute_api_get
|
||||
evacuate_mock.side_effects = self._fake_compute_api
|
||||
|
||||
ctxt = self._get_admin_context()
|
||||
app = fakes.wsgi_app(fake_auth_context=ctxt)
|
||||
req = webob.Request.blank('/v2/fake/servers/%s/action' % self.UUID)
|
||||
req.method = 'POST'
|
||||
req.body = jsonutils.dumps({
|
||||
'evacuate': {
|
||||
'onSharedStorage': 'False',
|
||||
'adminPass': 'MyNewPass'
|
||||
}
|
||||
})
|
||||
req.content_type = 'application/json'
|
||||
res = req.get_response(app)
|
||||
self.assertEqual(200, res.status_int)
|
||||
evacuate_mock.assert_called_once_with(mock.ANY, mock.ANY, None,
|
||||
mock.ANY, mock.ANY)
|
||||
|
||||
@mock.patch('nova.compute.api.HostAPI.service_get_by_compute_host')
|
||||
@mock.patch('nova.compute.api.API.get')
|
||||
def test_no_target_fails_if_extension_not_loaded(self, api_get_mock,
|
||||
service_get_mock):
|
||||
self.flags(
|
||||
osapi_compute_extension=[
|
||||
'nova.api.openstack.compute.contrib.select_extensions'],
|
||||
osapi_compute_ext_list=['Evacuate'])
|
||||
service_get_mock.side_effects = self._fake_service_get_by_compute_host
|
||||
api_get_mock.side_effects = self._fake_compute_api_get
|
||||
|
||||
ctxt = self._get_admin_context()
|
||||
app = fakes.wsgi_app(fake_auth_context=ctxt)
|
||||
req = webob.Request.blank('/v2/fake/servers/%s/action' % self.UUID)
|
||||
req.method = 'POST'
|
||||
req.body = jsonutils.dumps({
|
||||
'evacuate': {
|
||||
'onSharedStorage': 'False',
|
||||
'adminPass': 'MyNewPass'
|
||||
}
|
||||
})
|
||||
req.content_type = 'application/json'
|
||||
res = req.get_response(app)
|
||||
self.assertEqual(400, res.status_int)
|
@ -671,6 +671,14 @@
|
||||
"name": "ServerGroups",
|
||||
"namespace": "http://docs.openstack.org/compute/ext/servergroups/api/v2",
|
||||
"updated": "%(isotime)s"
|
||||
},
|
||||
{
|
||||
"alias": "os-extended-evacuate-find-host",
|
||||
"description": "%(text)s",
|
||||
"links": [],
|
||||
"name": "ExtendedEvacuateFindHost",
|
||||
"namespace": "http://docs.openstack.org/compute/ext/extended_evacuate_find_host/api/v2",
|
||||
"updated": "%(isotime)s"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -251,4 +251,7 @@
|
||||
<extension alias="os-server-groups" updated="%(isotime)s" namespace="http://docs.openstack.org/compute/ext/servergroups/api/v2" name="ServerGroups">
|
||||
<description>%(text)s</description>
|
||||
</extension>
|
||||
<extension alias="os-extended-evacuate-find-host" updated="%(isotime)s" namespace="http://docs.openstack.org/compute/ext/extended_evacuate_find_host/api/v2" name="ExtendedEvacuateFindHost">
|
||||
<description>%(text)s</description>
|
||||
</extension>
|
||||
</extensions>
|
||||
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"evacuate": {
|
||||
"adminPass": "%(adminPass)s",
|
||||
"onSharedStorage": "%(onSharedStorage)s"
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<evacuate xmlns="http://docs.openstack.org/compute/api/v2"
|
||||
adminPass="%(adminPass)s"
|
||||
onSharedStorage="%(onSharedStorage)s"/>
|
||||
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"adminPass": "%(password)s"
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<adminPass>%(password)s</adminPass>
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"server" : {
|
||||
"name" : "new-server-test",
|
||||
"imageRef" : "%(host)s/openstack/images/%(image_id)s",
|
||||
"flavorRef" : "%(host)s/openstack/flavors/1",
|
||||
"metadata" : {
|
||||
"My Server Name" : "Apache1"
|
||||
},
|
||||
"personality" : [
|
||||
{
|
||||
"path" : "/etc/banner.txt",
|
||||
"contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<server xmlns="http://docs.openstack.org/compute/api/v1.1" imageRef="%(host)s/openstack/images/%(image_id)s" flavorRef="%(host)s/openstack/flavors/1" name="new-server-test">
|
||||
<metadata>
|
||||
<meta key="My Server Name">Apache1</meta>
|
||||
</metadata>
|
||||
<personality>
|
||||
<file path="/etc/banner.txt">
|
||||
ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp
|
||||
dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k
|
||||
IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs
|
||||
c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g
|
||||
QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo
|
||||
ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv
|
||||
dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy
|
||||
c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6
|
||||
b25zLiINCg0KLVJpY2hhcmQgQmFjaA==
|
||||
</file>
|
||||
</personality>
|
||||
</server>
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"server": {
|
||||
"adminPass": "%(password)s",
|
||||
"id": "%(id)s",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(host)s/v2/openstack/servers/%(uuid)s",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "%(host)s/openstack/servers/%(uuid)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<server xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://docs.openstack.org/compute/api/v1.1" id="%(id)s" adminPass="%(password)s">
|
||||
<metadata/>
|
||||
<atom:link href="%(host)s/v2/openstack/servers/%(uuid)s" rel="self"/>
|
||||
<atom:link href="%(host)s/openstack/servers/%(uuid)s" rel="bookmark"/>
|
||||
</server>
|
@ -3238,6 +3238,53 @@ class EvacuateXmlTest(EvacuateJsonTest):
|
||||
ctype = 'xml'
|
||||
|
||||
|
||||
class EvacuateFindHostSampleJsonTest(ServersSampleBase):
|
||||
extends_name = ("nova.api.openstack.compute.contrib"
|
||||
".evacuate.Evacuate")
|
||||
|
||||
extension_name = ("nova.api.openstack.compute.contrib"
|
||||
".extended_evacuate_find_host.Extended_evacuate_find_host")
|
||||
|
||||
@mock.patch('nova.compute.manager.ComputeManager._check_instance_exists')
|
||||
@mock.patch('nova.compute.api.HostAPI.service_get_by_compute_host')
|
||||
@mock.patch('nova.conductor.manager.ComputeTaskManager.rebuild_instance')
|
||||
def test_server_evacuate(self, rebuild_mock, service_get_mock,
|
||||
check_instance_mock):
|
||||
self.uuid = self._post_server()
|
||||
|
||||
req_subs = {
|
||||
"adminPass": "MySecretPass",
|
||||
"onSharedStorage": 'False'
|
||||
}
|
||||
|
||||
check_instance_mock.return_value = False
|
||||
|
||||
def fake_service_get_by_compute_host(self, context, host):
|
||||
return {
|
||||
'host_name': host,
|
||||
'service': 'compute',
|
||||
'zone': 'nova'
|
||||
}
|
||||
service_get_mock.side_effect = fake_service_get_by_compute_host
|
||||
with mock.patch.object(service_group_api.API, 'service_is_up',
|
||||
return_value=False):
|
||||
response = self._do_post('servers/%s/action' % self.uuid,
|
||||
'server-evacuate-find-host-req', req_subs)
|
||||
subs = self._get_regexes()
|
||||
self._verify_response('server-evacuate-find-host-resp', subs,
|
||||
response, 200)
|
||||
rebuild_mock.assert_called_once_with(mock.ANY, instance=mock.ANY,
|
||||
orig_image_ref=mock.ANY, image_ref=mock.ANY,
|
||||
injected_files=mock.ANY, new_pass="MySecretPass",
|
||||
orig_sys_metadata=mock.ANY, bdms=mock.ANY, recreate=mock.ANY,
|
||||
on_shared_storage=False, preserve_ephemeral=mock.ANY,
|
||||
host=None)
|
||||
|
||||
|
||||
class EvacuateFindHostSampleXmlTests(EvacuateFindHostSampleJsonTest):
|
||||
ctype = "xml"
|
||||
|
||||
|
||||
class FloatingIpDNSJsonTest(ApiSampleTestBaseV2):
|
||||
extension_name = ("nova.api.openstack.compute.contrib.floating_ip_dns."
|
||||
"Floating_ip_dns")
|
||||
|
Loading…
x
Reference in New Issue
Block a user