summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRick Harris <rconradharris@gmail.com>2012-01-17 08:18:36 +0000
committerRick Harris <rconradharris@gmail.com>2012-01-17 15:00:08 -0600
commit1357caea7955a558e309acbfff304eea73b16893 (patch)
treec8f23c17be59276f40421b4e55b0d901d53a2bfa
parent8c1e0022028a454f536ff211aa200cab9b0111aa (diff)
downloadnova-1357caea7955a558e309acbfff304eea73b16893.tar.gz
nova-1357caea7955a558e309acbfff304eea73b16893.tar.xz
nova-1357caea7955a558e309acbfff304eea73b16893.zip
Rebuild/Resize support for disk-config.
Fixes bug 917306 Change-Id: I27848a4503a5027b0e5f9cad3f110ea67c462327
-rw-r--r--nova/api/openstack/compute/contrib/disk_config.py250
-rw-r--r--nova/api/openstack/compute/servers.py26
-rw-r--r--nova/compute/api.py5
3 files changed, 141 insertions, 140 deletions
diff --git a/nova/api/openstack/compute/contrib/disk_config.py b/nova/api/openstack/compute/contrib/disk_config.py
index 392291652..30aac0acc 100644
--- a/nova/api/openstack/compute/contrib/disk_config.py
+++ b/nova/api/openstack/compute/contrib/disk_config.py
@@ -16,42 +16,38 @@
"""Disk Config extension."""
-from xml.dom import minidom
-
from webob import exc
from nova.api.openstack import extensions
+from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
-from nova import compute
from nova import db
-from nova import log as logging
from nova import utils
-LOG = logging.getLogger('nova.api.openstack.contrib.disk_config')
-
ALIAS = 'RAX-DCF'
XMLNS_DCF = "http://docs.rackspacecloud.com/servers/api/ext/diskConfig/v1.0"
+API_DISK_CONFIG = "%s:diskConfig" % ALIAS
+INTERNAL_DISK_CONFIG = "auto_disk_config"
-class ServerDiskConfigTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('server')
- root.set('{%s}diskConfig' % XMLNS_DCF, '%s:diskConfig' % ALIAS)
- return xmlutil.SlaveTemplate(root, 1, nsmap={ALIAS: XMLNS_DCF})
+def disk_config_to_api(value):
+ return 'AUTO' if value else 'MANUAL'
-class ServersDiskConfigTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('servers')
- elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
- elem.set('{%s}diskConfig' % XMLNS_DCF, '%s:diskConfig' % ALIAS)
- return xmlutil.SlaveTemplate(root, 1, nsmap={ALIAS: XMLNS_DCF})
+def disk_config_from_api(value):
+ if value == 'AUTO':
+ return True
+ elif value == 'MANUAL':
+ return False
+ else:
+ msg = _("%s must be either 'MANUAL' or 'AUTO'." % API_DISK_CONFIG)
+ raise exc.HTTPBadRequest(explanation=msg)
class ImageDiskConfigTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('image')
- root.set('{%s}diskConfig' % XMLNS_DCF, '%s:diskConfig' % ALIAS)
+ root.set('{%s}diskConfig' % XMLNS_DCF, API_DISK_CONFIG)
return xmlutil.SlaveTemplate(root, 1, nsmap={ALIAS: XMLNS_DCF})
@@ -59,142 +55,130 @@ class ImagesDiskConfigTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('images')
elem = xmlutil.SubTemplateElement(root, 'image', selector='images')
- elem.set('{%s}diskConfig' % XMLNS_DCF, '%s:diskConfig' % ALIAS)
+ elem.set('{%s}diskConfig' % XMLNS_DCF, API_DISK_CONFIG)
return xmlutil.SlaveTemplate(root, 1, nsmap={ALIAS: XMLNS_DCF})
-def disk_config_to_api(value):
- return 'AUTO' if value else 'MANUAL'
-
-
-def disk_config_from_api(value):
- if value == 'AUTO':
- return True
- elif value == 'MANUAL':
- return False
- else:
- msg = _("RAX-DCF:diskConfig must be either 'MANUAL' or 'AUTO'.")
- raise exc.HTTPBadRequest(explanation=msg)
-
-
-class Disk_config(extensions.ExtensionDescriptor):
- """Disk Management Extension"""
-
- name = "DiskConfig"
- alias = ALIAS
- namespace = XMLNS_DCF
- updated = "2011-09-27:00:00+00:00"
+class ImageDiskConfigController(wsgi.Controller):
+ def _add_disk_config(self, context, images):
+ for image in images:
+ metadata = image['metadata']
+ if INTERNAL_DISK_CONFIG in metadata:
+ raw_value = metadata[INTERNAL_DISK_CONFIG]
+ value = utils.bool_from_str(raw_value)
+ image[API_DISK_CONFIG] = disk_config_to_api(value)
- API_DISK_CONFIG = "%s:diskConfig" % ALIAS
- INTERNAL_DISK_CONFIG = "auto_disk_config"
+ @wsgi.extends
+ def show(self, req, resp_obj, id):
+ if 'image' in resp_obj.obj:
+ context = req.environ['nova.context']
+ resp_obj.attach(xml=ImageDiskConfigTemplate())
+ image = resp_obj.obj['image']
+ self._add_disk_config(context, [image])
- def __init__(self, ext_mgr):
- super(Disk_config, self).__init__(ext_mgr)
- self.compute_api = compute.API()
+ @wsgi.extends
+ def detail(self, req, resp_obj):
+ if 'images' in resp_obj.obj:
+ context = req.environ['nova.context']
+ resp_obj.attach(xml=ImagesDiskConfigTemplate())
+ images = resp_obj.obj['images']
+ self._add_disk_config(context, images)
- def _extract_resource_from_body(self, res, body,
- singular, singular_template, plural, plural_template):
- """Returns a list of the given resources from the request body.
- The templates passed in are used for XML serialization.
- """
- template = res.environ.get('nova.template')
- if plural in body:
- resources = body[plural]
- if template:
- template.attach(plural_template)
- elif singular in body:
- resources = [body[singular]]
- if template:
- template.attach(singular_template)
- else:
- resources = []
+class ServerDiskConfigTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('server')
+ root.set('{%s}diskConfig' % XMLNS_DCF, API_DISK_CONFIG)
+ return xmlutil.SlaveTemplate(root, 1, nsmap={ALIAS: XMLNS_DCF})
- return resources
- def _GET_servers(self, req, res, body):
- context = req.environ['nova.context']
+class ServersDiskConfigTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('servers')
+ elem = xmlutil.SubTemplateElement(root, 'server', selector='servers')
+ elem.set('{%s}diskConfig' % XMLNS_DCF, API_DISK_CONFIG)
+ return xmlutil.SlaveTemplate(root, 1, nsmap={ALIAS: XMLNS_DCF})
- servers = self._extract_resource_from_body(res, body,
- singular='server', singular_template=ServerDiskConfigTemplate(),
- plural='servers', plural_template=ServersDiskConfigTemplate())
+class ServerDiskConfigController(wsgi.Controller):
+ def _add_disk_config(self, context, servers):
# Filter out any servers that already have the key set (most likely
# from a remote zone)
- servers = filter(lambda s: self.API_DISK_CONFIG not in s, servers)
+ servers = [s for s in servers if API_DISK_CONFIG not in s]
# Get DB information for servers
uuids = [server['id'] for server in servers]
db_servers = db.instance_get_all_by_filters(context, {'uuid': uuids})
- db_servers = dict([(s['uuid'], s) for s in db_servers])
+ db_servers_by_uuid = dict((s['uuid'], s) for s in db_servers)
for server in servers:
- db_server = db_servers.get(server['id'])
+ db_server = db_servers_by_uuid.get(server['id'])
if db_server:
- value = db_server[self.INTERNAL_DISK_CONFIG]
- server[self.API_DISK_CONFIG] = disk_config_to_api(value)
+ value = db_server[INTERNAL_DISK_CONFIG]
+ server[API_DISK_CONFIG] = disk_config_to_api(value)
+
+ def _show(self, req, resp_obj):
+ if 'server' in resp_obj.obj:
+ context = req.environ['nova.context']
+ resp_obj.attach(xml=ServerDiskConfigTemplate())
+ server = resp_obj.obj['server']
+ self._add_disk_config(context, [server])
+
+ @wsgi.extends
+ def show(self, req, resp_obj, id):
+ self._show(req, resp_obj)
+
+ @wsgi.extends
+ def detail(self, req, resp_obj):
+ if 'servers' in resp_obj.obj:
+ context = req.environ['nova.context']
+ resp_obj.attach(xml=ServersDiskConfigTemplate())
+ servers = resp_obj.obj['servers']
+ self._add_disk_config(context, servers)
+
+ def _set_disk_config(self, dict_):
+ if API_DISK_CONFIG in dict_:
+ api_value = dict_[API_DISK_CONFIG]
+ internal_value = disk_config_from_api(api_value)
+ dict_[INTERNAL_DISK_CONFIG] = internal_value
+
+ @wsgi.extends
+ def create(self, req, body):
+ self._set_disk_config(body['server'])
+ resp_obj = (yield)
+ self._show(req, resp_obj)
+
+ @wsgi.extends
+ def update(self, req, id, body):
+ self._set_disk_config(body['server'])
+ resp_obj = (yield)
+ self._show(req, resp_obj)
+
+ @wsgi.extends(action='rebuild')
+ def _action_rebuild(self, req, id, body):
+ self._set_disk_config(body['rebuild'])
+ resp_obj = (yield)
+ self._show(req, resp_obj)
+
+ @wsgi.extends(action='resize')
+ def _action_resize(self, req, id, body):
+ self._set_disk_config(body['resize'])
+ yield
- return res
- def _GET_images(self, req, res, body):
- images = self._extract_resource_from_body(res, body,
- singular='image', singular_template=ImageDiskConfigTemplate(),
- plural='images', plural_template=ImagesDiskConfigTemplate())
+class Disk_config(extensions.ExtensionDescriptor):
+ """Disk Management Extension"""
- for image in images:
- metadata = image['metadata']
+ name = "DiskConfig"
+ alias = ALIAS
+ namespace = XMLNS_DCF
+ updated = "2011-09-27:00:00+00:00"
- if self.INTERNAL_DISK_CONFIG in metadata:
- raw_value = metadata[self.INTERNAL_DISK_CONFIG]
- value = utils.bool_from_str(raw_value)
- image[self.API_DISK_CONFIG] = disk_config_to_api(value)
-
- return res
-
- def _POST_servers(self, req, res, body):
- return self._GET_servers(req, res, body)
-
- def _pre_POST_servers(self, req):
- # NOTE(sirp): deserialization currently occurs *after* pre-processing
- # extensions are called. Until extensions are refactored so that
- # deserialization occurs earlier, we have to perform the
- # deserialization ourselves.
- content_type = req.content_type
-
- if 'xml' in content_type:
- node = minidom.parseString(req.body)
- server = node.getElementsByTagName('server')[0]
- api_value = server.getAttribute(self.API_DISK_CONFIG)
- if api_value:
- value = disk_config_from_api(api_value)
- server.setAttribute(self.INTERNAL_DISK_CONFIG, str(value))
- req.body = str(node.toxml())
- else:
- body = utils.loads(req.body)
- server = body['server']
- api_value = server.get(self.API_DISK_CONFIG)
- if api_value:
- value = disk_config_from_api(api_value)
- server[self.INTERNAL_DISK_CONFIG] = value
- req.body = utils.dumps(body)
-
- def _pre_PUT_servers(self, req):
- return self._pre_POST_servers(req)
-
- def get_request_extensions(self):
- ReqExt = extensions.RequestExtension
- return [
- ReqExt(method='GET',
- url_route='/:(project_id)/servers/:(id)',
- handler=self._GET_servers),
- ReqExt(method='POST',
- url_route='/:(project_id)/servers',
- handler=self._POST_servers,
- pre_handler=self._pre_POST_servers),
- ReqExt(method='PUT',
- url_route='/:(project_id)/servers/:(id)',
- pre_handler=self._pre_PUT_servers),
- ReqExt(method='GET',
- url_route='/:(project_id)/images/:(id)',
- handler=self._GET_images)
- ]
+ def get_controller_extensions(self):
+ servers_extension = extensions.ControllerExtension(
+ self, 'servers', ServerDiskConfigController())
+
+ images_extension = extensions.ControllerExtension(
+ self, 'images', ImageDiskConfigController())
+
+ return [servers_extension, images_extension]
diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py
index 274589696..0648a43ba 100644
--- a/nova/api/openstack/compute/servers.py
+++ b/nova/api/openstack/compute/servers.py
@@ -277,6 +277,9 @@ class ActionDeserializer(CommonDeserializer):
if node.hasAttribute("name"):
rebuild['name'] = node.getAttribute("name")
+ if node.hasAttribute("auto_disk_config"):
+ rebuild['auto_disk_config'] = node.getAttribute("auto_disk_config")
+
metadata_node = self.find_first_child_named(node, "metadata")
if metadata_node is not None:
rebuild["metadata"] = self.extract_metadata(metadata_node)
@@ -292,9 +295,17 @@ class ActionDeserializer(CommonDeserializer):
return rebuild
def _action_resize(self, node):
- if not node.hasAttribute("flavorRef"):
+ resize = {}
+
+ if node.hasAttribute("flavorRef"):
+ resize["flavorRef"] = node.getAttribute("flavorRef")
+ else:
raise AttributeError("No flavorRef was specified in request")
- return {"flavorRef": node.getAttribute("flavorRef")}
+
+ if node.hasAttribute("auto_disk_config"):
+ rezise['auto_disk_config'] = node.getAttribute("auto_disk_config")
+
+ return resize
def _action_confirm_resize(self, node):
return None
@@ -891,13 +902,13 @@ class Controller(wsgi.Controller):
raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
- def _resize(self, req, instance_id, flavor_id):
+ def _resize(self, req, instance_id, flavor_id, **kwargs):
"""Begin the resize process with given instance/flavor."""
context = req.environ["nova.context"]
instance = self._get_server(context, instance_id)
try:
- self.compute_api.resize(context, instance, flavor_id)
+ self.compute_api.resize(context, instance, flavor_id, **kwargs)
except exception.FlavorNotFound:
msg = _("Unable to locate requested flavor.")
raise exc.HTTPBadRequest(explanation=msg)
@@ -996,7 +1007,11 @@ class Controller(wsgi.Controller):
msg = _("Resize requests require 'flavorRef' attribute.")
raise exc.HTTPBadRequest(explanation=msg)
- return self._resize(req, id, flavor_ref)
+ kwargs = {}
+ if 'auto_disk_config' in body['resize']:
+ kwargs['auto_disk_config'] = body['resize']['auto_disk_config']
+
+ return self._resize(req, id, flavor_ref, **kwargs)
@wsgi.response(202)
@wsgi.serializers(xml=FullServerTemplate)
@@ -1031,6 +1046,7 @@ class Controller(wsgi.Controller):
'accessIPv4': 'access_ip_v4',
'accessIPv6': 'access_ip_v6',
'metadata': 'metadata',
+ 'auto_disk_config': 'auto_disk_config',
}
if 'accessIPv4' in body:
diff --git a/nova/compute/api.py b/nova/compute/api.py
index a75d5c027..23bb4de82 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -1346,7 +1346,7 @@ class API(base.Base):
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
task_state=[None])
@scheduler_api.reroute_compute("resize")
- def resize(self, context, instance, flavor_id=None):
+ def resize(self, context, instance, flavor_id=None, **kwargs):
"""Resize (ie, migrate) a running instance.
If flavor_id is None, the process is considered a migration, keeping
@@ -1379,7 +1379,8 @@ class API(base.Base):
self.update(context,
instance,
vm_state=vm_states.RESIZING,
- task_state=task_states.RESIZE_PREP)
+ task_state=task_states.RESIZE_PREP,
+ **kwargs)
request_spec = {
'instance_type': new_instance_type,