Allow user to specify image to use during rescue - V2 API changes

This commit includes the V2 changes.
Making changes in v2 API to take an extra parameter rescue_image_ref
as part of the rescue action API.
If specified, this image_ref will be used to rescue the instance.
If the image is not specified, then the base image ref is used by
default.

Partially Implements: blueprint allow-image-to-be-specified-during-rescue

DocImpact

Change-Id: I985b21264841a6a18a66d19ccd41753f78576776
This commit is contained in:
Aditi Raveesh 2014-03-05 12:30:37 +05:30 committed by Gerrit Code Review
parent e88fc374c6
commit ff5547883b
29 changed files with 442 additions and 5 deletions

View File

@ -512,6 +512,14 @@
"namespace": "http://docs.openstack.org/compute/ext/rescue/api/v1.1",
"updated": "2011-08-18T00:00:00+00:00"
},
{
"alias": "os-extended-rescue-with-image",
"description": "Rescue instance with the image specified.",
"links": [],
"name": "ExtendedRescueWithImage",
"namespace": "http://docs.openstack.org/compute/ext/extended_rescue_with_image/api/v2",
"updated": "2014-03-05T00:00:00+00:00"
},
{
"alias": "os-security-group-default-rules",
"description": "Default rules for security group support.",

View File

@ -212,6 +212,9 @@
<extension alias="os-rescue" updated="2011-08-18T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/rescue/api/v1.1" name="Rescue">
<description>Instance rescue mode.</description>
</extension>
<extension alias="os-extended-rescue-with-image" updated="2014-03-05T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/extended_rescue_with_image/api/v2" name="ExtendedRescueWithImage">
<description>Rescue instance with the image specified.</description>
</extension>
<extension alias="os-security-group-default-rules" updated="2013-02-05T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/securitygroupdefaultrules/api/v1.1" name="SecurityGroupDefaultRules">
<description>Default rules for security group support.</description>
</extension>

View File

@ -0,0 +1,53 @@
{
"server": {
"accessIPv4": "",
"accessIPv6": "",
"addresses": {
"private": [
{
"addr": "192.168.0.3",
"version": 4
}
]
},
"created": "2012-09-19T09:22:27Z",
"flavor": {
"id": "1",
"links": [
{
"href": "http://openstack.example.com/openstack/flavors/1",
"rel": "bookmark"
}
]
},
"hostId": "c02c15d17499304e9893d9fc41c415f5096f8aa880bc651cac092650",
"id": "7e21a264-6cc1-4d90-b7b5-f9f49cec3a85",
"image": {
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
"links": [
{
"href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
"rel": "bookmark"
}
]
},
"links": [
{
"href": "http://openstack.example.com/v2/openstack/servers/7e21a264-6cc1-4d90-b7b5-f9f49cec3a85",
"rel": "self"
},
{
"href": "http://openstack.example.com/openstack/servers/7e21a264-6cc1-4d90-b7b5-f9f49cec3a85",
"rel": "bookmark"
}
],
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"status": "RESCUE",
"tenant_id": "openstack",
"updated": "2012-09-19T09:22:27Z",
"user_id": "fake"
}
}

View File

@ -0,0 +1,19 @@
<?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" status="RESCUE" updated="2012-09-19T11:44:22Z" hostId="8eb4062a49009f6ee8066fbe1ed4c60d67b0a4ebd4434f51d8f03293" name="new-server-test" created="2012-09-19T11:44:22Z" userId="fake" tenantId="openstack" accessIPv4="" accessIPv6="" id="eb2b2fbb-a6f8-4363-9007-e9a9098ea080">
<image id="70a599e0-31e7-49b7-b260-868f441e862b">
<atom:link href="http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b" rel="bookmark"/>
</image>
<flavor id="1">
<atom:link href="http://openstack.example.com/openstack/flavors/1" rel="bookmark"/>
</flavor>
<metadata>
<meta key="My Server Name">Apache1</meta>
</metadata>
<addresses>
<network id="private">
<ip version="4" addr="192.168.0.3"/>
</network>
</addresses>
<atom:link href="http://openstack.example.com/v2/openstack/servers/eb2b2fbb-a6f8-4363-9007-e9a9098ea080" rel="self"/>
<atom:link href="http://openstack.example.com/openstack/servers/eb2b2fbb-a6f8-4363-9007-e9a9098ea080" rel="bookmark"/>
</server>

View File

@ -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=="
}
]
}
}

