Add preserve_ephemeral option to rebuild

The preserve_ephemeral option for rebuild will preserve the content of
the ephemeral partition, making stateful golden image based
deployments possible even when clouds haven't deployed Cinder, or the
hypervisor doesn't support Cinder (e.g. BareMetal / Ironic).

Partial-Bug: #1174154
blueprint: baremetal-preserve-ephemeral
Co-Authored-By: Robert Collins <rbtcollins@hp.com>

Change-Id: Id33d5d4107f89814842a3f0b7f33690dd7e3aadc
This commit is contained in:
Roman Podoliaka 2013-12-05 17:17:36 +02:00
parent 5c1cac0cde
commit c557472496
41 changed files with 885 additions and 15 deletions

View File

@ -464,6 +464,14 @@
"namespace": "http://docs.openstack.org/compute/ext/networks_associate/api/v2",
"updated": "2012-11-19T00:00:00+00:00"
},
{
"alias": "os-preserve-ephemeral-rebuild",
"description": "Allow preservation of the ephemeral partition on rebuild.",
"links": [],
"name": "PreserveEphemeralOnRebuild",
"namespace": "http://docs.openstack.org/compute/ext/preserve_ephemeral_rebuild/api/v2",
"updated": "2013-12-17T00:00:00+00:00"
},
{
"alias": "os-quota-class-sets",
"description": "Quota classes management support.",

View File

@ -194,6 +194,9 @@
<extension alias="os-networks-associate" updated="2012-11-19T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/networks_associate/api/v2" name="NetworkAssociationSupport">
<description>Network association support.</description>
</extension>
<extension alias="os-preserve-ephemeral-rebuild" updated="2013-12-17T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/preserve_ephemeral_rebuild/api/v2" name="PreserveEphemeralOnRebuild">
<description>Allow preservation of the ephemeral partition on rebuild.</description>
</extension>
<extension alias="os-quota-class-sets" updated="2012-03-12T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1" name="QuotaClasses">
<description>Quota classes management support.</description>
</extension>

View File

@ -0,0 +1,55 @@
{
"server": {
"accessIPv4": "1.2.3.4",
"accessIPv6": "fe80::100",
"addresses": {
"private": [
{
"addr": "192.168.0.3",
"version": 4
}
]
},
"adminPass": "seekr3t",
"created": "2013-12-30T12:28:14Z",
"flavor": {
"id": "1",
"links": [
{
"href": "http://openstack.example.com/openstack/flavors/1",
"rel": "bookmark"
}
]
},
"hostId": "ee8ea077f8548ce25c59c2d5020d0f82810c815c210fd68194a5c0f8",
"id": "810e78d5-47fe-48bf-9559-bfe5dc918685",
"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/810e78d5-47fe-48bf-9559-bfe5dc918685",
"rel": "self"
},
{
"href": "http://openstack.example.com/openstack/servers/810e78d5-47fe-48bf-9559-bfe5dc918685",
"rel": "bookmark"
}
],
"metadata": {
"meta var": "meta val"
},
"name": "foobar",
"progress": 0,
"status": "ACTIVE",
"tenant_id": "openstack",
"updated": "2013-12-30T12:28:15Z",
"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="ACTIVE" updated="2013-12-30T12:28:05Z" hostId="29cf2103bf26495b084577ca87c0134abf5e2c46f284403a2637aebf" name="foobar" created="2013-12-30T12:28:04Z" userId="fake" tenantId="openstack" accessIPv4="1.2.3.4" accessIPv6="fe80::100" progress="0" id="26248125-6c64-4ef4-9d14-5f63ba1f8a60" adminPass="seekr3t">
<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/26248125-6c64-4ef4-9d14-5f63ba1f8a60" rel="self"/>
<atom:link href="http://openstack.example.com/openstack/servers/26248125-6c64-4ef4-9d14-5f63ba1f8a60" rel="bookmark"/>
</server>

View File

@ -0,0 +1,19 @@
{
"rebuild" : {
"imageRef" : "http://openstack.example.com/v2/32278/images/70a599e0-31e7-49b7-b260-868f441e862b",
"name" : "new-server-test",
"adminPass" : "seekr3t",
"accessIPv4" : "1.2.3.4",
"accessIPv6" : "fe80::100",
"metadata" : {
"meta var" : "meta val"
},
"personality" : [
{
"path" : "/etc/banner.txt",
"contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
}
],
"preserve_ephemeral": true
}
}

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<rebuild
xmlns="http://docs.openstack.org/compute/api/v1.1"
name="foobar"
imageRef="http://openstack.example.com/v1.1/32278/images/70a599e0-31e7-49b7-b260-868f441e862b"
accessIPv4="1.2.3.4"
accessIPv6="fe80::100"
adminPass="seekr3t"
preserve_ephemeral="false">
<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>
</rebuild>

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": "8SV3AwzrE9Yn",
"id": "5b39f651-2c68-48a1-ad26-dab135c4f543",
"links": [
{
"href": "http://openstack.example.com/v2/openstack/servers/5b39f651-2c68-48a1-ad26-dab135c4f543",
"rel": "self"
},
{
"href": "http://openstack.example.com/openstack/servers/5b39f651-2c68-48a1-ad26-dab135c4f543",
"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="26248125-6c64-4ef4-9d14-5f63ba1f8a60" adminPass="MAvcr8ZtqSHf">
<metadata/>
<atom:link href="http://openstack.example.com/v2/openstack/servers/26248125-6c64-4ef4-9d14-5f63ba1f8a60" rel="self"/>
<atom:link href="http://openstack.example.com/openstack/servers/26248125-6c64-4ef4-9d14-5f63ba1f8a60" rel="bookmark"/>
</server>

View File

@ -0,0 +1,17 @@
{
"rebuild" : {
"image_ref" : "http://glance.openstack.example.com/images/70a599e0-31e7-49b7-b260-868f441e862b",
"name" : "foobar",
"admin_password" : "seekr3t",
"metadata" : {
"meta_var" : "meta_val"
},
"personality" : [
{
"path" : "/etc/banner.txt",
"contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
}
],
"preserve_ephemeral": true
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<rebuild
xmlns="http://docs.openstack.org/compute/api/v1.1"
name="foobar"
image_ref="http://glance.openstack.example.com/images/70a599e0-31e7-49b7-b260-868f441e862b"
admin_password="seekr3t"
preserve_ephemeral="true">
<metadata>
<meta key="meta_var">meta_val</meta>
</metadata>
<personality>
<file path="/etc/banner.txt">
ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp
dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k
IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs
c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g
QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo
ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv
dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy
c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6
b25zLiINCg0KLVJpY2hhcmQgQmFjaA==
</file>
</personality>
</rebuild>

View File

@ -0,0 +1,23 @@
# 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 Preserve_ephemeral_rebuild(extensions.ExtensionDescriptor):
"""Allow preservation of the ephemeral partition on rebuild."""
name = "PreserveEphemeralOnRebuild"
alias = "os-preserve-ephemeral-rebuild"
namespace = ("http://docs.openstack.org/compute/ext/"
"preserve_ephemeral_rebuild/api/v2")
updated = "2013-12-17T00:00:00+00:00"

View File

@ -250,6 +250,10 @@ class ActionDeserializer(CommonDeserializer):
if node.hasAttribute("admin_password"):
rebuild["admin_password"] = node.getAttribute("admin_password")
if node.hasAttribute("preserve_ephemeral"):
rebuild["preserve_ephemeral"] = strutils.bool_from_string(
node.getAttribute("preserve_ephemeral"), strict=True)
if self.controller:
self.controller.server_rebuild_xml_deserialize(node, rebuild)
return rebuild
@ -1110,10 +1114,15 @@ class ServersController(wsgi.Controller):
'metadata': 'metadata',
}
rebuild_kwargs = {}
if 'name' in rebuild_dict:
self._validate_server_name(rebuild_dict['name'])
rebuild_kwargs = {}
if 'preserve_ephemeral' in rebuild_dict:
rebuild_kwargs['preserve_ephemeral'] = strutils.bool_from_string(
rebuild_dict['preserve_ephemeral'], strict=True)
if list(self.rebuild_extension_manager):
self.rebuild_extension_manager.map(self._rebuild_extension_point,
rebuild_dict, rebuild_kwargs)

View File

@ -413,6 +413,10 @@ class ActionDeserializer(CommonDeserializer):
if node.hasAttribute("accessIPv6"):
rebuild["accessIPv6"] = node.getAttribute("accessIPv6")
if node.hasAttribute("preserve_ephemeral"):
rebuild["preserve_ephemeral"] = strutils.bool_from_string(
node.getAttribute("preserve_ephemeral"), strict=True)
return rebuild
def _action_resize(self, node):
@ -1319,6 +1323,15 @@ class Controller(wsgi.Controller):
'auto_disk_config': 'auto_disk_config',
}
kwargs = {}
# take the preserve_ephemeral value into account only when the
# corresponding extension is active
if (self.ext_mgr.is_loaded('os-preserve-ephemeral-rebuild')
and 'preserve_ephemeral' in body):
kwargs['preserve_ephemeral'] = strutils.bool_from_string(
body['preserve_ephemeral'], strict=True)
if 'accessIPv4' in body:
self._validate_access_ipv4(body['accessIPv4'])
@ -1328,8 +1341,6 @@ class Controller(wsgi.Controller):
if 'name' in body:
self._validate_server_name(body['name'])
kwargs = {}
for request_attribute, instance_attribute in attr_map.items():
try:
kwargs[instance_attribute] = body[request_attribute]

View File

@ -577,7 +577,7 @@ class CellsAPI(rpcclient.RpcProxy):
def rebuild_instance(self, ctxt, instance, new_pass, injected_files,
image_ref, orig_image_ref, orig_sys_metadata, bdms,
recreate=False, on_shared_storage=False, host=None,
kwargs=None):
preserve_ephemeral=False, kwargs=None):
if not CONF.cells.enable:
return
@ -585,4 +585,4 @@ class CellsAPI(rpcclient.RpcProxy):
cctxt.cast(ctxt, 'rebuild_instance',
instance=instance, image_href=image_ref,
admin_password=new_pass, files_to_inject=injected_files,
kwargs=kwargs)
preserve_ephemeral=preserve_ephemeral, kwargs=kwargs)

View File

@ -2060,6 +2060,7 @@ class API(base.Base):
orig_image_ref = instance.image_ref or ''
files_to_inject = files_to_inject or []
metadata = kwargs.get('metadata', {})
preserve_ephemeral = kwargs.get('preserve_ephemeral', False)
image_id, image = self._get_image(context, image_href)
self._check_auto_disk_config(image=image, **kwargs)
@ -2122,7 +2123,7 @@ class API(base.Base):
new_pass=admin_password, injected_files=files_to_inject,
image_ref=image_href, orig_image_ref=orig_image_ref,
orig_sys_metadata=orig_sys_metadata, bdms=bdms,
kwargs=kwargs)
preserve_ephemeral=preserve_ephemeral, kwargs=kwargs)
@wrap_check_policy
@check_instance_lock

