summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Wolf <throughnothing@gmail.com>2011-07-21 15:14:16 -0400
committerWilliam Wolf <throughnothing@gmail.com>2011-07-21 15:14:16 -0400
commit8bf63ee3132e2f41eca5fa34ea8428e03b22986c (patch)
treec836bc1d53b6d90965d4b08d7ab7da8a6d2b4772
parentdc616cd633007aa83d7576ae74cf807aa0df6776 (diff)
parentaf1b6c947a9bc8915e328546bda6ff454a1246e9 (diff)
downloadnova-8bf63ee3132e2f41eca5fa34ea8428e03b22986c.tar.gz
nova-8bf63ee3132e2f41eca5fa34ea8428e03b22986c.tar.xz
nova-8bf63ee3132e2f41eca5fa34ea8428e03b22986c.zip
merge from trunk
-rw-r--r--nova/api/openstack/accounts.py6
-rw-r--r--nova/api/openstack/backup_schedules.py9
-rw-r--r--nova/api/openstack/common.py19
-rw-r--r--nova/api/openstack/consoles.py7
-rw-r--r--nova/api/openstack/create_instance_helper.py11
-rw-r--r--nova/api/openstack/flavors.py55
-rw-r--r--nova/api/openstack/image_metadata.py5
-rw-r--r--nova/api/openstack/images.py16
-rw-r--r--nova/api/openstack/ips.py11
-rw-r--r--nova/api/openstack/server_metadata.py1
-rw-r--r--nova/api/openstack/servers.py92
-rw-r--r--nova/api/openstack/shared_ip_groups.py13
-rw-r--r--nova/api/openstack/users.py3
-rw-r--r--nova/api/openstack/versions.py147
-rw-r--r--nova/api/openstack/views/versions.py3
-rw-r--r--nova/api/openstack/wsgi.py25
-rw-r--r--nova/api/openstack/zones.py8
-rw-r--r--nova/compute/api.py32
-rw-r--r--nova/compute/instance_types.py2
-rw-r--r--nova/compute/manager.py83
-rw-r--r--nova/db/api.py4
-rw-r--r--nova/db/sqlalchemy/api.py14
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/034_change_instance_id_in_migrations.py43
-rw-r--r--nova/db/sqlalchemy/models.py3
-rw-r--r--nova/exception.py2
-rw-r--r--nova/image/glance.py8
-rw-r--r--nova/image/s3.py2
-rw-r--r--nova/network/manager.py3
-rw-r--r--nova/tests/api/openstack/test_flavors.py374
-rw-r--r--nova/tests/api/openstack/test_images.py206
-rw-r--r--nova/tests/api/openstack/test_servers.py2
-rw-r--r--nova/tests/api/openstack/test_versions.py159
-rw-r--r--nova/tests/db/fakes.py8
-rw-r--r--nova/tests/test_compute.py40
-rw-r--r--nova/tests/test_instance_types_extra_specs.py8
-rw-r--r--nova/tests/test_network.py4
-rw-r--r--nova/virt/libvirt/netutils.py2
-rw-r--r--smoketests/test_sysadmin.py33
38 files changed, 1086 insertions, 377 deletions
diff --git a/nova/api/openstack/accounts.py b/nova/api/openstack/accounts.py
index e3201b14f..a13a758ab 100644
--- a/nova/api/openstack/accounts.py
+++ b/nova/api/openstack/accounts.py
@@ -47,10 +47,10 @@ class Controller(object):
raise exception.AdminRequired()
def index(self, req):
- raise faults.Fault(webob.exc.HTTPNotImplemented())
+ raise webob.exc.HTTPNotImplemented()
def detail(self, req):
- raise faults.Fault(webob.exc.HTTPNotImplemented())
+ raise webob.exc.HTTPNotImplemented()
def show(self, req, id):
"""Return data about the given account id"""
@@ -65,7 +65,7 @@ class Controller(object):
def create(self, req, body):
"""We use update with create-or-update semantics
because the id comes from an external source"""
- raise faults.Fault(webob.exc.HTTPNotImplemented())
+ raise webob.exc.HTTPNotImplemented()
def update(self, req, id, body):
"""This is really create or update."""
diff --git a/nova/api/openstack/backup_schedules.py b/nova/api/openstack/backup_schedules.py
index 3e95aedf3..7ff0d999e 100644
--- a/nova/api/openstack/backup_schedules.py
+++ b/nova/api/openstack/backup_schedules.py
@@ -19,7 +19,6 @@ import time
from webob import exc
-from nova.api.openstack import faults
from nova.api.openstack import wsgi
@@ -36,20 +35,20 @@ class Controller(object):
def index(self, req, server_id, **kwargs):
""" Returns the list of backup schedules for a given instance """
- return faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def show(self, req, server_id, id, **kwargs):
""" Returns a single backup schedule for a given instance """
- return faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def create(self, req, server_id, **kwargs):
""" No actual update method required, since the existing API allows
both create and update through a POST """
- return faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def delete(self, req, server_id, id, **kwargs):
""" Deletes an existing backup schedule """
- return faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def create_resource():
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index 8e12ce0c0..57031ebf1 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -53,10 +53,10 @@ def get_pagination_params(request):
params[param] = int(request.GET[param])
except ValueError:
msg = _('%s param must be an integer') % param
- raise webob.exc.HTTPBadRequest(msg)
+ raise webob.exc.HTTPBadRequest(explanation=msg)
if params[param] < 0:
msg = _('%s param must be positive') % param
- raise webob.exc.HTTPBadRequest(msg)
+ raise webob.exc.HTTPBadRequest(explanation=msg)
return params
@@ -77,18 +77,22 @@ def limited(items, request, max_limit=FLAGS.osapi_max_limit):
try:
offset = int(request.GET.get('offset', 0))
except ValueError:
- raise webob.exc.HTTPBadRequest(_('offset param must be an integer'))
+ msg = _('offset param must be an integer')
+ raise webob.exc.HTTPBadRequest(explanation=msg)
try:
limit = int(request.GET.get('limit', max_limit))
except ValueError:
- raise webob.exc.HTTPBadRequest(_('limit param must be an integer'))
+ msg = _('limit param must be an integer')
+ raise webob.exc.HTTPBadRequest(explanation=msg)
if limit < 0:
- raise webob.exc.HTTPBadRequest(_('limit param must be positive'))
+ msg = _('limit param must be positive')
+ raise webob.exc.HTTPBadRequest(explanation=msg)
if offset < 0:
- raise webob.exc.HTTPBadRequest(_('offset param must be positive'))
+ msg = _('offset param must be positive')
+ raise webob.exc.HTTPBadRequest(explanation=msg)
limit = min(max_limit, limit or max_limit)
range_end = offset + limit
@@ -111,7 +115,8 @@ def limited_by_marker(items, request, max_limit=FLAGS.osapi_max_limit):
start_index = i + 1
break
if start_index < 0:
- raise webob.exc.HTTPBadRequest(_('marker [%s] not found' % marker))
+ msg = _('marker [%s] not found') % marker
+ raise webob.exc.HTTPBadRequest(explanation=msg)
range_end = start_index + limit
return items[start_index:range_end]
diff --git a/nova/api/openstack/consoles.py b/nova/api/openstack/consoles.py
index 9c7b37f0d..d2655acfa 100644
--- a/nova/api/openstack/consoles.py
+++ b/nova/api/openstack/consoles.py
@@ -20,7 +20,6 @@ import webob
from nova import console
from nova import exception
-from nova.api.openstack import faults
from nova.api.openstack import wsgi
@@ -72,12 +71,12 @@ class Controller(object):
int(server_id),
int(id))
except exception.NotFound:
- return faults.Fault(exc.HTTPNotFound())
+ raise exc.HTTPNotFound()
return _translate_detail_keys(console)
def update(self, req, server_id, id):
"""You can't update a console"""
- raise faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def delete(self, req, server_id, id):
"""Deletes a console"""
@@ -86,7 +85,7 @@ class Controller(object):
int(server_id),
int(id))
except exception.NotFound:
- return faults.Fault(exc.HTTPNotFound())
+ raise exc.HTTPNotFound()
return webob.Response(status_int=202)
diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py
index 2b9256896..7249f1261 100644
--- a/nova/api/openstack/create_instance_helper.py
+++ b/nova/api/openstack/create_instance_helper.py
@@ -28,7 +28,6 @@ from nova import quota
from nova import utils
from nova.compute import instance_types
-from nova.api.openstack import faults
from nova.api.openstack import wsgi
from nova.auth import manager as auth_manager
@@ -70,7 +69,7 @@ class CreateInstanceHelper(object):
return type from this method is left to the caller.
"""
if not body:
- raise faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
context = req.environ['nova.context']
@@ -94,7 +93,7 @@ class CreateInstanceHelper(object):
except Exception, e:
msg = _("Cannot find requested image %(image_href)s: %(e)s" %
locals())
- raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+ raise exc.HTTPBadRequest(explanation=msg)
personality = body['server'].get('personality')
@@ -106,7 +105,7 @@ class CreateInstanceHelper(object):
flavor_id = self.controller._flavor_id_from_req_data(body)
except ValueError as error:
msg = _("Invalid flavorRef provided.")
- raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+ raise exc.HTTPBadRequest(explanation=msg)
if not 'name' in body['server']:
msg = _("Server name is not defined")
@@ -157,10 +156,10 @@ class CreateInstanceHelper(object):
self._handle_quota_error(error)
except exception.ImageNotFound as error:
msg = _("Can not find requested image")
- raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+ raise exc.HTTPBadRequest(explanation=msg)
except exception.FlavorNotFound as error:
msg = _("Invalid flavorRef provided.")
- raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+ raise exc.HTTPBadRequest(explanation=msg)
# Let the caller deal with unhandled exceptions.
def _handle_quota_error(self, error):
diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py
index 6fab13147..b4bda68d4 100644
--- a/nova/api/openstack/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -16,6 +16,7 @@
# under the License.
import webob
+import xml.dom.minidom as minidom
from nova import db
from nova import exception
@@ -74,19 +75,65 @@ class ControllerV11(Controller):
return views.flavors.ViewBuilderV11(base_url)
+class FlavorXMLSerializer(wsgi.XMLDictSerializer):
+
+ def __init__(self):
+ super(FlavorXMLSerializer, self).__init__(xmlns=wsgi.XMLNS_V11)
+
+ def _flavor_to_xml(self, xml_doc, flavor, detailed):
+ flavor_node = xml_doc.createElement('flavor')
+ flavor_node.setAttribute('id', str(flavor['id']))
+ flavor_node.setAttribute('name', flavor['name'])
+
+ if detailed:
+ flavor_node.setAttribute('ram', str(flavor['ram']))
+ flavor_node.setAttribute('disk', str(flavor['disk']))
+
+ link_nodes = self._create_link_nodes(xml_doc, flavor['links'])
+ for link_node in link_nodes:
+ flavor_node.appendChild(link_node)
+ return flavor_node
+
+ def _flavors_list_to_xml(self, xml_doc, flavors, detailed):
+ container_node = xml_doc.createElement('flavors')
+
+ for flavor in flavors:
+ item_node = self._flavor_to_xml(xml_doc, flavor, detailed)
+ container_node.appendChild(item_node)
+ return container_node
+
+ def show(self, flavor_container):
+ xml_doc = minidom.Document()
+ flavor = flavor_container['flavor']
+ node = self._flavor_to_xml(xml_doc, flavor, True)
+ return self.to_xml_string(node, True)
+
+ def detail(self, flavors_container):
+ xml_doc = minidom.Document()
+ flavors = flavors_container['flavors']
+ node = self._flavors_list_to_xml(xml_doc, flavors, True)
+ return self.to_xml_string(node, True)
+
+ def index(self, flavors_container):
+ xml_doc = minidom.Document()
+ flavors = flavors_container['flavors']
+ node = self._flavors_list_to_xml(xml_doc, flavors, False)
+ return self.to_xml_string(node, True)
+
+
def create_resource(version='1.0'):
controller = {
'1.0': ControllerV10,
'1.1': ControllerV11,
}[version]()
- xmlns = {
- '1.0': wsgi.XMLNS_V10,
- '1.1': wsgi.XMLNS_V11,
+ xml_serializer = {
+ '1.0': wsgi.XMLDictSerializer(xmlns=wsgi.XMLNS_V10),
+ '1.1': FlavorXMLSerializer(),
}[version]
body_serializers = {
- 'application/xml': wsgi.XMLDictSerializer(xmlns=xmlns),
+ 'application/xml': xml_serializer,
}
serializer = wsgi.ResponseSerializer(body_serializers)
diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py
index 4f33844fa..c0fc8c09b 100644
--- a/nova/api/openstack/image_metadata.py
+++ b/nova/api/openstack/image_metadata.py
@@ -22,7 +22,6 @@ from nova import flags
from nova import image
from nova import quota
from nova import utils
-from nova.api.openstack import faults
from nova.api.openstack import wsgi
@@ -62,7 +61,7 @@ class Controller(object):
if id in metadata:
return {'meta': {id: metadata[id]}}
else:
- return faults.Fault(exc.HTTPNotFound())
+ raise exc.HTTPNotFound()
def create(self, req, image_id, body):
context = req.environ['nova.context']
@@ -105,7 +104,7 @@ class Controller(object):
img = self.image_service.show(context, image_id)
metadata = self._get_metadata(context, image_id)
if not id in metadata:
- return faults.Fault(exc.HTTPNotFound())
+ raise exc.HTTPNotFound()
metadata.pop(id)
img['properties'] = metadata
self.image_service.update(context, image_id, img, None)
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index d0317583e..30e4fd389 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -25,7 +25,6 @@ from nova import flags
import nova.image
from nova import log
from nova.api.openstack import common
-from nova.api.openstack import faults
from nova.api.openstack import image_metadata
from nova.api.openstack import servers
from nova.api.openstack.views import images as images_view
@@ -35,7 +34,13 @@ from nova.api.openstack import wsgi
LOG = log.getLogger('nova.api.openstack.images')
FLAGS = flags.FLAGS
-SUPPORTED_FILTERS = ['name', 'status']
+SUPPORTED_FILTERS = {
+ 'name': 'name',
+ 'status': 'status',
+ 'changes-since': 'changes-since',
+ 'server': 'property-instance_ref',
+ 'type': 'property-image_type',
+}
class Controller(object):
@@ -62,8 +67,9 @@ class Controller(object):
filters = {}
for param in req.str_params:
if param in SUPPORTED_FILTERS or param.startswith('property-'):
- filters[param] = req.str_params.get(param)
-
+ # map filter name or carry through if property-*
+ filter_name = SUPPORTED_FILTERS.get(param, param)
+ filters[filter_name] = req.str_params.get(param)
return filters
def show(self, req, id):
@@ -78,7 +84,7 @@ class Controller(object):
image = self._image_service.show(context, id)
except (exception.NotFound, exception.InvalidImageRef):
explanation = _("Image not found.")
- raise faults.Fault(webob.exc.HTTPNotFound(explanation=explanation))
+ raise webob.exc.HTTPNotFound(explanation=explanation)
return dict(image=self.get_builder(req).build(image, detail=True))
diff --git a/nova/api/openstack/ips.py b/nova/api/openstack/ips.py
index 1ebfdb831..2996b032d 100644
--- a/nova/api/openstack/ips.py
+++ b/nova/api/openstack/ips.py
@@ -20,7 +20,6 @@ import time
from webob import exc
import nova
-from nova.api.openstack import faults
import nova.api.openstack.views.addresses
from nova.api.openstack import wsgi
from nova import db
@@ -37,14 +36,14 @@ class Controller(object):
instance = self.compute_api.get(
req.environ['nova.context'], server_id)
except nova.exception.NotFound:
- return faults.Fault(exc.HTTPNotFound())
+ raise exc.HTTPNotFound()
return instance
def create(self, req, server_id, body):
- return faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def delete(self, req, server_id, id):
- return faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
class ControllerV10(Controller):
@@ -63,7 +62,7 @@ class ControllerV10(Controller):
view = builder.build_public_parts(instance)
else:
msg = _("Only private and public networks available")
- return faults.Fault(exc.HTTPNotFound(explanation=msg))
+ raise exc.HTTPNotFound(explanation=msg)
return {id: view}
@@ -86,7 +85,7 @@ class ControllerV11(Controller):
if network is None:
msg = _("Instance is not a member of specified network")
- return faults.Fault(exc.HTTPNotFound(explanation=msg))
+ raise exc.HTTPNotFound(explanation=msg)
return network
diff --git a/nova/api/openstack/server_metadata.py b/nova/api/openstack/server_metadata.py
index 3b9169f81..d4f42bbf5 100644
--- a/nova/api/openstack/server_metadata.py
+++ b/nova/api/openstack/server_metadata.py
@@ -18,7 +18,6 @@
from webob import exc
from nova import compute
-from nova.api.openstack import faults
from nova.api.openstack import wsgi
from nova import exception
from nova import quota
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 3b3d0685d..7bef1d9b2 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -27,7 +27,6 @@ from nova import log as logging
from nova import utils
from nova.api.openstack import common
from nova.api.openstack import create_instance_helper as helper
-from nova.api.openstack import faults
import nova.api.openstack.views.addresses
import nova.api.openstack.views.flavors
import nova.api.openstack.views.images
@@ -102,17 +101,14 @@ class Controller(object):
req.environ['nova.context'], id)
return self._build_view(req, instance, is_detail=True)
except exception.NotFound:
- return faults.Fault(exc.HTTPNotFound())
+ raise exc.HTTPNotFound()
def create(self, req, body):
""" Creates a new server for a given user """
extra_values = None
result = None
- try:
- extra_values, instances = self.helper.create_instance(
- req, body, self.compute_api.create)
- except faults.Fault, f:
- return f
+ extra_values, instances = self.helper.create_instance(
+ req, body, self.compute_api.create)
# We can only return 1 instance via the API, if we happen to
# build more than one... instances is a list, so we'll just
@@ -132,7 +128,7 @@ class Controller(object):
raise exc.HTTPUnprocessableEntity()
if not body:
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
ctxt = req.environ['nova.context']
update_dict = {}
@@ -147,7 +143,7 @@ class Controller(object):
try:
self.compute_api.update(ctxt, id, **update_dict)
except exception.NotFound:
- return faults.Fault(exc.HTTPNotFound())
+ raise exc.HTTPNotFound()
return exc.HTTPNoContent()
@@ -171,7 +167,7 @@ class Controller(object):
for key in actions.keys():
if key in body:
return actions[key](body, req, id)
- return faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def _action_change_password(self, input_dict, req, id):
return exc.HTTPNotImplemented()
@@ -181,7 +177,7 @@ class Controller(object):
self.compute_api.confirm_resize(req.environ['nova.context'], id)
except Exception, e:
LOG.exception(_("Error in confirm-resize %s"), e)
- return faults.Fault(exc.HTTPBadRequest())
+ raise exc.HTTPBadRequest()
return exc.HTTPNoContent()
def _action_revert_resize(self, input_dict, req, id):
@@ -189,7 +185,7 @@ class Controller(object):
self.compute_api.revert_resize(req.environ['nova.context'], id)
except Exception, e:
LOG.exception(_("Error in revert-resize %s"), e)
- return faults.Fault(exc.HTTPBadRequest())
+ raise exc.HTTPBadRequest()
return webob.Response(status_int=202)
def _action_resize(self, input_dict, req, id):
@@ -200,14 +196,14 @@ class Controller(object):
reboot_type = input_dict['reboot']['type']
else:
LOG.exception(_("Missing argument 'type' for reboot"))
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
try:
# TODO(gundlach): pass reboot_type, support soft reboot in
# virt driver
self.compute_api.reboot(req.environ['nova.context'], id)
except Exception, e:
LOG.exception(_("Error in reboot %s"), e)
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
def _action_migrate(self, input_dict, req, id):
@@ -215,7 +211,7 @@ class Controller(object):
self.compute_api.resize(req.environ['nova.context'], id)
except Exception, e:
LOG.exception(_("Error in migrate %s"), e)
- return faults.Fault(exc.HTTPBadRequest())
+ raise exc.HTTPBadRequest()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -231,7 +227,7 @@ class Controller(object):
except:
readable = traceback.format_exc()
LOG.exception(_("Compute.api::lock %s"), readable)
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -247,7 +243,7 @@ class Controller(object):
except:
readable = traceback.format_exc()
LOG.exception(_("Compute.api::unlock %s"), readable)
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -262,7 +258,7 @@ class Controller(object):
except:
readable = traceback.format_exc()
LOG.exception(_("Compute.api::get_lock %s"), readable)
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -277,7 +273,7 @@ class Controller(object):
except:
readable = traceback.format_exc()
LOG.exception(_("Compute.api::reset_network %s"), readable)
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -292,7 +288,7 @@ class Controller(object):
except:
readable = traceback.format_exc()
LOG.exception(_("Compute.api::inject_network_info %s"), readable)
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -304,7 +300,7 @@ class Controller(object):
except:
readable = traceback.format_exc()
LOG.exception(_("Compute.api::pause %s"), readable)
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -316,7 +312,7 @@ class Controller(object):
except:
readable = traceback.format_exc()
LOG.exception(_("Compute.api::unpause %s"), readable)
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -328,7 +324,7 @@ class Controller(object):
except:
readable = traceback.format_exc()
LOG.exception(_("compute.api::suspend %s"), readable)
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -340,7 +336,7 @@ class Controller(object):
except:
readable = traceback.format_exc()
LOG.exception(_("compute.api::resume %s"), readable)
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -352,7 +348,7 @@ class Controller(object):
except:
readable = traceback.format_exc()
LOG.exception(_("compute.api::rescue %s"), readable)
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -364,7 +360,7 @@ class Controller(object):
except:
readable = traceback.format_exc()
LOG.exception(_("compute.api::unrescue %s"), readable)
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -374,7 +370,7 @@ class Controller(object):
self.compute_api.get_ajax_console(req.environ['nova.context'],
int(id))
except exception.NotFound:
- return faults.Fault(exc.HTTPNotFound())
+ raise exc.HTTPNotFound()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -384,7 +380,7 @@ class Controller(object):
self.compute_api.get_vnc_console(req.environ['nova.context'],
int(id))
except exception.NotFound:
- return faults.Fault(exc.HTTPNotFound())
+ raise exc.HTTPNotFound()
return webob.Response(status_int=202)
@scheduler_api.redirect_handler
@@ -416,7 +412,7 @@ class ControllerV10(Controller):
try:
self.compute_api.delete(req.environ['nova.context'], id)
except exception.NotFound:
- return faults.Fault(exc.HTTPNotFound())
+ raise exc.HTTPNotFound()
return webob.Response(status_int=202)
def _image_ref_from_req_data(self, data):
@@ -440,17 +436,13 @@ class ControllerV10(Controller):
def _action_resize(self, input_dict, req, id):
""" Resizes a given instance to the flavor size requested """
- try:
- if 'resize' in input_dict and 'flavorId' in input_dict['resize']:
- flavor_id = input_dict['resize']['flavorId']
- self.compute_api.resize(req.environ['nova.context'], id,
- flavor_id)
- else:
- LOG.exception(_("Missing 'flavorId' argument for resize"))
- return faults.Fault(exc.HTTPUnprocessableEntity())
- except Exception, e:
- LOG.exception(_("Error in resize %s"), e)
- return faults.Fault(exc.HTTPBadRequest())
+ if 'resize' in input_dict and 'flavorId' in input_dict['resize']:
+ flavor_id = input_dict['resize']['flavorId']
+ self.compute_api.resize(req.environ['nova.context'], id,
+ flavor_id)
+ else:
+ LOG.exception(_("Missing 'flavorId' argument for resize"))
+ raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
def _action_rebuild(self, info, request, instance_id):
@@ -462,14 +454,14 @@ class ControllerV10(Controller):
except (KeyError, TypeError):
msg = _("Could not parse imageId from request.")
LOG.debug(msg)
- return faults.Fault(exc.HTTPBadRequest(explanation=msg))
+ raise exc.HTTPBadRequest(explanation=msg)
try:
self.compute_api.rebuild(context, instance_id, image_id)
except exception.BuildInProgress:
msg = _("Instance %d is currently being rebuilt.") % instance_id
LOG.debug(msg)
- return faults.Fault(exc.HTTPConflict(explanation=msg))
+ raise exc.HTTPConflict(explanation=msg)
return webob.Response(status_int=202)
@@ -486,7 +478,7 @@ class ControllerV11(Controller):
try:
self.compute_api.delete(req.environ['nova.context'], id)
except exception.NotFound:
- return faults.Fault(exc.HTTPNotFound())
+ raise exc.HTTPNotFound()
def _image_ref_from_req_data(self, data):
return data['server']['imageRef']
@@ -530,7 +522,7 @@ class ControllerV11(Controller):
except AttributeError as ex:
msg = _("Unable to parse metadata key/value pairs.")
LOG.debug(msg)
- raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+ raise exc.HTTPBadRequest(explanation=msg)
def _decode_personalities(self, personalities):
"""Decode the Base64-encoded personalities."""
@@ -541,14 +533,14 @@ class ControllerV11(Controller):
except (KeyError, TypeError):
msg = _("Unable to parse personality path/contents.")
LOG.info(msg)
- raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+ raise exc.HTTPBadRequest(explanation=msg)
try:
personality["contents"] = base64.b64decode(contents)
except TypeError:
msg = _("Personality content could not be Base64 decoded.")
LOG.info(msg)
- raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+ raise exc.HTTPBadRequest(explanation=msg)
def _action_resize(self, input_dict, req, id):
""" Resizes a given instance to the flavor size requested """
@@ -560,10 +552,10 @@ class ControllerV11(Controller):
flavor_id)
else:
LOG.exception(_("Missing 'flavorRef' argument for resize"))
- return faults.Fault(exc.HTTPUnprocessableEntity())
+ raise exc.HTTPUnprocessableEntity()
except Exception, e:
LOG.exception(_("Error in resize %s"), e)
- return faults.Fault(exc.HTTPBadRequest())
+ raise exc.HTTPBadRequest()
return webob.Response(status_int=202)
def _action_rebuild(self, info, request, instance_id):
@@ -575,7 +567,7 @@ class ControllerV11(Controller):
except (KeyError, TypeError):
msg = _("Could not parse imageRef from request.")
LOG.debug(msg)
- return faults.Fault(exc.HTTPBadRequest(explanation=msg))
+ raise exc.HTTPBadRequest(explanation=msg)
personalities = info["rebuild"].get("personality", [])
metadata = info["rebuild"].get("metadata")
@@ -591,7 +583,7 @@ class ControllerV11(Controller):
except exception.BuildInProgress:
msg = _("Instance %d is currently being rebuilt.") % instance_id
LOG.debug(msg)
- return faults.Fault(exc.HTTPConflict(explanation=msg))
+ raise exc.HTTPConflict(explanation=msg)
return webob.Response(status_int=202)
diff --git a/nova/api/openstack/shared_ip_groups.py b/nova/api/openstack/shared_ip_groups.py
index cf2ddbabb..54d0a8334 100644
--- a/nova/api/openstack/shared_ip_groups.py
+++ b/nova/api/openstack/shared_ip_groups.py
@@ -17,7 +17,6 @@
from webob import exc
-from nova.api.openstack import faults
from nova.api.openstack import wsgi
@@ -26,27 +25,27 @@ class Controller(object):
def index(self, req, **kwargs):
""" Returns a list of Shared IP Groups for the user """
- raise faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def show(self, req, id, **kwargs):
""" Shows in-depth information on a specific Shared IP Group """
- raise faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def update(self, req, id, **kwargs):
""" You can't update a Shared IP Group """
- raise faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def delete(self, req, id, **kwargs):
""" Deletes a Shared IP Group """
- raise faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def detail(self, req, **kwargs):
""" Returns a complete list of Shared IP Groups """
- raise faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def create(self, req, **kwargs):
""" Creates a new Shared IP group """
- raise faults.Fault(exc.HTTPNotImplemented())
+ raise exc.HTTPNotImplemented()
def create_resource():
diff --git a/nova/api/openstack/users.py b/nova/api/openstack/users.py
index 6ae1eaf2a..8dd72d559 100644
--- a/nova/api/openstack/users.py
+++ b/nova/api/openstack/users.py
@@ -19,7 +19,6 @@ from nova import exception
from nova import flags
from nova import log as logging
from nova.api.openstack import common
-from nova.api.openstack import faults
from nova.api.openstack import wsgi
from nova.auth import manager
@@ -69,7 +68,7 @@ class Controller(object):
user = None
if user is None:
- raise faults.Fault(exc.HTTPNotFound())
+ raise exc.HTTPNotFound()
return dict(user=_translate_keys(user))
diff --git a/nova/api/openstack/versions.py b/nova/api/openstack/versions.py
index a634c3267..df7a94b7e 100644
--- a/nova/api/openstack/versions.py
+++ b/nova/api/openstack/versions.py
@@ -15,13 +15,18 @@
# License for the specific language governing permissions and limitations
# under the License.
+from datetime import datetime
import webob
import webob.dec
+from xml.dom import minidom
import nova.api.openstack.views.versions
from nova.api.openstack import wsgi
+ATOM_XMLNS = "http://www.w3.org/2005/Atom"
+
+
class Versions(wsgi.Resource):
def __init__(self):
metadata = {
@@ -32,11 +37,19 @@ class Versions(wsgi.Resource):
}
body_serializers = {
- 'application/xml': wsgi.XMLDictSerializer(metadata=metadata),
+ 'application/atom+xml': VersionsAtomSerializer(metadata=metadata),
+ 'application/xml': VersionsXMLSerializer(metadata=metadata),
}
serializer = wsgi.ResponseSerializer(body_serializers)
- wsgi.Resource.__init__(self, None, serializer=serializer)
+ supported_content_types = ('application/json',
+ 'application/xml',
+ 'application/atom+xml')
+ deserializer = wsgi.RequestDeserializer(
+ supported_content_types=supported_content_types)
+
+ wsgi.Resource.__init__(self, None, serializer=serializer,
+ deserializer=deserializer)
def dispatch(self, request, *args):
"""Respond to a request for all OpenStack API versions."""
@@ -44,13 +57,143 @@ class Versions(wsgi.Resource):
{
"id": "v1.1",
"status": "CURRENT",
+ #TODO(wwolf) get correct value for these
+ "updated": "2011-07-18T11:30:00Z",
},
{
"id": "v1.0",
"status": "DEPRECATED",
+ #TODO(wwolf) get correct value for these
+ "updated": "2010-10-09T11:30:00Z",
},
]
builder = nova.api.openstack.views.versions.get_view_builder(request)
versions = [builder.build(version) for version in version_objs]
return dict(versions=versions)
+
+
+class VersionsXMLSerializer(wsgi.XMLDictSerializer):
+ def _versions_to_xml(self, versions):
+ root = self._xml_doc.createElement('versions')
+
+ for version in versions:
+ root.appendChild(self._create_version_node(version))
+
+ return root
+
+ def _create_version_node(self, version):
+ version_node = self._xml_doc.createElement('version')
+ version_node.setAttribute('id', version['id'])
+ version_node.setAttribute('status', version['status'])
+ version_node.setAttribute('updated', version['updated'])
+
+ for link in version['links']:
+ link_node = self._xml_doc.createElement('atom:link')
+ link_node.setAttribute('rel', link['rel'])
+ link_node.setAttribute('href', link['href'])
+ version_node.appendChild(link_node)
+
+ return version_node
+
+ def default(self, data):
+ self._xml_doc = minidom.Document()
+ node = self._versions_to_xml(data['versions'])
+
+ return self.to_xml_string(node)
+
+
+class VersionsAtomSerializer(wsgi.XMLDictSerializer):
+ def __init__(self, metadata=None, xmlns=None):
+ if not xmlns:
+ self.xmlns = ATOM_XMLNS
+ else:
+ self.xmlns = xmlns
+
+ def _create_text_elem(self, name, text, type=None):
+ elem = self._xml_doc.createElement(name)
+ if type:
+ elem.setAttribute('type', type)
+ elem_text = self._xml_doc.createTextNode(text)
+ elem.appendChild(elem_text)
+ return elem
+
+ def _get_most_recent_update(self, versions):
+ recent = None
+ for version in versions:
+ updated = datetime.strptime(version['updated'],
+ '%Y-%m-%dT%H:%M:%SZ')
+ if not recent:
+ recent = updated
+ elif updated > recent:
+ recent = updated
+
+ return recent.strftime('%Y-%m-%dT%H:%M:%SZ')
+
+ def _get_base_url(self, link_href):
+ # Make sure no trailing /
+ link_href = link_href.rstrip('/')
+ return link_href.rsplit('/', 1)[0] + '/'
+
+ def _create_meta(self, root, versions):
+ title = self._create_text_elem('title', 'Available API Versions',
+ type='text')
+ # Set this updated to the most recently updated version
+ recent = self._get_most_recent_update(versions)
+ updated = self._create_text_elem('updated', recent)
+
+ base_url = self._get_base_url(versions[0]['links'][0]['href'])
+ id = self._create_text_elem('id', base_url)
+ link = self._xml_doc.createElement('link')
+ link.setAttribute('rel', 'self')
+ link.setAttribute('href', base_url)
+
+ author = self._xml_doc.createElement('author')
+ author_name = self._create_text_elem('name', 'Rackspace')
+ author_uri = self._create_text_elem('uri', 'http://www.rackspace.com/')
+ author.appendChild(author_name)
+ author.appendChild(author_uri)
+
+ root.appendChild(title)
+ root.appendChild(updated)
+ root.appendChild(id)
+ root.appendChild(author)
+ root.appendChild(link)
+
+ def _create_version_entries(self, root, versions):
+ for version in versions:
+ entry = self._xml_doc.createElement('entry')
+
+ id = self._create_text_elem('id', version['links'][0]['href'])
+ title = self._create_text_elem('title',
+ 'Version %s' % version['id'],
+ type='text')
+ updated = self._create_text_elem('updated', version['updated'])
+
+ entry.appendChild(id)
+ entry.appendChild(title)
+ entry.appendChild(updated)
+
+ for link in version['links']:
+ link_node = self._xml_doc.createElement('link')
+ link_node.setAttribute('rel', link['rel'])
+ link_node.setAttribute('href', link['href'])
+ entry.appendChild(link_node)
+
+ content = self._create_text_elem('content',
+ 'Version %s %s (%s)' %
+ (version['id'],
+ version['status'],
+ version['updated']),
+ type='text')
+
+ entry.appendChild(content)
+ root.appendChild(entry)
+
+ def default(self, data):
+ self._xml_doc = minidom.Document()
+ node = self._xml_doc.createElementNS(self.xmlns, 'feed')
+ self._create_meta(node, data['versions'])
+ self._create_version_entries(node, data['versions'])
+
+ return self.to_xml_string(node)
diff --git a/nova/api/openstack/views/versions.py b/nova/api/openstack/views/versions.py
index d0145c94a..9fa8f49dc 100644
--- a/nova/api/openstack/views/versions.py
+++ b/nova/api/openstack/views/versions.py
@@ -36,6 +36,7 @@ class ViewBuilder(object):
version = {
"id": version_data["id"],
"status": version_data["status"],
+ "updated": version_data["updated"],
"links": self._build_links(version_data),
}
@@ -56,4 +57,4 @@ class ViewBuilder(object):
def generate_href(self, version_number):
"""Create an url that refers to a specific version_number."""
- return os.path.join(self.base_url, version_number)
+ return os.path.join(self.base_url, version_number) + '/'
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index c3f841aa5..1e5a5143d 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -20,21 +20,22 @@ LOG = logging.getLogger('nova.api.openstack.wsgi')
class Request(webob.Request):
"""Add some Openstack API-specific logic to the base webob.Request."""
- def best_match_content_type(self):
+ def best_match_content_type(self, supported_content_types=None):
"""Determine the requested response content-type.
Based on the query extension then the Accept header.
"""
- supported = ('application/json', 'application/xml')
+ supported_content_types = supported_content_types or \
+ ('application/json', 'application/xml')
parts = self.path.rsplit('.', 1)
if len(parts) > 1:
ctype = 'application/{0}'.format(parts[1])
- if ctype in supported:
+ if ctype in supported_content_types:
return ctype
- bm = self.accept.best_match(supported)
+ bm = self.accept.best_match(supported_content_types)
# default to application/json if we don't find a preference
return bm or 'application/json'
@@ -151,7 +152,12 @@ class RequestHeadersDeserializer(ActionDispatcher):
class RequestDeserializer(object):
"""Break up a Request object into more useful pieces."""
- def __init__(self, body_deserializers=None, headers_deserializer=None):
+ def __init__(self, body_deserializers=None, headers_deserializer=None,
+ supported_content_types=None):
+
+ self.supported_content_types = supported_content_types or \
+ ('application/json', 'application/xml')
+
self.body_deserializers = {
'application/xml': XMLDeserializer(),
'application/json': JSONDeserializer(),
@@ -213,7 +219,7 @@ class RequestDeserializer(object):
raise exception.InvalidContentType(content_type=content_type)
def get_expected_content_type(self, request):
- return request.best_match_content_type()
+ return request.best_match_content_type(self.supported_content_types)
def get_action_args(self, request_environment):
"""Parse dictionary created by routes library."""
@@ -412,6 +418,7 @@ class Resource(wsgi.Application):
serialized by requested content type.
"""
+
def __init__(self, controller, deserializer=None, serializer=None):
"""
:param controller: object that implement methods created by routes lib
@@ -441,7 +448,11 @@ class Resource(wsgi.Application):
msg = _("Malformed request body")
return faults.Fault(webob.exc.HTTPBadRequest(explanation=msg))
- action_result = self.dispatch(request, action, args)
+ try:
+ action_result = self.dispatch(request, action, args)
+ except webob.exc.HTTPException as ex:
+ LOG.info(_("HTTP exception thrown: %s"), unicode(ex))
+ action_result = faults.Fault(ex)
#TODO(bcwaldon): find a more elegant way to pass through non-dict types
if type(action_result) is dict or action_result is None:
diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py
index 2e02ec380..f7fd87bcd 100644
--- a/nova/api/openstack/zones.py
+++ b/nova/api/openstack/zones.py
@@ -27,7 +27,6 @@ from nova.scheduler import api
from nova.api.openstack import create_instance_helper as helper
from nova.api.openstack import common
-from nova.api.openstack import faults
from nova.api.openstack import wsgi
@@ -127,11 +126,8 @@ class Controller(object):
Returns a reservation ID (a UUID).
"""
result = None
- try:
- extra_values, result = self.helper.create_instance(req, body,
- self.compute_api.create_all_at_once)
- except faults.Fault, f:
- return f
+ extra_values, result = self.helper.create_instance(req, body,
+ self.compute_api.create_all_at_once)
reservation_id = result
return {'reservation_id': reservation_id}
diff --git a/nova/compute/api.py b/nova/compute/api.py
index acafc7760..67aa3c20f 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -868,39 +868,50 @@ class API(base.Base):
instance_id,
params=rebuild_params)
+ @scheduler_api.reroute_compute("revert_resize")
def revert_resize(self, context, instance_id):
"""Reverts a resize, deleting the 'new' instance in the process."""
context = context.elevated()
+ instance_ref = self._get_instance(context, instance_id,
+ 'revert_resize')
migration_ref = self.db.migration_get_by_instance_and_status(context,
- instance_id, 'finished')
+ instance_ref['uuid'], 'finished')
if not migration_ref:
raise exception.MigrationNotFoundByStatus(instance_id=instance_id,
status='finished')
params = {'migration_id': migration_ref['id']}
- self._cast_compute_message('revert_resize', context, instance_id,
- migration_ref['dest_compute'], params=params)
+ self._cast_compute_message('revert_resize', context,
+ instance_ref['uuid'],
+ migration_ref['source_compute'],
+ params=params)
+
self.db.migration_update(context, migration_ref['id'],
{'status': 'reverted'})
+ @scheduler_api.reroute_compute("confirm_resize")
def confirm_resize(self, context, instance_id):
"""Confirms a migration/resize and deletes the 'old' instance."""
context = context.elevated()
+ instance_ref = self._get_instance(context, instance_id,
+ 'confirm_resize')
migration_ref = self.db.migration_get_by_instance_and_status(context,
- instance_id, 'finished')
+ instance_ref['uuid'], 'finished')
if not migration_ref:
raise exception.MigrationNotFoundByStatus(instance_id=instance_id,
status='finished')
- instance_ref = self.db.instance_get(context, instance_id)
params = {'migration_id': migration_ref['id']}
- self._cast_compute_message('confirm_resize', context, instance_id,
- migration_ref['source_compute'], params=params)
+ self._cast_compute_message('confirm_resize', context,
+ instance_ref['uuid'],
+ migration_ref['dest_compute'],
+ params=params)
self.db.migration_update(context, migration_ref['id'],
{'status': 'confirmed'})
self.db.instance_update(context, instance_id,
{'host': migration_ref['dest_compute'], })
+ @scheduler_api.reroute_compute("resize")
def resize(self, context, instance_id, flavor_id=None):
"""Resize (ie, migrate) a running instance.
@@ -908,8 +919,8 @@ class API(base.Base):
the original flavor_id. If flavor_id is not None, the instance should
be migrated to a new host and resized to the new flavor_id.
"""
- instance = self.db.instance_get(context, instance_id)
- current_instance_type = instance['instance_type']
+ instance_ref = self._get_instance(context, instance_id, 'resize')
+ current_instance_type = instance_ref['instance_type']
# If flavor_id is not provided, only migrate the instance.
if not flavor_id:
@@ -937,10 +948,11 @@ class API(base.Base):
raise exception.ApiError(_("Invalid flavor: cannot use"
"the same flavor. "))
+ instance_ref = self._get_instance(context, instance_id, 'resize')
self._cast_scheduler_message(context,
{"method": "prep_resize",
"args": {"topic": FLAGS.compute_topic,
- "instance_id": instance_id,
+ "instance_id": instance_ref['uuid'],
"flavor_id": new_instance_type['id']}})
@scheduler_api.reroute_compute("add_fixed_ip")
diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py
index 1d246e445..c13a629a9 100644
--- a/nova/compute/instance_types.py
+++ b/nova/compute/instance_types.py
@@ -112,7 +112,7 @@ def get_instance_type(id):
return get_default_instance_type()
try:
ctxt = context.get_admin_context()
- return db.instance_type_get_by_id(ctxt, id)
+ return db.instance_type_get(ctxt, id)
except exception.DBError:
raise exception.ApiError(_("Unknown instance type: %s") % id)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index c9afa8072..5819a520a 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -91,6 +91,10 @@ def checks_instance_lock(function):
"""Decorator to prevent action against locked instances for non-admins."""
@functools.wraps(function)
def decorated_function(self, context, instance_id, *args, **kwargs):
+ #TODO(anyone): this being called instance_id is forcing a slightly
+ # confusing convention of pushing instance_uuids
+ # through an "instance_id" key in the queue args dict when
+ # casting through the compute API
LOG.info(_("check_instance_lock: decorating: |%s|"), function,
context=context)
LOG.info(_("check_instance_lock: arguments: |%(self)s| |%(context)s|"
@@ -671,8 +675,10 @@ class ComputeManager(manager.SchedulerDependentManager):
@checks_instance_lock
def confirm_resize(self, context, instance_id, migration_id):
"""Destroys the source instance."""
- context = context.elevated()
- instance_ref = self.db.instance_get(context, instance_id)
+ migration_ref = self.db.migration_get(context, migration_id)
+ instance_ref = self.db.instance_get_by_uuid(context,
+ migration_ref.instance_uuid)
+
self.driver.destroy(instance_ref)
usage_info = utils.usage_from_instance(instance_ref)
notifier.notify('compute.%s' % self.host,
@@ -689,17 +695,16 @@ class ComputeManager(manager.SchedulerDependentManager):
source machine.
"""
- instance_ref = self.db.instance_get(context, instance_id)
migration_ref = self.db.migration_get(context, migration_id)
+ instance_ref = self.db.instance_get_by_uuid(context,
+ migration_ref.instance_uuid)
self.driver.destroy(instance_ref)
topic = self.db.queue_get_for(context, FLAGS.compute_topic,
instance_ref['host'])
rpc.cast(context, topic,
{'method': 'finish_revert_resize',
- 'args': {
- 'migration_id': migration_ref['id'],
- 'instance_id': instance_id, },
+ 'args': {'migration_id': migration_ref['id']},
})
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
@@ -711,14 +716,16 @@ class ComputeManager(manager.SchedulerDependentManager):
in the database.
"""
- instance_ref = self.db.instance_get(context, instance_id)
migration_ref = self.db.migration_get(context, migration_id)
+ instance_ref = self.db.instance_get_by_uuid(context,
+ migration_ref.instance_uuid)
+
instance_type = self.db.instance_type_get_by_flavor_id(context,
migration_ref['old_flavor_id'])
# Just roll back the record. There's no need to resize down since
# the 'old' VM already has the preferred attributes
- self.db.instance_update(context, instance_id,
+ self.db.instance_update(context, instance_ref['uuid'],
dict(memory_mb=instance_type['memory_mb'],
vcpus=instance_type['vcpus'],
local_gb=instance_type['local_gb'],
@@ -742,17 +749,23 @@ class ComputeManager(manager.SchedulerDependentManager):
"""
context = context.elevated()
- instance_ref = self.db.instance_get(context, instance_id)
+
+ # Because of checks_instance_lock, this must currently be called
+ # instance_id. However, the compute API is always passing the UUID
+ # of the instance down
+ instance_ref = self.db.instance_get_by_uuid(context, instance_id)
if instance_ref['host'] == FLAGS.host:
raise exception.Error(_(
'Migration error: destination same as source!'))
- old_instance_type = self.db.instance_type_get_by_id(context,
+ old_instance_type = self.db.instance_type_get(context,
instance_ref['instance_type_id'])
+ new_instance_type = self.db.instance_type_get_by_flavor_id(context,
+ flavor_id)
migration_ref = self.db.migration_create(context,
- {'instance_id': instance_id,
+ {'instance_uuid': instance_ref['uuid'],
'source_compute': instance_ref['host'],
'dest_compute': FLAGS.host,
'dest_host': self.driver.get_host_ip_addr(),
@@ -760,22 +773,18 @@ class ComputeManager(manager.SchedulerDependentManager):
'new_flavor_id': flavor_id,
'status': 'pre-migrating'})
- LOG.audit(_('instance %s: migrating to '), instance_id,
+ LOG.audit(_('instance %s: migrating'), instance_ref['uuid'],
context=context)
topic = self.db.queue_get_for(context, FLAGS.compute_topic,
instance_ref['host'])
rpc.cast(context, topic,
{'method': 'resize_instance',
- 'args': {
- 'migration_id': migration_ref['id'],
- 'instance_id': instance_id, },
- })
+ 'args': {'instance_id': instance_ref['uuid'],
+ 'migration_id': migration_ref['id']}})
- instance_type = self.db.instance_type_get_by_flavor_id(context,
- flavor_id)
usage_info = utils.usage_from_instance(instance_ref,
- new_instance_type=instance_type['name'],
- new_instance_type_id=instance_type['id'])
+ new_instance_type=new_instance_type['name'],
+ new_instance_type_id=new_instance_type['id'])
notifier.notify('compute.%s' % self.host,
'compute.instance.resize.prep',
notifier.INFO,
@@ -786,7 +795,9 @@ class ComputeManager(manager.SchedulerDependentManager):
def resize_instance(self, context, instance_id, migration_id):
"""Starts the migration of a running instance to another host."""
migration_ref = self.db.migration_get(context, migration_id)
- instance_ref = self.db.instance_get(context, instance_id)
+ instance_ref = self.db.instance_get_by_uuid(context,
+ migration_ref.instance_uuid)
+
self.db.migration_update(context,
migration_id,
{'status': 'migrating'})
@@ -802,10 +813,11 @@ class ComputeManager(manager.SchedulerDependentManager):
topic = self.db.queue_get_for(context,
FLAGS.compute_topic,
migration_ref['dest_compute'])
+ params = {'migration_id': migration_id,
+ 'disk_info': disk_info,
+ 'instance_id': instance_ref['uuid']}
rpc.cast(context, topic, {'method': 'finish_resize',
- 'args': {'migration_id': migration_id,
- 'instance_id': instance_id,
- 'disk_info': disk_info}})
+ 'args': params})
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
@checks_instance_lock
@@ -817,24 +829,21 @@ class ComputeManager(manager.SchedulerDependentManager):
"""
migration_ref = self.db.migration_get(context, migration_id)
- instance_ref = self.db.instance_get(context,
- migration_ref['instance_id'])
- # TODO(mdietz): apply the rest of the instance_type attributes going
- # after they're supported
+ instance_ref = self.db.instance_get_by_uuid(context,
+ migration_ref.instance_uuid)
instance_type = self.db.instance_type_get_by_flavor_id(context,
migration_ref['new_flavor_id'])
- self.db.instance_update(context, instance_id,
+ self.db.instance_update(context, instance_ref.uuid,
dict(instance_type_id=instance_type['id'],
memory_mb=instance_type['memory_mb'],
vcpus=instance_type['vcpus'],
local_gb=instance_type['local_gb']))
- # reload the updated instance ref
- # FIXME(mdietz): is there reload functionality?
- instance = self.db.instance_get(context, instance_id)
+ instance_ref = self.db.instance_get_by_uuid(context,
+ instance_ref.uuid)
network_info = self.network_api.get_instance_nw_info(context,
- instance)
- self.driver.finish_resize(instance, disk_info, network_info)
+ instance_ref)
+ self.driver.finish_resize(instance_ref, disk_info, network_info)
self.db.migration_update(context, migration_id,
{'status': 'finished', })
@@ -966,7 +975,11 @@ class ComputeManager(manager.SchedulerDependentManager):
context = context.elevated()
LOG.debug(_('instance %s: getting locked state'), instance_id,
context=context)
- instance_ref = self.db.instance_get(context, instance_id)
+ if utils.is_uuid_like(instance_id):
+ uuid = instance_id
+ instance_ref = self.db.instance_get_by_uuid(context, uuid)
+ else:
+ instance_ref = self.db.instance_get(context, instance_id)
return instance_ref['locked']
@checks_instance_lock
diff --git a/nova/db/api.py b/nova/db/api.py
index 2efbf957d..d69732920 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -1311,9 +1311,9 @@ def instance_type_get_all(context, inactive=False):
return IMPL.instance_type_get_all(context, inactive)
-def instance_type_get_by_id(context, id):
+def instance_type_get(context, id):
"""Get instance type by id."""
- return IMPL.instance_type_get_by_id(context, id)
+ return IMPL.instance_type_get(context, id)
def instance_type_get_by_name(context, name):
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 33bdd767a..ba03cabbc 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1349,7 +1349,11 @@ def instance_update(context, instance_id, values):
instance_metadata_update_or_create(context, instance_id,
values.pop('metadata'))
with session.begin():
- instance_ref = instance_get(context, instance_id, session=session)
+ if utils.is_uuid_like(instance_id):
+ instance_ref = instance_get_by_uuid(context, instance_id,
+ session=session)
+ else:
+ instance_ref = instance_get(context, instance_id, session=session)
instance_ref.update(values)
instance_ref.save(session=session)
return instance_ref
@@ -2819,13 +2823,13 @@ def migration_get(context, id, session=None):
@require_admin_context
-def migration_get_by_instance_and_status(context, instance_id, status):
+def migration_get_by_instance_and_status(context, instance_uuid, status):
session = get_session()
result = session.query(models.Migration).\
- filter_by(instance_id=instance_id).\
+ filter_by(instance_uuid=instance_uuid).\
filter_by(status=status).first()
if not result:
- raise exception.MigrationNotFoundByStatus(instance_id=instance_id,
+ raise exception.MigrationNotFoundByStatus(instance_id=instance_uuid,
status=status)
return result
@@ -3006,7 +3010,7 @@ def instance_type_get_all(context, inactive=False):
@require_context
-def instance_type_get_by_id(context, id):
+def instance_type_get(context, id):
"""Returns a dict describing specific instance_type"""
session = get_session()
inst_type = session.query(models.InstanceTypes).\
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/034_change_instance_id_in_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/034_change_instance_id_in_migrations.py
new file mode 100644
index 000000000..b002ba064
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/034_change_instance_id_in_migrations.py
@@ -0,0 +1,43 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# 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 sqlalchemy import *
+
+from sqlalchemy import Column, Integer, String, MetaData, Table
+
+meta = MetaData()
+
+
+#
+# Tables to alter
+#
+#
+
+instance_id = Column('instance_id', Integer())
+instance_uuid = Column('instance_uuid', String(255))
+
+
+def upgrade(migrate_engine):
+ meta.bind = migrate_engine
+ migrations = Table('migrations', meta, autoload=True)
+ migrations.create_column(instance_uuid)
+ migrations.c.instance_id.drop()
+
+
+def downgrade(migrate_engine):
+ meta.bind = migrate_engine
+ migrations = Table('migrations', meta, autoload=True)
+ migrations.c.instance_uuid.drop()
+ migrations.create_column(instance_id)
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 45e0f89c9..c1150f7ca 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -529,7 +529,8 @@ class Migration(BASE, NovaBase):
dest_host = Column(String(255))
old_flavor_id = Column(Integer())
new_flavor_id = Column(Integer())
- instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True)
+ instance_uuid = Column(String(255), ForeignKey('instances.uuid'),
+ nullable=True)
#TODO(_cerberus_): enum
status = Column(String(255))
diff --git a/nova/exception.py b/nova/exception.py
index cb015e694..38e705417 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -78,8 +78,8 @@ def wrap_db_error(f):
except Exception, e:
LOG.exception(_('DB exception wrapped.'))
raise DBError(e)
- return _wrap
_wrap.func_name = f.func_name
+ return _wrap
def wrap_exception(notifier=None, publisher_id=None, event_type=None,
diff --git a/nova/image/glance.py b/nova/image/glance.py
index 55d948a32..5c2dc957b 100644
--- a/nova/image/glance.py
+++ b/nova/image/glance.py
@@ -89,6 +89,10 @@ class GlanceImageService(service.BaseImageService):
# `get_images` here because we need `is_public` and `properties`
# included so we can filter by user
filtered = []
+ filters = filters or {}
+ if 'is_public' not in filters:
+ # NOTE(vish): don't filter out private images
+ filters['is_public'] = 'none'
image_metas = self.client.get_images_detailed(filters=filters,
marker=marker,
limit=limit)
@@ -101,6 +105,10 @@ class GlanceImageService(service.BaseImageService):
def detail(self, context, filters=None, marker=None, limit=None):
"""Calls out to Glance for a list of detailed image information."""
filtered = []
+ filters = filters or {}
+ if 'is_public' not in filters:
+ # NOTE(vish): don't filter out private images
+ filters['is_public'] = 'none'
image_metas = self.client.get_images_detailed(filters=filters,
marker=marker,
limit=limit)
diff --git a/nova/image/s3.py b/nova/image/s3.py
index 4a3df98ba..c313c7a13 100644
--- a/nova/image/s3.py
+++ b/nova/image/s3.py
@@ -168,7 +168,7 @@ class S3ImageService(service.BaseImageService):
metadata.update({'disk_format': image_format,
'container_format': image_format,
'status': 'queued',
- 'is_public': True,
+ 'is_public': False,
'properties': properties})
metadata['properties']['image_state'] = 'pending'
image = self.service.create(context, metadata)
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 34001657b..75c3f668d 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -429,8 +429,7 @@ class NetworkManager(manager.SchedulerDependentManager):
# TODO(tr3buchet) should handle floating IPs as well?
fixed_ips = self.db.fixed_ip_get_by_instance(context, instance_id)
vifs = self.db.virtual_interface_get_by_instance(context, instance_id)
- flavor = self.db.instance_type_get_by_id(context,
- instance_type_id)
+ flavor = self.db.instance_type_get(context, instance_type_id)
network_info = []
# a vif has an address, instance_id, and network_id
# it is also joined to the instance and network given by those IDs
diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py
index 689647cc6..4ac35b26b 100644
--- a/nova/tests/api/openstack/test_flavors.py
+++ b/nova/tests/api/openstack/test_flavors.py
@@ -18,12 +18,14 @@
import json
import stubout
import webob
+import xml.dom.minidom as minidom
+from nova.api.openstack import flavors
import nova.db.api
-from nova import context
from nova import exception
from nova import test
from nova.tests.api.openstack import fakes
+from nova import wsgi
def stub_flavor(flavorid, name, memory_mb="256", local_gb="10"):
@@ -64,7 +66,6 @@ class FlavorsTest(test.TestCase):
return_instance_types)
self.stubs.Set(nova.db.api, "instance_type_get_by_flavor_id",
return_instance_type_by_flavor_id)
- self.context = context.get_admin_context()
def tearDown(self):
self.stubs.UnsetAll()
@@ -146,61 +147,65 @@ class FlavorsTest(test.TestCase):
req.environ['api.version'] = '1.1'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
- flavor = json.loads(res.body)["flavor"]
+ flavor = json.loads(res.body)
expected = {
- "id": "12",
- "name": "flavor 12",
- "ram": "256",
- "disk": "10",
- "links": [
- {
- "rel": "self",
- "href": "http://localhost/v1.1/flavors/12",
- },
- {
- "rel": "bookmark",
- "href": "http://localhost/flavors/12",
- },
- ],
- }
- self.assertEqual(flavor, expected)
-
- def test_get_flavor_list_v1_1(self):
- req = webob.Request.blank('/v1.1/flavors')
- req.environ['api.version'] = '1.1'
- res = req.get_response(fakes.wsgi_app())
- self.assertEqual(res.status_int, 200)
- flavor = json.loads(res.body)["flavors"]
- expected = [
- {
- "id": "1",
- "name": "flavor 1",
- "links": [
- {
- "rel": "self",
- "href": "http://localhost/v1.1/flavors/1",
- },
- {
- "rel": "bookmark",
- "href": "http://localhost/flavors/1",
- },
- ],
- },
- {
- "id": "2",
- "name": "flavor 2",
+ "flavor": {
+ "id": "12",
+ "name": "flavor 12",
+ "ram": "256",
+ "disk": "10",
"links": [
{
"rel": "self",
- "href": "http://localhost/v1.1/flavors/2",
+ "href": "http://localhost/v1.1/flavors/12",
},
{
"rel": "bookmark",
- "href": "http://localhost/flavors/2",
+ "href": "http://localhost/flavors/12",
},
],
},
- ]
+ }
+ self.assertEqual(flavor, expected)
+
+ def test_get_flavor_list_v1_1(self):
+ req = webob.Request.blank('/v1.1/flavors')
+ req.environ['api.version'] = '1.1'
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ flavor = json.loads(res.body)
+ expected = {
+ "flavors": [
+ {
+ "id": "1",
+ "name": "flavor 1",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/flavors/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/flavors/1",
+ },
+ ],
+ },
+ {
+ "id": "2",
+ "name": "flavor 2",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/flavors/2",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/flavors/2",
+ },
+ ],
+ },
+ ],
+ }
self.assertEqual(flavor, expected)
def test_get_flavor_list_detail_v1_1(self):
@@ -208,52 +213,273 @@ class FlavorsTest(test.TestCase):
req.environ['api.version'] = '1.1'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
- flavor = json.loads(res.body)["flavors"]
- expected = [
- {
- "id": "1",
- "name": "flavor 1",
+ flavor = json.loads(res.body)
+ expected = {
+ "flavors": [
+ {
+ "id": "1",
+ "name": "flavor 1",
+ "ram": "256",
+ "disk": "10",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/flavors/1",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/flavors/1",
+ },
+ ],
+ },
+ {
+ "id": "2",
+ "name": "flavor 2",
+ "ram": "256",
+ "disk": "10",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/flavors/2",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/flavors/2",
+ },
+ ],
+ },
+ ],
+ }
+ self.assertEqual(flavor, expected)
+
+ def test_get_empty_flavor_list_v1_1(self):
+ def _return_empty(self):
+ return {}
+ self.stubs.Set(nova.db.api, "instance_type_get_all", _return_empty)
+
+ req = webob.Request.blank('/v1.1/flavors')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ flavors = json.loads(res.body)["flavors"]
+ expected = []
+ self.assertEqual(flavors, expected)
+
+
+class FlavorsXMLSerializationTest(test.TestCase):
+
+ def test_show(self):
+ serializer = flavors.FlavorXMLSerializer()
+
+ input = {
+ "flavor": {
+ "id": "12",
+ "name": "asdf",
"ram": "256",
"disk": "10",
"links": [
{
"rel": "self",
- "href": "http://localhost/v1.1/flavors/1",
+ "href": "http://localhost/v1.1/flavors/12",
},
{
"rel": "bookmark",
- "href": "http://localhost/flavors/1",
+ "href": "http://localhost/flavors/12",
},
],
},
- {
- "id": "2",
- "name": "flavor 2",
- "ram": "256",
- "disk": "10",
+ }
+
+ output = serializer.serialize(input, 'show')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <flavor xmlns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ id="12"
+ name="asdf"
+ ram="256"
+ disk="10">
+ <atom:link href="http://localhost/v1.1/flavors/12" rel="self"/>
+ <atom:link href="http://localhost/flavors/12" rel="bookmark"/>
+ </flavor>
+ """.replace(" ", ""))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_show_handles_integers(self):
+ serializer = flavors.FlavorXMLSerializer()
+
+ input = {
+ "flavor": {
+ "id": 12,
+ "name": "asdf",
+ "ram": 256,
+ "disk": 10,
"links": [
{
"rel": "self",
- "href": "http://localhost/v1.1/flavors/2",
+ "href": "http://localhost/v1.1/flavors/12",
},
{
"rel": "bookmark",
- "href": "http://localhost/flavors/2",
+ "href": "http://localhost/flavors/12",
},
],
},
- ]
- self.assertEqual(flavor, expected)
+ }
- def test_get_empty_flavor_list_v1_1(self):
- def _return_empty(self):
- return {}
- self.stubs.Set(nova.db.api, "instance_type_get_all",
- _return_empty)
+ output = serializer.serialize(input, 'show')
+ actual = minidom.parseString(output.replace(" ", ""))
- req = webob.Request.blank('/v1.1/flavors')
- res = req.get_response(fakes.wsgi_app())
- self.assertEqual(res.status_int, 200)
- flavors = json.loads(res.body)["flavors"]
- expected = []
- self.assertEqual(flavors, expected)
+ expected = minidom.parseString("""
+ <flavor xmlns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ id="12"
+ name="asdf"
+ ram="256"
+ disk="10">
+ <atom:link href="http://localhost/v1.1/flavors/12" rel="self"/>
+ <atom:link href="http://localhost/flavors/12" rel="bookmark"/>
+ </flavor>
+ """.replace(" ", ""))
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_detail(self):
+ serializer = flavors.FlavorXMLSerializer()
+
+ input = {
+ "flavors": [
+ {
+ "id": "23",
+ "name": "flavor 23",
+ "ram": "512",
+ "disk": "20",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/flavors/23",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/flavors/23",
+ },
+ ],
+ }, {
+ "id": "13",
+ "name": "flavor 13",
+ "ram": "256",
+ "disk": "10",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/flavors/13",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/flavors/13",
+ },
+ ],
+ },
+ ],
+ }
+
+ output = serializer.serialize(input, 'detail')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <flavors xmlns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+ <flavor id="23"
+ name="flavor 23"
+ ram="512"
+ disk="20">
+ <atom:link href="http://localhost/v1.1/flavors/23" rel="self"/>
+ <atom:link href="http://localhost/flavors/23" rel="bookmark"/>
+ </flavor>
+ <flavor id="13"
+ name="flavor 13"
+ ram="256"
+ disk="10">
+ <atom:link href="http://localhost/v1.1/flavors/13" rel="self"/>
+ <atom:link href="http://localhost/flavors/13" rel="bookmark"/>
+ </flavor>
+ </flavors>
+ """.replace(" ", "") % locals())
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_index(self):
+ serializer = flavors.FlavorXMLSerializer()
+
+ input = {
+ "flavors": [
+ {
+ "id": "23",
+ "name": "flavor 23",
+ "ram": "512",
+ "disk": "20",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/flavors/23",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/flavors/23",
+ },
+ ],
+ }, {
+ "id": "13",
+ "name": "flavor 13",
+ "ram": "256",
+ "disk": "10",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/flavors/13",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://localhost/flavors/13",
+ },
+ ],
+ },
+ ],
+ }
+
+ output = serializer.serialize(input, 'index')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <flavors xmlns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+ <flavor id="23" name="flavor 23">
+ <atom:link href="http://localhost/v1.1/flavors/23" rel="self"/>
+ <atom:link href="http://localhost/flavors/23" rel="bookmark"/>
+ </flavor>
+ <flavor id="13" name="flavor 13">
+ <atom:link href="http://localhost/v1.1/flavors/13" rel="self"/>
+ <atom:link href="http://localhost/flavors/13" rel="bookmark"/>
+ </flavor>
+ </flavors>
+ """.replace(" ", "") % locals())
+
+ self.assertEqual(expected.toxml(), actual.toxml())
+
+ def test_index_empty(self):
+ serializer = flavors.FlavorXMLSerializer()
+
+ input = {
+ "flavors": [],
+ }
+
+ output = serializer.serialize(input, 'index')
+ actual = minidom.parseString(output.replace(" ", ""))
+
+ expected = minidom.parseString("""
+ <flavors xmlns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns:atom="http://www.w3.org/2005/Atom" />
+ """.replace(" ", "") % locals())
+
+ self.assertEqual(expected.toxml(), actual.toxml())
diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py
index 534460d46..17f2fb755 100644
--- a/nova/tests/api/openstack/test_images.py
+++ b/nova/tests/api/openstack/test_images.py
@@ -803,154 +803,206 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
self.assertDictListMatch(expected, response_list)
def test_image_filter_with_name(self):
- mocker = mox.Mox()
- image_service = mocker.CreateMockAnything()
+ image_service = self.mox.CreateMockAnything()
context = object()
filters = {'name': 'testname'}
- image_service.index(
- context, filters=filters).AndReturn([])
- mocker.ReplayAll()
- request = webob.Request.blank(
- '/v1.1/images?name=testname')
+ image_service.index(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images?name=testname')
request.environ['nova.context'] = context
controller = images.ControllerV11(image_service=image_service)
controller.index(request)
- mocker.VerifyAll()
+ self.mox.VerifyAll()
def test_image_filter_with_status(self):
- mocker = mox.Mox()
- image_service = mocker.CreateMockAnything()
+ image_service = self.mox.CreateMockAnything()
context = object()
filters = {'status': 'ACTIVE'}
- image_service.index(
- context, filters=filters).AndReturn([])
- mocker.ReplayAll()
- request = webob.Request.blank(
- '/v1.1/images?status=ACTIVE')
+ image_service.index(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images?status=ACTIVE')
request.environ['nova.context'] = context
controller = images.ControllerV11(image_service=image_service)
controller.index(request)
- mocker.VerifyAll()
+ self.mox.VerifyAll()
def test_image_filter_with_property(self):
- mocker = mox.Mox()
- image_service = mocker.CreateMockAnything()
+ image_service = self.mox.CreateMockAnything()
context = object()
filters = {'property-test': '3'}
- image_service.index(
- context, filters=filters).AndReturn([])
- mocker.ReplayAll()
- request = webob.Request.blank(
- '/v1.1/images?property-test=3')
+ image_service.index(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images?property-test=3')
+ request.environ['nova.context'] = context
+ controller = images.ControllerV11(image_service=image_service)
+ controller.index(request)
+ self.mox.VerifyAll()
+
+ def test_image_filter_server(self):
+ image_service = self.mox.CreateMockAnything()
+ context = object()
+ # 'server' should be converted to 'property-instance_ref'
+ filters = {'property-instance_ref': 'http://localhost:8774/servers/12'}
+ image_service.index(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images?server='
+ 'http://localhost:8774/servers/12')
request.environ['nova.context'] = context
controller = images.ControllerV11(image_service=image_service)
controller.index(request)
- mocker.VerifyAll()
+ self.mox.VerifyAll()
+
+ def test_image_filter_changes_since(self):
+ image_service = self.mox.CreateMockAnything()
+ context = object()
+ filters = {'changes-since': '2011-01-24T17:08Z'}
+ image_service.index(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images?changes-since='
+ '2011-01-24T17:08Z')
+ request.environ['nova.context'] = context
+ controller = images.ControllerV11(image_service=image_service)
+ controller.index(request)
+ self.mox.VerifyAll()
+
+ def test_image_filter_with_type(self):
+ image_service = self.mox.CreateMockAnything()
+ context = object()
+ filters = {'property-image_type': 'BASE'}
+ image_service.index(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images?type=BASE')
+ request.environ['nova.context'] = context
+ controller = images.ControllerV11(image_service=image_service)
+ controller.index(request)
+ self.mox.VerifyAll()
def test_image_filter_not_supported(self):
- mocker = mox.Mox()
- image_service = mocker.CreateMockAnything()
+ image_service = self.mox.CreateMockAnything()
context = object()
filters = {'status': 'ACTIVE'}
- image_service.index(
- context, filters=filters).AndReturn([])
- mocker.ReplayAll()
- request = webob.Request.blank(
- '/v1.1/images?status=ACTIVE&UNSUPPORTEDFILTER=testname')
+ image_service.detail(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images?status=ACTIVE&'
+ 'UNSUPPORTEDFILTER=testname')
request.environ['nova.context'] = context
controller = images.ControllerV11(image_service=image_service)
- controller.index(request)
- mocker.VerifyAll()
+ controller.detail(request)
+ self.mox.VerifyAll()
def test_image_no_filters(self):
- mocker = mox.Mox()
- image_service = mocker.CreateMockAnything()
+ image_service = self.mox.CreateMockAnything()
context = object()
filters = {}
image_service.index(
context, filters=filters).AndReturn([])
- mocker.ReplayAll()
+ self.mox.ReplayAll()
request = webob.Request.blank(
'/v1.1/images')
request.environ['nova.context'] = context
controller = images.ControllerV11(image_service=image_service)
controller.index(request)
- mocker.VerifyAll()
+ self.mox.VerifyAll()
def test_image_detail_filter_with_name(self):
- mocker = mox.Mox()
- image_service = mocker.CreateMockAnything()
+ image_service = self.mox.CreateMockAnything()
context = object()
filters = {'name': 'testname'}
- image_service.detail(
- context, filters=filters).AndReturn([])
- mocker.ReplayAll()
- request = webob.Request.blank(
- '/v1.1/images/detail?name=testname')
+ image_service.detail(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images/detail?name=testname')
request.environ['nova.context'] = context
controller = images.ControllerV11(image_service=image_service)
controller.detail(request)
- mocker.VerifyAll()
+ self.mox.VerifyAll()
def test_image_detail_filter_with_status(self):
- mocker = mox.Mox()
- image_service = mocker.CreateMockAnything()
+ image_service = self.mox.CreateMockAnything()
context = object()
filters = {'status': 'ACTIVE'}
- image_service.detail(
- context, filters=filters).AndReturn([])
- mocker.ReplayAll()
- request = webob.Request.blank(
- '/v1.1/images/detail?status=ACTIVE')
+ image_service.detail(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images/detail?status=ACTIVE')
request.environ['nova.context'] = context
controller = images.ControllerV11(image_service=image_service)
controller.detail(request)
- mocker.VerifyAll()
+ self.mox.VerifyAll()
def test_image_detail_filter_with_property(self):
- mocker = mox.Mox()
- image_service = mocker.CreateMockAnything()
+ image_service = self.mox.CreateMockAnything()
context = object()
filters = {'property-test': '3'}
- image_service.detail(
- context, filters=filters).AndReturn([])
- mocker.ReplayAll()
- request = webob.Request.blank(
- '/v1.1/images/detail?property-test=3')
+ image_service.detail(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images/detail?property-test=3')
request.environ['nova.context'] = context
controller = images.ControllerV11(image_service=image_service)
controller.detail(request)
- mocker.VerifyAll()
+ self.mox.VerifyAll()
+
+ def test_image_detail_filter_server(self):
+ image_service = self.mox.CreateMockAnything()
+ context = object()
+ # 'server' should be converted to 'property-instance_ref'
+ filters = {'property-instance_ref': 'http://localhost:8774/servers/12'}
+ image_service.index(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images/detail?server='
+ 'http://localhost:8774/servers/12')
+ request.environ['nova.context'] = context
+ controller = images.ControllerV11(image_service=image_service)
+ controller.index(request)
+ self.mox.VerifyAll()
+
+ def test_image_detail_filter_changes_since(self):
+ image_service = self.mox.CreateMockAnything()
+ context = object()
+ filters = {'changes-since': '2011-01-24T17:08Z'}
+ image_service.index(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images/detail?changes-since='
+ '2011-01-24T17:08Z')
+ request.environ['nova.context'] = context
+ controller = images.ControllerV11(image_service=image_service)
+ controller.index(request)
+ self.mox.VerifyAll()
+
+ def test_image_detail_filter_with_type(self):
+ image_service = self.mox.CreateMockAnything()
+ context = object()
+ filters = {'property-image_type': 'BASE'}
+ image_service.index(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images/detail?type=BASE')
+ request.environ['nova.context'] = context
+ controller = images.ControllerV11(image_service=image_service)
+ controller.index(request)
+ self.mox.VerifyAll()
def test_image_detail_filter_not_supported(self):
- mocker = mox.Mox()
- image_service = mocker.CreateMockAnything()
+ image_service = self.mox.CreateMockAnything()
context = object()
filters = {'status': 'ACTIVE'}
- image_service.detail(
- context, filters=filters).AndReturn([])
- mocker.ReplayAll()
- request = webob.Request.blank(
- '/v1.1/images/detail?status=ACTIVE&UNSUPPORTEDFILTER=testname')
+ image_service.detail(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images/detail?status=ACTIVE&'
+ 'UNSUPPORTEDFILTER=testname')
request.environ['nova.context'] = context
controller = images.ControllerV11(image_service=image_service)
controller.detail(request)
- mocker.VerifyAll()
+ self.mox.VerifyAll()
def test_image_detail_no_filters(self):
- mocker = mox.Mox()
- image_service = mocker.CreateMockAnything()
+ image_service = self.mox.CreateMockAnything()
context = object()
filters = {}
- image_service.detail(
- context, filters=filters).AndReturn([])
- mocker.ReplayAll()
- request = webob.Request.blank(
- '/v1.1/images/detail')
+ image_service.detail(context, filters=filters).AndReturn([])
+ self.mox.ReplayAll()
+ request = webob.Request.blank('/v1.1/images/detail')
request.environ['nova.context'] = context
controller = images.ControllerV11(image_service=image_service)
controller.detail(request)
- mocker.VerifyAll()
+ self.mox.VerifyAll()
def test_get_image_found(self):
req = webob.Request.blank('/v1.0/images/123')
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 7ceb0c075..1c66c1aa6 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -1790,7 +1790,7 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.compute.api.API, 'resize', resize_mock)
res = req.get_response(fakes.wsgi_app())
- self.assertEqual(res.status_int, 400)
+ self.assertEqual(res.status_int, 500)
def test_resized_server_has_correct_status(self):
req = self.webreq('/1', 'GET')
diff --git a/nova/tests/api/openstack/test_versions.py b/nova/tests/api/openstack/test_versions.py
index fd8d50904..0eae18a62 100644
--- a/nova/tests/api/openstack/test_versions.py
+++ b/nova/tests/api/openstack/test_versions.py
@@ -21,6 +21,7 @@ import webob
from nova import context
from nova import test
from nova.tests.api.openstack import fakes
+from nova.api.openstack import versions
from nova.api.openstack import views
@@ -43,19 +44,21 @@ class VersionsTest(test.TestCase):
{
"id": "v1.1",
"status": "CURRENT",
+ "updated": "2011-07-18T11:30:00Z",
"links": [
{
"rel": "self",
- "href": "http://localhost/v1.1",
+ "href": "http://localhost/v1.1/",
}],
},
{
"id": "v1.0",
"status": "DEPRECATED",
+ "updated": "2010-10-09T11:30:00Z",
"links": [
{
"rel": "self",
- "href": "http://localhost/v1.0",
+ "href": "http://localhost/v1.0/",
}],
},
]
@@ -69,15 +72,12 @@ class VersionsTest(test.TestCase):
self.assertEqual(res.content_type, "application/xml")
expected = """<versions>
- <version id="v1.1" status="CURRENT">
- <links>
- <link href="http://localhost/v1.1" rel="self"/>
- </links>
+ <version id="v1.1" status="CURRENT" updated="2011-07-18T11:30:00Z">
+ <atom:link href="http://localhost/v1.1/" rel="self"/>
</version>
- <version id="v1.0" status="DEPRECATED">
- <links>
- <link href="http://localhost/v1.0" rel="self"/>
- </links>
+ <version id="v1.0" status="DEPRECATED"
+ updated="2010-10-09T11:30:00Z">
+ <atom:link href="http://localhost/v1.0/" rel="self"/>
</version>
</versions>""".replace(" ", "").replace("\n", "")
@@ -85,21 +85,64 @@ class VersionsTest(test.TestCase):
self.assertEqual(expected, actual)
+ def test_get_version_list_atom(self):
+ req = webob.Request.blank('/')
+ req.accept = "application/atom+xml"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual(res.content_type, "application/atom+xml")
+
+ expected = """
+ <feed xmlns="http://www.w3.org/2005/Atom">
+ <title type="text">Available API Versions</title>
+ <updated>2011-07-18T11:30:00Z</updated>
+ <id>http://localhost/</id>
+ <author>
+ <name>Rackspace</name>
+ <uri>http://www.rackspace.com/</uri>
+ </author>
+ <link href="http://localhost/" rel="self"/>
+ <entry>
+ <id>http://localhost/v1.1/</id>
+ <title type="text">Version v1.1</title>
+ <updated>2011-07-18T11:30:00Z</updated>
+ <link href="http://localhost/v1.1/" rel="self"/>
+ <content type="text">
+ Version v1.1 CURRENT (2011-07-18T11:30:00Z)
+ </content>
+ </entry>
+ <entry>
+ <id>http://localhost/v1.0/</id>
+ <title type="text">Version v1.0</title>
+ <updated>2010-10-09T11:30:00Z</updated>
+ <link href="http://localhost/v1.0/" rel="self"/>
+ <content type="text">
+ Version v1.0 DEPRECATED (2010-10-09T11:30:00Z)
+ </content>
+ </entry>
+ </feed>
+ """.replace(" ", "").replace("\n", "")
+
+ actual = res.body.replace(" ", "").replace("\n", "")
+
+ self.assertEqual(expected, actual)
+
def test_view_builder(self):
base_url = "http://example.org/"
version_data = {
"id": "3.2.1",
"status": "CURRENT",
- }
+ "updated": "2011-07-18T11:30:00Z"}
expected = {
"id": "3.2.1",
"status": "CURRENT",
+ "updated": "2011-07-18T11:30:00Z",
"links": [
{
"rel": "self",
- "href": "http://example.org/3.2.1",
+ "href": "http://example.org/3.2.1/",
},
],
}
@@ -113,9 +156,99 @@ class VersionsTest(test.TestCase):
base_url = "http://example.org/app/"
version_number = "v1.4.6"
- expected = "http://example.org/app/v1.4.6"
+ expected = "http://example.org/app/v1.4.6/"
builder = views.versions.ViewBuilder(base_url)
actual = builder.generate_href(version_number)
self.assertEqual(actual, expected)
+
+ def test_xml_serializer(self):
+ versions_data = {
+ 'versions': [
+ {
+ "id": "2.7.1",
+ "updated": "2011-07-18T11:30:00Z",
+ "status": "DEPRECATED",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://test/2.7.1",
+ }
+ ],
+ },
+ ]
+ }
+
+ expected = """
+ <versions>
+ <version id="2.7.1" status="DEPRECATED"
+ updated="2011-07-18T11:30:00Z">
+ <atom:link href="http://test/2.7.1" rel="self"/>
+ </version>
+ </versions>""".replace(" ", "").replace("\n", "")
+
+ serializer = versions.VersionsXMLSerializer()
+ response = serializer.default(versions_data)
+ response = response.replace(" ", "").replace("\n", "")
+ self.assertEqual(expected, response)
+
+ def test_atom_serializer(self):
+ versions_data = {
+ 'versions': [
+ {
+ "id": "2.9.8",
+ "updated": "2011-07-20T11:40:00Z",
+ "status": "CURRENT",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://test/2.9.8",
+ }
+ ],
+ },
+ ]
+ }
+
+ expected = """
+ <feed xmlns="http://www.w3.org/2005/Atom">
+ <title type="text">
+ Available API Versions
+ </title>
+ <updated>
+ 2011-07-20T11:40:00Z
+ </updated>
+ <id>
+ http://test/
+ </id>
+ <author>
+ <name>
+ Rackspace
+ </name>
+ <uri>
+ http://www.rackspace.com/
+ </uri>
+ </author>
+ <link href="http://test/" rel="self"/>
+ <entry>
+ <id>
+ http://test/2.9.8
+ </id>
+ <title type="text">
+ Version 2.9.8
+ </title>
+ <updated>
+ 2011-07-20T11:40:00Z
+ </updated>
+ <link href="http://test/2.9.8" rel="self"/>
+ <content type="text">
+ Version 2.9.8 CURRENT (2011-07-20T11:40:00Z)
+ </content>
+ </entry>
+ </feed>""".replace(" ", "").replace("\n", "")
+
+ serializer = versions.VersionsAtomSerializer()
+ response = serializer.default(versions_data)
+ print response
+ response = response.replace(" ", "").replace("\n", "")
+ self.assertEqual(expected, response)
diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py
index 7762df41c..19028a451 100644
--- a/nova/tests/db/fakes.py
+++ b/nova/tests/db/fakes.py
@@ -230,7 +230,7 @@ def stub_out_db_network_api(stubs):
continue
fixed_ip_fields['virtual_interface'] = FakeModel(vif[0])
- def fake_instance_type_get_by_id(context, id):
+ def fake_instance_type_get(context, id):
if flavor_fields['id'] == id:
return FakeModel(flavor_fields)
@@ -323,7 +323,7 @@ def stub_out_db_network_api(stubs):
fake_fixed_ip_get_by_address,
fake_fixed_ip_get_network,
fake_fixed_ip_update,
- fake_instance_type_get_by_id,
+ fake_instance_type_get,
fake_virtual_interface_create,
fake_virtual_interface_delete_by_instance,
fake_virtual_interface_get_by_instance,
@@ -415,7 +415,7 @@ def stub_out_db_instance_api(stubs, injected=True):
def fake_instance_type_get_by_name(context, name):
return INSTANCE_TYPES[name]
- def fake_instance_type_get_by_id(context, id):
+ def fake_instance_type_get(context, id):
for name, inst_type in INSTANCE_TYPES.iteritems():
if str(inst_type['id']) == str(id):
return inst_type
@@ -448,7 +448,7 @@ def stub_out_db_instance_api(stubs, injected=True):
fake_network_get_all_by_instance,
fake_instance_type_get_all,
fake_instance_type_get_by_name,
- fake_instance_type_get_by_id,
+ fake_instance_type_get,
fake_instance_get_fixed_addresses,
fake_instance_get_fixed_addresses_v6,
fake_network_get_all_by_instance,
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index dc3f0596d..5d59b628a 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -424,11 +424,12 @@ class ComputeTestCase(test.TestCase):
self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake)
context = self.context.elevated()
instance_id = self._create_instance()
- self.compute.prep_resize(context, instance_id, 1)
+ instance_ref = db.instance_get(context, instance_id)
+ self.compute.prep_resize(context, instance_ref['uuid'], 1)
migration_ref = db.migration_get_by_instance_and_status(context,
- instance_id, 'pre-migrating')
+ instance_ref['uuid'], 'pre-migrating')
try:
- self.compute.finish_resize(context, instance_id,
+ self.compute.finish_resize(context, instance_ref['uuid'],
int(migration_ref['id']), {})
except KeyError, e:
# Only catch key errors. We want other reasons for the test to
@@ -441,14 +442,15 @@ class ComputeTestCase(test.TestCase):
"""Ensure notifications on instance migrate/resize"""
instance_id = self._create_instance()
context = self.context.elevated()
+ inst_ref = db.instance_get(context, instance_id)
self.compute.run_instance(self.context, instance_id)
test_notifier.NOTIFICATIONS = []
db.instance_update(self.context, instance_id, {'host': 'foo'})
- self.compute.prep_resize(context, instance_id, 1)
+ self.compute.prep_resize(context, inst_ref['uuid'], 1)
migration_ref = db.migration_get_by_instance_and_status(context,
- instance_id, 'pre-migrating')
+ inst_ref['uuid'], 'pre-migrating')
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
msg = test_notifier.NOTIFICATIONS[0]
@@ -471,13 +473,15 @@ class ComputeTestCase(test.TestCase):
"""Ensure instance can be migrated/resized"""
instance_id = self._create_instance()
context = self.context.elevated()
+ inst_ref = db.instance_get(context, instance_id)
self.compute.run_instance(self.context, instance_id)
- db.instance_update(self.context, instance_id, {'host': 'foo'})
- self.compute.prep_resize(context, instance_id, 1)
+ db.instance_update(self.context, inst_ref['uuid'],
+ {'host': 'foo'})
+ self.compute.prep_resize(context, inst_ref['uuid'], 1)
migration_ref = db.migration_get_by_instance_and_status(context,
- instance_id, 'pre-migrating')
- self.compute.resize_instance(context, instance_id,
+ inst_ref['uuid'], 'pre-migrating')
+ self.compute.resize_instance(context, inst_ref['uuid'],
migration_ref['id'])
self.compute.terminate_instance(context, instance_id)
@@ -535,36 +539,36 @@ class ComputeTestCase(test.TestCase):
# Confirm the instance size before the resize starts
inst_ref = db.instance_get(context, instance_id)
- instance_type_ref = db.instance_type_get_by_id(context,
+ instance_type_ref = db.instance_type_get(context,
inst_ref['instance_type_id'])
self.assertEqual(instance_type_ref['flavorid'], 1)
db.instance_update(self.context, instance_id, {'host': 'foo'})
- self.compute.prep_resize(context, instance_id, 3)
+ self.compute.prep_resize(context, inst_ref['uuid'], 3)
migration_ref = db.migration_get_by_instance_and_status(context,
- instance_id, 'pre-migrating')
+ inst_ref['uuid'], 'pre-migrating')
- self.compute.resize_instance(context, instance_id,
+ self.compute.resize_instance(context, inst_ref['uuid'],
migration_ref['id'])
- self.compute.finish_resize(context, instance_id,
+ self.compute.finish_resize(context, inst_ref['uuid'],
int(migration_ref['id']), {})
# Prove that the instance size is now the new size
inst_ref = db.instance_get(context, instance_id)
- instance_type_ref = db.instance_type_get_by_id(context,
+ instance_type_ref = db.instance_type_get(context,
inst_ref['instance_type_id'])
self.assertEqual(instance_type_ref['flavorid'], 3)
# Finally, revert and confirm the old flavor has been applied
- self.compute.revert_resize(context, instance_id,
+ self.compute.revert_resize(context, inst_ref['uuid'],
migration_ref['id'])
- self.compute.finish_revert_resize(context, instance_id,
+ self.compute.finish_revert_resize(context, inst_ref['uuid'],
migration_ref['id'])
inst_ref = db.instance_get(context, instance_id)
- instance_type_ref = db.instance_type_get_by_id(context,
+ instance_type_ref = db.instance_type_get(context,
inst_ref['instance_type_id'])
self.assertEqual(instance_type_ref['flavorid'], 1)
diff --git a/nova/tests/test_instance_types_extra_specs.py b/nova/tests/test_instance_types_extra_specs.py
index c26cf82ff..393ed1e36 100644
--- a/nova/tests/test_instance_types_extra_specs.py
+++ b/nova/tests/test_instance_types_extra_specs.py
@@ -105,8 +105,8 @@ class InstanceTypeExtraSpecsTestCase(test.TestCase):
self.instance_type_id)
self.assertEquals(expected_specs, actual_specs)
- def test_instance_type_get_by_id_with_extra_specs(self):
- instance_type = db.api.instance_type_get_by_id(
+ def test_instance_type_get_with_extra_specs(self):
+ instance_type = db.api.instance_type_get(
context.get_admin_context(),
self.instance_type_id)
self.assertEquals(instance_type['extra_specs'],
@@ -115,7 +115,7 @@ class InstanceTypeExtraSpecsTestCase(test.TestCase):
xpu_arch="fermi",
xpus="2",
xpu_model="Tesla 2050"))
- instance_type = db.api.instance_type_get_by_id(
+ instance_type = db.api.instance_type_get(
context.get_admin_context(),
5)
self.assertEquals(instance_type['extra_specs'], {})
@@ -136,7 +136,7 @@ class InstanceTypeExtraSpecsTestCase(test.TestCase):
"m1.small")
self.assertEquals(instance_type['extra_specs'], {})
- def test_instance_type_get_by_id_with_extra_specs(self):
+ def test_instance_type_get_with_extra_specs(self):
instance_type = db.api.instance_type_get_by_flavor_id(
context.get_admin_context(),
105)
diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py
index 7cc174842..d30c5e9e5 100644
--- a/nova/tests/test_network.py
+++ b/nova/tests/test_network.py
@@ -127,13 +127,13 @@ class FlatNetworkTestCase(test.TestCase):
def test_get_instance_nw_info(self):
self.mox.StubOutWithMock(db, 'fixed_ip_get_by_instance')
self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance')
- self.mox.StubOutWithMock(db, 'instance_type_get_by_id')
+ self.mox.StubOutWithMock(db, 'instance_type_get')
db.fixed_ip_get_by_instance(mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn(fixed_ips)
db.virtual_interface_get_by_instance(mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn(vifs)
- db.instance_type_get_by_id(mox.IgnoreArg(),
+ db.instance_type_get(mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn(flavor)
self.mox.ReplayAll()
diff --git a/nova/virt/libvirt/netutils.py b/nova/virt/libvirt/netutils.py
index e5aaf7cec..8d36c0a05 100644
--- a/nova/virt/libvirt/netutils.py
+++ b/nova/virt/libvirt/netutils.py
@@ -59,7 +59,7 @@ def get_network_info(instance):
vifs = db.virtual_interface_get_by_instance(admin_context, instance['id'])
networks = db.network_get_all_by_instance(admin_context,
instance['id'])
- flavor = db.instance_type_get_by_id(admin_context,
+ flavor = db.instance_type_get(admin_context,
instance['instance_type_id'])
network_info = []
diff --git a/smoketests/test_sysadmin.py b/smoketests/test_sysadmin.py
index 268d9865b..454f6f1d5 100644
--- a/smoketests/test_sysadmin.py
+++ b/smoketests/test_sysadmin.py
@@ -103,27 +103,48 @@ class ImageTests(base.UserSmokeTestCase):
'launchPermission')
self.assert_(attrs.name, 'launch_permission')
- def test_009_can_modify_image_launch_permission(self):
+ def test_009_can_add_image_launch_permission(self):
+ image = self.conn.get_image(self.data['image_id'])
+ self.assertEqual(image.id, self.data['image_id'])
+ self.assertEqual(image.is_public, False)
self.conn.modify_image_attribute(image_id=self.data['image_id'],
operation='add',
attribute='launchPermission',
groups='all')
image = self.conn.get_image(self.data['image_id'])
self.assertEqual(image.id, self.data['image_id'])
+ self.assertEqual(image.is_public, True)
def test_010_can_see_launch_permission(self):
attrs = self.conn.get_image_attribute(self.data['image_id'],
'launchPermission')
- self.assert_(attrs.name, 'launch_permission')
- self.assert_(attrs.attrs['groups'][0], 'all')
+ self.assertEqual(attrs.name, 'launch_permission')
+ self.assertEqual(attrs.attrs['groups'][0], 'all')
+
+ def test_011_can_remove_image_launch_permission(self):
+ image = self.conn.get_image(self.data['image_id'])
+ self.assertEqual(image.id, self.data['image_id'])
+ self.assertEqual(image.is_public, True)
+ self.conn.modify_image_attribute(image_id=self.data['image_id'],
+ operation='remove',
+ attribute='launchPermission',
+ groups='all')
+ image = self.conn.get_image(self.data['image_id'])
+ self.assertEqual(image.id, self.data['image_id'])
+ self.assertEqual(image.is_public, False)
+
+ def test_012_private_image_shows_in_list(self):
+ images = self.conn.get_all_images()
+ image_ids = [image.id for image in images]
+ self.assertTrue(self.data['image_id'] in image_ids)
- def test_011_user_can_deregister_kernel(self):
+ def test_013_user_can_deregister_kernel(self):
self.assertTrue(self.conn.deregister_image(self.data['kernel_id']))
- def test_012_can_deregister_image(self):
+ def test_014_can_deregister_image(self):
self.assertTrue(self.conn.deregister_image(self.data['image_id']))
- def test_013_can_delete_bundle(self):
+ def test_015_can_delete_bundle(self):
self.assertTrue(self.delete_bundle_bucket(TEST_BUCKET))