View File

@ -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>

View File

@ -0,0 +1,16 @@
{
"server": {
"adminPass": "xjDVAYHmc34s",
"id": "784f5005-bec9-4c22-8c42-5a7dcba88d82",
"links": [
{
"href": "http://openstack.example.com/v2/openstack/servers/784f5005-bec9-4c22-8c42-5a7dcba88d82",
"rel": "self"
},
{
"href": "http://openstack.example.com/openstack/servers/784f5005-bec9-4c22-8c42-5a7dcba88d82",
"rel": "bookmark"
}
]
}
}

View File

@ -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="70b4263f-bae1-4ac4-a1c5-e5bb193767bc" adminPass="ttv8YPD3tRPm">
<metadata/>
<atom:link href="http://openstack.example.com/v2/openstack/servers/70b4263f-bae1-4ac4-a1c5-e5bb193767bc" rel="self"/>
<atom:link href="http://openstack.example.com/openstack/servers/70b4263f-bae1-4ac4-a1c5-e5bb193767bc" rel="bookmark"/>
</server>

View File

@ -0,0 +1,6 @@
{
"rescue": {
"adminPass": "MySecretPass",
"rescue_image_ref": "70a599e0-31e7-49b7-b260-868f441e862b"
}
}

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<rescue xmlns="http://docs.openstack.org/compute/api/v1.1"
adminPass="MySecretPass"/>

View File

@ -0,0 +1,3 @@
{
"adminPass": "MySecretPass"
}

View File

@ -0,0 +1,2 @@
<?xml version='1.0' encoding='UTF-8'?>
<adminPass>MySecretPass</adminPass>

View File

@ -0,0 +1,25 @@
# Copyright 2014 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.
from nova.api.openstack import extensions as exts
class Extended_rescue_with_image(exts.ExtensionDescriptor):
"""Allow the user to specify the image to use for rescue."""
name = "ExtendedRescueWithImage"
alias = "os-extended-rescue-with-image"
namespace = ("http://docs.openstack.org/compute/ext/"
"extended_rescue_with_image/api/v2")
updated = "2014-01-04T00:00:00+00:00"

View File

@ -32,9 +32,10 @@ authorize = exts.extension_authorizer('compute', 'rescue')
class RescueController(wsgi.Controller):
def __init__(self, *args, **kwargs):
def __init__(self, ext_mgr, *args, **kwargs):
super(RescueController, self).__init__(*args, **kwargs)
self.compute_api = compute.API()
self.ext_mgr = ext_mgr
def _get_instance(self, context, instance_id, want_objects=False):
try:
@ -57,8 +58,12 @@ class RescueController(wsgi.Controller):
instance = self._get_instance(context, id, want_objects=True)
try:
rescue_image_ref = None
if self.ext_mgr.is_loaded("os-extended-rescue-with-image"):
if body['rescue'] and 'rescue_image_ref' in body['rescue']:
rescue_image_ref = body['rescue']['rescue_image_ref']
self.compute_api.rescue(context, instance,
rescue_password=password)
rescue_password=password, rescue_image_ref=rescue_image_ref)
except exception.InstanceIsLocked as e:
raise exc.HTTPConflict(explanation=e.format_message())
except exception.InstanceInvalidState as state_error:
@ -105,6 +110,6 @@ class Rescue(exts.ExtensionDescriptor):
updated = "2011-08-18T00:00:00+00:00"
def get_controller_extensions(self):
controller = RescueController()
controller = RescueController(self.ext_mgr)
extension = exts.ControllerExtension(self, 'servers', controller)
return [extension]