View File

@ -416,7 +416,7 @@ class ComputeVirtAPI(virtapi.VirtAPI):
class ComputeManager(manager.Manager):
"""Manages the running instances from creation to destruction."""
RPC_API_VERSION = '3.4'
RPC_API_VERSION = '3.5'
def __init__(self, compute_driver=None, *args, **kwargs):
"""Load configuration options and connect to the hypervisor."""
@ -2132,7 +2132,13 @@ class ComputeManager(manager.Manager):
injected_files, admin_password, bdms,
detach_block_devices, attach_block_devices,
network_info=None,
recreate=False, block_device_info=None):
recreate=False, block_device_info=None,
preserve_ephemeral=False):
if preserve_ephemeral:
# The default code path does not support preserving ephemeral
# partitions.
raise exception.PreserveEphemeralNotSupported()
detach_block_devices(context, bdms)
if not recreate:
@ -2152,6 +2158,7 @@ class ComputeManager(manager.Manager):
admin_password, network_info=network_info,
block_device_info=new_block_device_info)
@rpc_common.client_exceptions(exception.PreserveEphemeralNotSupported)
@object_compat
@wrap_exception()
@reverts_task_state
@ -2159,7 +2166,8 @@ class ComputeManager(manager.Manager):
@wrap_instance_fault
def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
injected_files, new_pass, orig_sys_metadata,
bdms, recreate, on_shared_storage):
bdms, recreate, on_shared_storage,
preserve_ephemeral=False):
"""Destroy and re-make this instance.
A 'rebuild' effectively purges all existing data from the system and
@ -2177,6 +2185,8 @@ class ComputeManager(manager.Manager):
hypervisor it was on failed) - cleanup of old state will be
skipped.
:param on_shared_storage: True if instance files on shared storage
:param preserve_ephemeral: True if the default ephemeral storage
partition must be preserved on rebuild
"""
context = context.elevated()
@ -2275,7 +2285,8 @@ class ComputeManager(manager.Manager):
detach_block_devices=detach_block_devices,
attach_block_devices=self._prep_block_device,
block_device_info=block_device_info,
network_info=network_info)
network_info=network_info,
preserve_ephemeral=preserve_ephemeral)
try:
self.driver.rebuild(**kwargs)
except NotImplementedError:

View File

@ -216,6 +216,7 @@ class ComputeAPI(rpcclient.RpcProxy):
3.2 - Update get_vnc_console() to take an instance object
3.3 - Update validate_console_port() to take an instance object
3.4 - Update rebuild_instance() to take an instance object
3.5 - Pass preserve_ephemeral flag to rebuild_instance()
'''
#
@ -561,15 +562,20 @@ class ComputeAPI(rpcclient.RpcProxy):
def rebuild_instance(self, ctxt, instance, new_pass, injected_files,
image_ref, orig_image_ref, orig_sys_metadata, bdms,
recreate=False, on_shared_storage=False, host=None,
kwargs=None):
preserve_ephemeral=False, kwargs=None):
# NOTE(danms): kwargs is only here for cells compatibility, don't
# actually send it to compute
if self.can_send_version('3.4'):
extra = {}
if self.can_send_version('3.5'):
version = '3.5'
extra['preserve_ephemeral'] = preserve_ephemeral
elif self.can_send_version('3.4'):
version = '3.4'
else:
# NOTE(russellb) Havana compat
version = self._get_compat_version('3.0', '2.22')
instance = jsonutils.to_primitive(instance)
bdms_p = jsonutils.to_primitive(bdms)
cctxt = self.client.prepare(server=_compute_host(host, instance),
version=version)
@ -578,7 +584,8 @@ class ComputeAPI(rpcclient.RpcProxy):
injected_files=injected_files, image_ref=image_ref,
orig_image_ref=orig_image_ref,
orig_sys_metadata=orig_sys_metadata, bdms=bdms_p,
recreate=recreate, on_shared_storage=on_shared_storage)
recreate=recreate, on_shared_storage=on_shared_storage,
**extra)
def refresh_provider_fw_rules(self, ctxt, host):
# NOTE(russellb) Havana compat

View File

@ -535,6 +535,11 @@ class ImageNotFound(NotFound):
msg_fmt = _("Image %(image_id)s could not be found.")
class PreserveEphemeralNotSupported(Invalid):
msg_fmt = _("The current driver does not support "
"preserving ephemeral partitions.")
# NOTE(jruzicka): ImageNotFound is not a valid EC2 error code.
class ImageNotFoundEC2(ImageNotFound):
msg_fmt = _("Image %(image_id)s could not be found. The nova EC2 API "

View File

@ -566,6 +566,43 @@ class ServerActionsControllerTest(test.TestCase):
self.assertEqual(instance_meta['kernel_id'], '1')
self.assertEqual(instance_meta['ramdisk_id'], '2')
def _test_rebuild_preserve_ephemeral(self, value=None):
return_server = fakes.fake_instance_get(image_ref='2',
vm_state=vm_states.ACTIVE,
host='fake_host')
self.stubs.Set(db, 'instance_get_by_uuid', return_server)
body = {
"rebuild": {
"image_ref": self._image_href,
},
}
if value is not None:
body['rebuild']['preserve_ephemeral'] = value
req = fakes.HTTPRequestV3.blank(self.url)
context = req.environ['nova.context']
self.mox.StubOutWithMock(compute_api.API, 'rebuild')
if value is not None:
compute_api.API.rebuild(context, mox.IgnoreArg(), self._image_href,
mox.IgnoreArg(), preserve_ephemeral=value)
else:
compute_api.API.rebuild(context, mox.IgnoreArg(), self._image_href,
mox.IgnoreArg())
self.mox.ReplayAll()
self.controller._action_rebuild(req, FAKE_UUID, body)
def test_rebuild_preserve_ephemeral_true(self):
self._test_rebuild_preserve_ephemeral(True)
def test_rebuild_preserve_ephemeral_false(self):
self._test_rebuild_preserve_ephemeral(False)
def test_rebuild_preserve_ephemeral_default(self):
self._test_rebuild_preserve_ephemeral()
def test_resize_server(self):
body = dict(resize=dict(flavor_ref="http://localhost/3"))

View File

@ -4430,6 +4430,24 @@ class TestServerRebuildXMLDeserializer(test.NoDBTestCase):
}
self.assertThat(request['body'], matchers.DictMatches(expected))
def test_rebuild_preserve_ephemeral_passed(self):
serial_request = """<?xml version="1.0" encoding="UTF-8"?>
<rebuild
xmlns="http://docs.openstack.org/compute/api/v1.1"
name="new-server-test"
image_ref="http://localhost/images/1"
preserve_ephemeral="true">
</rebuild>"""
request = self.deserializer.deserialize(serial_request, 'action')
expected = {
"rebuild": {
"name": "new-server-test",
"image_ref": "http://localhost/images/1",
"preserve_ephemeral": True,
},
}
self.assertThat(request['body'], matchers.DictMatches(expected))
class FakeExt(extensions.V3APIExtensionBase):
name = "AccessIPs"

View File

@ -100,7 +100,11 @@ class ServerActionsControllerTest(test.TestCase):
self.url = '/v2/fake/servers/%s/action' % self.uuid
self._image_href = '155d900f-4e14-4e4c-a73d-069cbf4541e6'
self.controller = servers.Controller()
class FakeExtManager(object):
def is_loaded(self, ext):
return False
self.controller = servers.Controller(ext_mgr=FakeExtManager())
self.compute_api = self.controller.compute_api
self.context = context.RequestContext('fake', 'fake')
self.app = fakes.wsgi_app(init_only=('servers',),
@ -320,6 +324,71 @@ class ServerActionsControllerTest(test.TestCase):
self.controller._action_reboot,
req, FAKE_UUID, body)
def test_rebuild_preserve_ephemeral_is_ignored_when_ext_not_loaded(self):
return_server = fakes.fake_instance_get(image_ref='2',
vm_state=vm_states.ACTIVE,
host='fake_host')
self.stubs.Set(db, 'instance_get_by_uuid', return_server)
body = {
"rebuild": {
"imageRef": self._image_href,
"preserve_ephemeral": False,
},
}
req = fakes.HTTPRequest.blank(self.url)
context = req.environ['nova.context']
self.mox.StubOutWithMock(compute_api.API, 'rebuild')
compute_api.API.rebuild(context, mox.IgnoreArg(), self._image_href,
mox.IgnoreArg(), files_to_inject=None)
self.mox.ReplayAll()
self.controller._action_rebuild(req, FAKE_UUID, body)
def _test_rebuild_preserve_ephemeral(self, value=None):
def fake_is_loaded(ext):
return ext == 'os-preserve-ephemeral-rebuild'
self.stubs.Set(self.controller.ext_mgr, 'is_loaded', fake_is_loaded)
return_server = fakes.fake_instance_get(image_ref='2',
vm_state=vm_states.ACTIVE,
host='fake_host')
self.stubs.Set(db, 'instance_get_by_uuid', return_server)
body = {
"rebuild": {
"imageRef": self._image_href,
},
}
if value is not None:
body['rebuild']['preserve_ephemeral'] = value
req = fakes.HTTPRequest.blank(self.url)
context = req.environ['nova.context']
self.mox.StubOutWithMock(compute_api.API, 'rebuild')
if value is not None:
compute_api.API.rebuild(context, mox.IgnoreArg(), self._image_href,
mox.IgnoreArg(), preserve_ephemeral=value,
files_to_inject=None)
else:
compute_api.API.rebuild(context, mox.IgnoreArg(), self._image_href,
mox.IgnoreArg(), files_to_inject=None)
self.mox.ReplayAll()
self.controller._action_rebuild(req, FAKE_UUID, body)
def test_rebuild_preserve_ephemeral_true(self):
self._test_rebuild_preserve_ephemeral(True)
def test_rebuild_preserve_ephemeral_false(self):
self._test_rebuild_preserve_ephemeral(False)
def test_rebuild_preserve_ephemeral_default(self):
self._test_rebuild_preserve_ephemeral()
def test_rebuild_accepted_minimum(self):
return_server = fakes.fake_instance_get(image_ref='2',
vm_state=vm_states.ACTIVE, host='fake_host')
@ -1361,6 +1430,21 @@ class TestServerActionXMLDeserializer(test.TestCase):
serial_request,
'action')
def test_rebuild_preserve_ephemeral_passed(self):
serial_request = """<?xml version="1.0" encoding="UTF-8"?>
<rebuild
xmlns="http://docs.openstack.org/compute/api/v1.1"
imageRef="http://localhost/images/1"
preserve_ephemeral="true"/>"""
request = self.deserializer.deserialize(serial_request, 'action')
expected = {
"rebuild": {
"imageRef": "http://localhost/images/1",
"preserve_ephemeral": True,
},
}
self.assertThat(request['body'], matchers.DictMatches(expected))
def test_corrupt_xml(self):
"""Should throw a 400 error on corrupt xml."""
self.assertRaises(

View File

@ -473,6 +473,7 @@ class ComputeRpcAPITestCase(test.TestCase):
reboot_type='type', version='2.32')
def test_rebuild_instance(self):
self.flags(compute='3.4', group='upgrade_levels')
self._test_compute_api('rebuild_instance', 'cast', new_pass='None',
injected_files='None', image_ref='None', orig_image_ref='None',
bdms=[], instance=self.fake_instance, host='new_host',
@ -487,6 +488,14 @@ class ComputeRpcAPITestCase(test.TestCase):
orig_sys_metadata=None, recreate=True, on_shared_storage=True,
version='2.22')
def test_rebuild_instance_preserve_ephemeral(self):
self.flags(compute='3.5', group='upgrade_levels')
self._test_compute_api('rebuild_instance', 'cast', new_pass='None',
injected_files='None', image_ref='None', orig_image_ref='None',
bdms=[], instance=self.fake_instance, host='new_host',
orig_sys_metadata=None, recreate=True, on_shared_storage=True,
preserve_ephemeral=True, version='3.5')
def test_reserve_block_device_name(self):
self._test_compute_api('reserve_block_device_name', 'call',
instance=self.fake_instance, device='device', volume_id='id')

View File

@ -615,6 +615,14 @@
"name": "BareMetalExtStatus",
"namespace": "http://docs.openstack.org/compute/ext/baremetal_ext_status/api/v2",
"updated": "%(timestamp)s"
},
{
"alias": "os-preserve-ephemeral-rebuild",
"description": "%(text)s",
"links": [],
"name": "PreserveEphemeralOnRebuild",
"namespace": "http://docs.openstack.org/compute/ext/preserve_ephemeral_rebuild/api/v2",
"updated": "%(timestamp)s"
}
]
}

View File

@ -230,4 +230,7 @@
</extension>
<extension alias="os-baremetal-ext-status" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/baremetal_ext_status/api/v2" name="BareMetalExtStatus"> <description>%(text)s</description>
</extension>
<extension alias="os-preserve-ephemeral-rebuild" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/preserve_ephemeral_rebuild/api/v2" name="PreserveEphemeralOnRebuild">
<description>%(text)s</description>
</extension>
</extensions>

View File

@ -0,0 +1,55 @@
{
"server": {
"accessIPv4": "%(ip)s",
"accessIPv6": "%(ip6)s",
"addresses": {
"private": [
{
"addr": "%(ip)s",
"version": 4
}
]
},
"adminPass": "%(password)s",
"created": "%(timestamp)s",
"flavor": {
"id": "1",
"links": [
{
"href": "%(host)s/openstack/flavors/1",
"rel": "bookmark"
}
]
},
"hostId": "%(hostid)s",
"id": "%(uuid)s",
"image": {
"id": "%(uuid)s",
"links": [
{
"href": "%(host)s/openstack/images/%(uuid)s",
"rel": "bookmark"
}
]
},
"links": [
{
"href": "%(host)s/v2/openstack/servers/%(uuid)s",
"rel": "self"
},
{
"href": "%(host)s/openstack/servers/%(uuid)s",
"rel": "bookmark"
}
],
"metadata": {
"meta var": "meta val"
},
"name": "%(name)s",
"progress": 0,
"status": "ACTIVE",
"tenant_id": "openstack",
"updated": "%(timestamp)s",
"user_id": "fake"
}
}

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<server xmlns="http://docs.openstack.org/compute/api/v1.1"
xmlns:atom="http://www.w3.org/2005/Atom"
id="%(uuid)s"
tenantId="openstack" userId="fake"
name="%(name)s"
hostId="%(hostid)s" progress="0"
status="ACTIVE" adminPass="%(password)s"
created="%(timestamp)s"
updated="%(timestamp)s"
accessIPv4="%(ip)s"
accessIPv6="%(ip6)s">
<image id="%(uuid)s">
<atom:link
rel="bookmark"
href="%(host)s/openstack/images/%(uuid)s"/>
</image>
<flavor id="1">
<atom:link
rel="bookmark"
href="%(host)s/openstack/flavors/1"/>
</flavor>
<metadata>
<meta key="My Server Name">Apache1</meta>
</metadata>
<addresses>
<network id="private">
<ip version="4" addr="%(ip)s"/>
</network>
</addresses>
<atom:link
rel="self"
href="%(host)s/v2/openstack/servers/%(uuid)s"/>
<atom:link
rel="bookmark"
href="%(host)s/openstack/servers/%(uuid)s"/>
</server>

View File

@ -0,0 +1,19 @@
{
"rebuild" : {
"imageRef" : "%(host)s/v2/32278/images/%(uuid)s",
"name" : "%(name)s",
"adminPass" : "%(pass)s",
"accessIPv4" : "%(ip)s",
"accessIPv6" : "%(ip6)s",
"metadata" : {
"meta var" : "meta val"
},
"personality" : [
{
"path" : "/etc/banner.txt",
"contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
}
],
"preserve_ephemeral": %(preserve_ephemeral)s
}
}

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<rebuild
xmlns="http://docs.openstack.org/compute/api/v1.1"
name="%(name)s"
imageRef="%(host)s/v1.1/32278/images/%(uuid)s"
accessIPv4="%(ip)s"
accessIPv6="%(ip6)s"
adminPass="%(pass)s"
preserve_ephemeral="%(preserve_ephemeral)s">
<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>
</rebuild>

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

@ -3982,3 +3982,61 @@ class MigrationsSamplesJsonTest(ApiSampleTestBaseV2):
class MigrationsSamplesXmlTest(MigrationsSamplesJsonTest):
ctype = 'xml'
class PreserveEphemeralOnRebuildJsonTest(ServersSampleBase):
extension_name = ('nova.api.openstack.compute.contrib.'
'preserve_ephemeral_rebuild.'
'Preserve_ephemeral_rebuild')
def _test_server_action(self, uuid, action,
subs={}, resp_tpl=None, code=202):
subs.update({'action': action})
response = self._do_post('servers/%s/action' % uuid,
'server-action-%s' % action.lower(),
subs)
if resp_tpl:
subs.update(self._get_regexes())
self._verify_response(resp_tpl, subs, response, code)
else:
self.assertEqual(response.status, code)
self.assertEqual(response.read(), "")
def test_rebuild_server_preserve_ephemeral_false(self):
uuid = self._post_server()
image = self.api.get_images()[0]['id']
subs = {'host': self._get_host(),
'uuid': image,
'name': 'foobar',
'pass': 'seekr3t',
'ip': '1.2.3.4',
'ip6': 'fe80::100',
'hostid': '[a-f0-9]+',
'preserve_ephemeral': 'false'}
self._test_server_action(uuid, 'rebuild', subs,
'server-action-rebuild-resp')
def test_rebuild_server_preserve_ephemeral_true(self):
image = self.api.get_images()[0]['id']
subs = {'host': self._get_host(),
'uuid': image,
'name': 'new-server-test',
'pass': 'seekr3t',
'ip': '1.2.3.4',
'ip6': 'fe80::100',
'hostid': '[a-f0-9]+',
'preserve_ephemeral': 'true'}
def fake_rebuild(self_, context, instance, image_href, admin_password,
**kwargs):
self.assertTrue(kwargs['preserve_ephemeral'])
self.stubs.Set(compute_api.API, 'rebuild', fake_rebuild)
instance_uuid = self._post_server()
response = self._do_post('servers/%s/action' % instance_uuid,
'server-action-rebuild', subs)
self.assertEqual(response.status, 202)
class PreserveEphemeralOnRebuildXmlTest(PreserveEphemeralOnRebuildJsonTest):
ctype = 'xml'

View File

@ -0,0 +1,55 @@
{
"server": {
"addresses": {
"private": [
{
"addr": "%(ip)s",
"version": 4,
"mac_addr": "aa:bb:cc:dd:ee:ff",
"type": "fixed"
}
]
},
"admin_password": "%(password)s",
"created": "%(timestamp)s",
"flavor": {
"id": "1",
"links": [
{
"href": "%(host)s/flavors/1",
"rel": "bookmark"
}
]
},
"host_id": "%(hostid)s",
"id": "%(uuid)s",
"image": {
"id": "%(uuid)s",
"links": [
{
"href": "%(glance_host)s/images/%(uuid)s",
"rel": "bookmark"
}
]
},
"links": [
{
"href": "%(host)s/v3/servers/%(uuid)s",
"rel": "self"
},
{
"href": "%(host)s/servers/%(uuid)s",
"rel": "bookmark"
}
],
"metadata": {
"meta_var": "meta_val"
},
"name": "%(name)s",
"progress": 0,
"status": "ACTIVE",
"tenant_id": "openstack",
"updated": "%(timestamp)s",
"user_id": "fake"
}
}

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<server xmlns="http://docs.openstack.org/compute/api/v1.1"
xmlns:atom="http://www.w3.org/2005/Atom"
id="%(uuid)s"
tenant_id="openstack" user_id="fake"
name="%(name)s"
host_id="%(hostid)s" progress="0"
status="ACTIVE" admin_password="%(password)s"
created="%(timestamp)s"
updated="%(timestamp)s">
<image id="%(uuid)s">
<atom:link
rel="bookmark"
href="%(glance_host)s/images/%(uuid)s"/>
</image>
<flavor id="1">
<atom:link
rel="bookmark"
href="%(host)s/flavors/1"/>
</flavor>
<metadata>
<meta key="meta_var">meta_val</meta>
</metadata>
<addresses>
<network id="private">
<ip version="4" addr="%(ip)s" type="fixed" mac_addr="aa:bb:cc:dd:ee:ff"/>
</network>
</addresses>
<atom:link
rel="self"
href="%(host)s/v3/servers/%(uuid)s"/>
<atom:link
rel="bookmark"
href="%(host)s/servers/%(uuid)s"/>
</server>

View File

@ -0,0 +1,17 @@
{
"rebuild" : {
"image_ref" : "%(glance_host)s/images/%(uuid)s",
"name" : "%(name)s",
"admin_password" : "%(pass)s",
"metadata" : {
"meta_var" : "meta_val"
},
"personality" : [
{
"path" : "/etc/banner.txt",
"contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
}
],
"preserve_ephemeral": %(preserve_ephemeral)s
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<rebuild
xmlns="http://docs.openstack.org/compute/api/v1.1"
name="%(name)s"
image_ref="%(glance_host)s/images/%(uuid)s"
admin_password="%(pass)s"
preserve_ephemeral="%(preserve_ephemeral)s">
<metadata>
<meta key="meta_var">meta_val</meta>
</metadata>
<personality>
<file path="/etc/banner.txt">
ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp
dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k
IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs
c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g
QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo
ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv
dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy
c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6
b25zLiINCg0KLVJpY2hhcmQgQmFjaA==
</file>
</personality>
</rebuild>

View File

@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from nova.compute import api as compute_api
from nova.tests.image import fake
from nova.tests.integrated.v3 import api_sample_base
@ -116,6 +117,35 @@ class ServersActionsJsonTest(ServersSampleBase):
self._test_server_action(uuid, 'rebuild', subs,
'server-action-rebuild-resp')
def _test_server_rebuild_preserve_ephemeral(self, value):
uuid = self._post_server()
image = fake.get_valid_image_id()
subs = {'host': self._get_host(),
'uuid': image,
'name': 'foobar',
'pass': 'seekr3t',
'hostid': '[a-f0-9]+',
'preserve_ephemeral': str(value).lower(),
'action': 'rebuild',
'glance_host': self._get_glance_host(),
}
def fake_rebuild(self_, context, instance, image_href, admin_password,
files_to_inject=None, **kwargs):
self.assertEqual(kwargs['preserve_ephemeral'], value)
self.stubs.Set(compute_api.API, 'rebuild', fake_rebuild)
response = self._do_post('servers/%s/action' % uuid,
'server-action-rebuild-preserve-ephemeral',
subs)
self.assertEqual(response.status, 202)
def test_server_rebuild_preserve_ephemeral_true(self):
self._test_server_rebuild_preserve_ephemeral(True)
def test_server_rebuild_preserve_ephemeral_false(self):
self._test_server_rebuild_preserve_ephemeral(False)
def test_server_resize(self):
self.flags(allow_resize_to_same_host=True)
uuid = self._post_server()

View File

@ -217,7 +217,8 @@ class ComputeDriver(object):
def rebuild(self, context, instance, image_meta, injected_files,
admin_password, bdms, detach_block_devices,
attach_block_devices, network_info=None,
recreate=False, block_device_info=None):
recreate=False, block_device_info=None,
preserve_ephemeral=False):
"""Destroy and re-make this instance.
A 'rebuild' effectively purges all existing data from the system and
@ -250,6 +251,8 @@ class ComputeDriver(object):
hypervisor - all the cleanup of old state is skipped.
:param block_device_info: Information about block devices to be
attached to the instance.
:param preserve_ephemeral: True if the default ephemeral storage
partition must be preserved on rebuild
"""
raise NotImplementedError()