View File

@ -0,0 +1,58 @@
# Copyright 2014 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 mock
from oslo.config import cfg
from nova.api.openstack.compute.contrib import rescue
from nova.api.openstack import extensions
from nova import compute
import nova.context as context
from nova import test
CONF = cfg.CONF
CONF.import_opt('password_length', 'nova.utils')
class FakeRequest(object):
def __init__(self, context):
self.environ = {"nova.context": context}
class ExtendedRescueWithImageTest(test.NoDBTestCase):
def setUp(self):
super(ExtendedRescueWithImageTest, self).setUp()
ext_mgr = extensions.ExtensionManager()
ext_mgr.extensions = {'os-extended-rescue-with-image': 'fake'}
self.controller = rescue.RescueController(ext_mgr)
@mock.patch.object(compute.api.API, "rescue")
def _make_rescue_request_with_image_ref(self, body, mock_rescue):
instance = "instance"
self.controller._get_instance = mock.Mock(return_value=instance)
fake_context = context.RequestContext('fake', 'fake')
req = FakeRequest(fake_context)
self.controller._rescue(req, "id", body)
rescue_image_ref = body["rescue"].get("rescue_image_ref")
mock_rescue.assert_called_with(mock.ANY, mock.ANY,
rescue_password=mock.ANY, rescue_image_ref=rescue_image_ref)
def test_rescue_with_image_specified(self):
body = dict(rescue={"rescue_image_ref": "image-ref"})
self._make_rescue_request_with_image_ref(body)
def test_rescue_without_image_specified(self):
body = dict(rescue={})
self._make_rescue_request_with_image_ref(body)

View File

@ -26,7 +26,8 @@ CONF = cfg.CONF
CONF.import_opt('password_length', 'nova.utils')
def rescue(self, context, instance, rescue_password=None):
def rescue(self, context, instance, rescue_password=None,
rescue_image_ref=None):
pass
@ -53,7 +54,7 @@ class RescueTest(test.NoDBTestCase):
def test_rescue_from_locked_server(self):
def fake_rescue_from_locked_server(self, context,
instance, rescue_password=None):
instance, rescue_password=None, rescue_image_ref=None):
raise exception.InstanceIsLocked(instance_uuid=instance['uuid'])
self.stubs.Set(compute.api.API,

View File

@ -504,6 +504,14 @@
"namespace": "http://docs.openstack.org/compute/ext/rescue/api/v1.1",
"updated": "%(timestamp)s"
},
{
"alias": "os-extended-rescue-with-image",
"description": "%(text)s",
"links": [],
"name": "ExtendedRescueWithImage",
"namespace": "http://docs.openstack.org/compute/ext/extended_rescue_with_image/api/v2",
"updated": "%(timestamp)s"
},
{
"alias": "os-security-group-default-rules",
"description": "%(text)s",

View File

@ -180,6 +180,9 @@
<extension alias="os-rescue" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/rescue/api/v1.1" name="Rescue">
<description>%(text)s</description>
</extension>
<extension alias="os-extended-rescue-with-image" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/extended_rescue_with_image/api/v2" name="ExtendedRescueWithImage">
<description>%(text)s</description>
</extension>
<extension alias="os-security-group-default-rules" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/securitygroupdefaultrules/api/v1.1" name="SecurityGroupDefaultRules">
<description>%(text)s</description>
</extension>

View File

@ -0,0 +1,53 @@
{
"server": {
"accessIPv4": "",
"accessIPv6": "",
"addresses": {
"private": [
{
"addr": "%(ip)s",
"version": 4
}
]
},
"created": "%(timestamp)s",
"flavor": {
"id": "1",
"links": [
{
"href": "%(host)s/openstack/flavors/1",
"rel": "bookmark"
}
]
},
"hostId": "%(hostid)s",
"id": "%(id)s",
"image": {
"id": "%(uuid)s",
"links": [
{
"href": "%(host)s/openstack/images/%(uuid)s",
"rel": "bookmark"
}
]
},
"links": [
{
"href": "%(host)s/v2/openstack/servers/%(id)s",
"rel": "self"
},
{
"href": "%(host)s/openstack/servers/%(id)s",
"rel": "bookmark"
}
],
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"status": "%(status)s",
"tenant_id": "openstack",
"updated": "%(timestamp)s",
"user_id": "fake"
}
}

View File

@ -0,0 +1,19 @@
<?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" status="%(status)s" updated="%(timestamp)s" hostId="%(hostid)s" name="new-server-test" created="%(timestamp)s" userId="fake" tenantId="openstack" accessIPv4="" accessIPv6="" id="%(id)s">
<image id="%(uuid)s">
<atom:link href="%(host)s/openstack/images/%(uuid)s" rel="bookmark"/>
</image>
<flavor id="1">
<atom:link href="%(host)s/openstack/flavors/1" rel="bookmark"/>
</flavor>
<metadata>
<meta key="My Server Name">Apache1</meta>
</metadata>
<addresses>
<network id="private">
<ip version="4" addr="%(ip)s"/>
</network>
</addresses>
<atom:link href="%(host)s/v2/openstack/servers/%(id)s" rel="self"/>
<atom:link href="%(host)s/openstack/servers/%(id)s" rel="bookmark"/>
</server>

View File

@ -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=="
}
]
}
}

View File

@ -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>

View File

@ -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"
}
]
}
}

View File

@ -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>

View File

@ -0,0 +1,6 @@
{
"rescue": {
"adminPass": "%(password)s",
"rescue_image_ref" : "%(rescue_image_ref)s"
}
}

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<rescue xmlns="http://docs.openstack.org/compute/api/v1.1"
adminPass="%(password)s" rescue_image_ref="%(rescue_image_ref)s"/>

View File

@ -0,0 +1,3 @@
{
"adminPass": "%(password)s"
}

View File

@ -0,0 +1,2 @@
<?xml version='1.0' encoding='UTF-8'?>
<adminPass>%(password)s</adminPass>

View File

@ -1174,6 +1174,46 @@ class RescueXmlTest(RescueJsonTest):
ctype = 'xml'
class ExtendedRescueWithImageJsonTest(ServersSampleBase):
extension_name = ("nova.api.openstack.compute.contrib"
".extended_rescue_with_image.Extended_rescue_with_image")
def _get_flags(self):
f = super(ExtendedRescueWithImageJsonTest, self)._get_flags()
f['osapi_compute_extension'] = CONF.osapi_compute_extension[:]
# ExtendedRescueWithImage extension also needs Rescue to be loaded.
f['osapi_compute_extension'].append(
'nova.api.openstack.compute.contrib.rescue.Rescue')
return f
def _rescue(self, uuid):
req_subs = {
'password': 'MySecretPass',
'rescue_image_ref': fake.get_valid_image_id()
}
response = self._do_post('servers/%s/action' % uuid,
'server-rescue-req', req_subs)
self._verify_response('server-rescue', req_subs, response, 200)
def test_server_rescue(self):
uuid = self._post_server()
self._rescue(uuid)
# Do a server get to make sure that the 'RESCUE' state is set
response = self._do_get('servers/%s' % uuid)
subs = self._get_regexes()
subs['hostid'] = '[a-f0-9]+'
subs['id'] = uuid
subs['status'] = 'RESCUE'
self._verify_response('server-get-resp-rescue', subs, response, 200)
class ExtendedRescueWithImageXmlTest(ExtendedRescueWithImageJsonTest):
ctype = 'xml'
class ShelveJsonTest(ServersSampleBase):
extension_name = "nova.api.openstack.compute.contrib.shelve.Shelve"