GET servers API sorting REST API updates

Updates the v2 and v3 /servers and /servers/detail APIs to support the
multiple sort keys and sort directions (using the 'sort_key' and
'sort_dir' parameters); these parameters can be specified multiple
times to create a list of sort keys and directions. These parameters
are passed from the API layer to the compute layer, then to the
instance layer (with updated version), then to the database layers,
and then to the common paginate_query function; the paginate_query
function already supports multiple sort keys and directions. The
function signatures in these various layers are updated with new
'sort_keys' and 'sort_dirs' parameters that represent the sort keys
and directions information as lists.

This support is enabled on the v2 API by the existence of a new
'os-server-sort-keys' API extension and is always enabled in the v3
API. The extension API sample issues 2 server creates and then ensures
that both servers are listed (name is unique) in the list reply.

DocImpact: The existing v2 and v3 servers API documentation needs to
reflect these new parameters.

The nova client will also be updated to use these parameters.

Change-Id: I02baf6c3cc7d29abab132ef1726140c57e17d9b6
Partially implements: blueprint nova-pagination
This commit is contained in:
Steven Kaufer 2014-06-27 07:10:49 +00:00
parent 59acb16fe3
commit f268be97bd
32 changed files with 454 additions and 157 deletions

View File

@ -711,6 +711,14 @@
"name": "Volumes",
"namespace": "http://docs.openstack.org/compute/ext/volumes/api/v1.1",
"updated": "2011-03-25T00:00:00Z"
},
{
"alias": "os-server-sort-keys",
"description": "Add sorting support in get Server v2 API.",
"links": [],
"name": "ServerSortKeys",
"namespace": "http://docs.openstack.org/compute/ext/server_sort_keys/api/v2",
"updated": "2014-05-22T00:00:00Z"
}
]
}

View File

@ -287,4 +287,7 @@
<extension alias="os-volumes" updated="2011-03-25T00:00:00Z" namespace="http://docs.openstack.org/compute/ext/volumes/api/v1.1" name="Volumes">
<description>Volumes support.</description>
</extension>
<extension alias="os-server-sort-keys" updated="2014-05-22T00:00:00Z" namespace="http://docs.openstack.org/compute/ext/server_sort_keys/api/v2" name="ServerSortKeys">
<description>Add sorting support in get Server v2 API.</description>
</extension>
</extensions>

View File

@ -0,0 +1,7 @@
{
"server" : {
"name" : "new-server-test",
"imageRef" : "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
"flavorRef" : "http://openstack.example.com/openstack/flavors/1"
}
}

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<server xmlns="http://docs.openstack.org/compute/api/v1.1" imageRef="http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b" flavorRef="http://openstack.example.com/openstack/flavors/1" name="new-server-test">
</server>

View File

@ -0,0 +1,16 @@
{
"server": {
"adminPass": "jDje6SdBHGfQ",
"id": "e08e6d34-fcc1-480e-b11e-24a675b479f8",
"links": [
{
"href": "http://openstack.example.com/v2/openstack/servers/e08e6d34-fcc1-480e-b11e-24a675b479f8",
"rel": "self"
},
{
"href": "http://openstack.example.com/openstack/servers/e08e6d34-fcc1-480e-b11e-24a675b479f8",
"rel": "bookmark"
}
]
}
}

View File

@ -0,0 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<server xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://docs.openstack.org/compute/api/v1.1" id="e08e6d34-fcc1-480e-b11e-24a675b479f8" adminPass="jDje6SdBHGfQ">
<metadata/>
<atom:link href="http://openstack.example.com/v2/openstack/servers/e08e6d34-fcc1-480e-b11e-24a675b479f8" rel="self"/>
<atom:link href="http://openstack.example.com/openstack/servers/e08e6d34-fcc1-480e-b11e-24a675b479f8" rel="bookmark"/>
</server>

View File

@ -0,0 +1,18 @@
{
"servers": [
{
"id": "e08e6d34-fcc1-480e-b11e-24a675b479f8",
"links": [
{
"href": "http://openstack.example.com/v2/openstack/servers/e08e6d34-fcc1-480e-b11e-24a675b479f8",
"rel": "self"
},
{
"href": "http://openstack.example.com/openstack/servers/e08e6d34-fcc1-480e-b11e-24a675b479f8",
"rel": "bookmark"
}
],
"name": "new-server-test"
}
]
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<servers xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://docs.openstack.org/compute/api/v1.1">
<server name="new-server-test" id="e08e6d34-fcc1-480e-b11e-24a675b479f8">
<atom:link href="http://openstack.example.com/v2/openstack/servers/e08e6d34-fcc1-480e-b11e-24a675b479f8" rel="self"/>
<atom:link href="http://openstack.example.com/openstack/servers/e08e6d34-fcc1-480e-b11e-24a675b479f8" rel="bookmark"/>
</server>
</servers>

View File

@ -0,0 +1,7 @@
{
"server" : {
"name" : "new-server-test",
"imageRef" : "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
"flavorRef" : "http://openstack.example.com/openstack/flavors/1"
}
}

View File

@ -0,0 +1,16 @@
{
"server": {
"adminPass": "jDje6SdBHGfQ",
"id": "e08e6d34-fcc1-480e-b11e-24a675b479f8",
"links": [
{
"href": "http://openstack.example.com/v3/servers/e08e6d34-fcc1-480e-b11e-24a675b479f8",
"rel": "self"
},
{
"href": "http://openstack.example.com/servers/e08e6d34-fcc1-480e-b11e-24a675b479f8",
"rel": "bookmark"
}
]
}
}

View File

@ -0,0 +1,18 @@
{
"servers": [
{
"id": "e08e6d34-fcc1-480e-b11e-24a675b479f8",
"links": [
{
"href": "http://openstack.example.com/v3/servers/e08e6d34-fcc1-480e-b11e-24a675b479f8",
"rel": "self"
},
{
"href": "http://openstack.example.com/servers/e08e6d34-fcc1-480e-b11e-24a675b479f8",
"rel": "bookmark"
}
],
"name": "new-server-test"
}
]
}

View File

@ -0,0 +1,25 @@
# Copyright 2014 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from nova.api.openstack import extensions
class Server_sort_keys(extensions.ExtensionDescriptor):
"""Add sort keys and directions to the Server GET v2 API."""
name = "ServerSortKeys"
alias = "os-server-sort-keys"
namespace = ("http://docs.openstack.org/compute/ext/"
"server_sort_keys/api/v2")
updated = "2014-05-22T00:00:00Z"

View File

@ -350,10 +350,12 @@ class ServersController(wsgi.Controller):
search_opts['user_id'] = context.user_id
limit, marker = common.get_limit_and_marker(req)
sort_keys, sort_dirs = common.get_sort_params(req.params)
try:
instance_list = self.compute_api.get_all(context,
search_opts=search_opts, limit=limit, marker=marker,
want_objects=True, expected_attrs=['pci_devices'])
want_objects=True, expected_attrs=['pci_devices'],
sort_keys=sort_keys, sort_dirs=sort_dirs)
except exception.MarkerNotFound:
msg = _('marker [%s] not found') % marker
raise exc.HTTPBadRequest(explanation=msg)

View File

@ -596,12 +596,18 @@ class Controller(wsgi.Controller):
search_opts['user_id'] = context.user_id
limit, marker = common.get_limit_and_marker(req)
# Sorting by multiple keys and directions is conditionally enabled
sort_keys, sort_dirs = None, None
if self.ext_mgr.is_loaded('os-server-sort-keys'):
sort_keys, sort_dirs = common.get_sort_params(req.params)
try:
instance_list = self.compute_api.get_all(context,
search_opts=search_opts,
limit=limit,
marker=marker,
want_objects=True)
want_objects=True,
sort_keys=sort_keys,
sort_dirs=sort_dirs)
except exception.MarkerNotFound:
msg = _('marker [%s] not found') % marker
raise exc.HTTPBadRequest(explanation=msg)

View File

@ -711,6 +711,14 @@
"name": "ServerGroupQuotas",
"namespace": "http://docs.openstack.org/compute/ext/server-group-quotas/api/v2",
"updated": "%(isotime)s"
},
{
"alias": "os-server-sort-keys",
"description": "%(text)s",
"links": [],
"name": "ServerSortKeys",
"namespace": "http://docs.openstack.org/compute/ext/server_sort_keys/api/v2",
"updated": "%(isotime)s"
}
]
}

View File

@ -266,4 +266,7 @@
<extension alias="os-server-group-quotas" updated="%(isotime)s" namespace="http://docs.openstack.org/compute/ext/server-group-quotas/api/v2" name="ServerGroupQuotas">
<description>%(text)s</description>
</extension>
<extension alias="os-server-sort-keys" updated="%(isotime)s" namespace="http://docs.openstack.org/compute/ext/server_sort_keys/api/v2" name="ServerSortKeys">
<description>%(text)s</description>
</extension>
</extensions>

View File

@ -0,0 +1,7 @@
{
"server" : {
"name" : "new-server-test",
"imageRef" : "%(host)s/openstack/images/%(image_id)s",
"flavorRef" : "%(host)s/openstack/flavors/1"
}
}

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<server xmlns="http://docs.openstack.org/compute/api/v1.1" imageRef="%(host)s/openstack/images/%(image_id)s" flavorRef="%(host)s/openstack/flavors/1" name="new-server-test">
</server>

View File

@ -0,0 +1,16 @@
{
"server": {
"adminPass": "%(password)s",
"id": "%(id)s",
"links": [
{
"href": "%(host)s/v2/openstack/servers/%(uuid)s",
"rel": "self"
},
{
"href": "%(host)s/openstack/servers/%(uuid)s",
"rel": "bookmark"
}
]
}
}

View File

@ -0,0 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<server xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://docs.openstack.org/compute/api/v1.1" id="%(id)s" adminPass="%(password)s">
<metadata/>
<atom:link href="%(host)s/v2/openstack/servers/%(uuid)s" rel="self"/>
<atom:link href="%(host)s/openstack/servers/%(uuid)s" rel="bookmark"/>
</server>

View File

@ -0,0 +1,18 @@
{
"servers": [
{
"id": "%(id)s",
"links": [
{
"href": "%(host)s/v2/openstack/servers/%(id)s",
"rel": "self"
},
{
"href": "%(host)s/openstack/servers/%(id)s",
"rel": "bookmark"
}
],
"name": "new-server-test"
}
]
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<servers xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://docs.openstack.org/compute/api/v1.1">
<server name="new-server-test" id="%(id)s">
<atom:link href="%(host)s/v2/openstack/servers/%(id)s" rel="self"/>
<atom:link href="%(host)s/openstack/servers/%(id)s" rel="bookmark"/>
</server>
</servers>

View File

@ -4433,3 +4433,19 @@ class ServerGroupQuotas_QuotaClassesSampleXmlTests(
"server_group_quotas.Server_group_quotas")
extends_name = ("nova.api.openstack.compute.contrib.quota_classes."
"Quota_classes")
class ServerSortKeysJsonTests(ServersSampleBase):
extension_name = ("nova.api.openstack.compute.contrib.server_sort_keys"
".Server_sort_keys")
def test_servers_list(self):
self._post_server()
response = self._do_get('servers?sort_key=display_name&sort_dir=asc')
subs = self._get_regexes()
self._verify_response('server-sort-keys-list-resp', subs, response,
200)
class ServerSortKeysXmlTests(ServerSortKeysJsonTests):
ctype = 'xml'

View File

@ -0,0 +1,7 @@
{
"server" : {
"name" : "new-server-test",
"imageRef" : "%(host)s/openstack/images/%(image_id)s",
"flavorRef" : "%(host)s/openstack/flavors/1"
}
}

View File

@ -0,0 +1,16 @@
{
"server": {
"adminPass": "%(password)s",
"id": "%(id)s",
"links": [
{
"href": "%(host)s/v3/servers/%(uuid)s",
"rel": "self"
},
{
"href": "%(host)s/servers/%(uuid)s",
"rel": "bookmark"
}
]
}
}

View File

@ -0,0 +1,18 @@
{
"servers": [
{
"id": "%(id)s",
"links": [
{
"href": "%(host)s/v3/servers/%(id)s",
"rel": "self"
},
{
"href": "%(host)s/servers/%(id)s",
"rel": "bookmark"
}
],
"name": "new-server-test"
}
]
}

View File

@ -64,6 +64,17 @@ class ServersSampleJsonTest(ServersSampleBase):
self._verify_response('servers-details-resp', subs, response, 200)
class ServerSortKeysJsonTests(ServersSampleBase):
sample_dir = 'servers-sort'
def test_servers_list(self):
self._post_server()
response = self._do_get('servers?sort_key=display_name&sort_dir=asc')
subs = self._get_regexes()
self._verify_response('server-sort-keys-list-resp', subs, response,
200)
class ServersSampleAllExtensionJsonTest(ServersSampleJsonTest):
all_extensions = True

View File

@ -69,8 +69,13 @@ class ConfigDriveTestV21(test.TestCase):
self.assertIn('config_drive', res_dict['server'])
def test_detail_servers(self):
# Sort is disabled in v2 without an extension so stub out
# the non-sorted DB get
self.stubs.Set(db, 'instance_get_all_by_filters',
fakes.fake_instance_get_all_by_filters())
# But it is enabled in v3 so stub out the sorted function
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fakes.fake_instance_get_all_by_filters())
req = fakes.HTTPRequest.blank(self.base_url + 'detail')
res = req.get_response(self.app)
server_dicts = jsonutils.loads(res.body)['servers']

View File

@ -312,8 +312,13 @@ class KeypairsTestV21(test.TestCase):
self.assertEqual(res_dict['server']['key_name'], '')
def test_detail_servers(self):
# Sort is disabled in v2 without an extension so stub out
# the non-sorted DB get
self.stubs.Set(db, 'instance_get_all_by_filters',
fakes.fake_instance_get_all_by_filters())
fakes.fake_instance_get_all_by_filters())
# But it is enabled in v3 so stub out the sorted function
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fakes.fake_instance_get_all_by_filters())
req = fakes.HTTPRequest.blank(self.base_url + '/servers/detail')
res = req.get_response(self.app_server)
server_dicts = jsonutils.loads(res.body)['servers']

View File

@ -180,7 +180,9 @@ class ControllerTest(test.TestCase):
fake.stub_out_image_service(self.stubs)
return_server = fakes.fake_instance_get()
return_servers = fakes.fake_instance_get_all_by_filters()
self.stubs.Set(db, 'instance_get_all_by_filters',
# Server sort keys extension is enabled in v3 so sort data is passed
# to the instance API and the sorted DB API is invoked
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
return_servers)
self.stubs.Set(db, 'instance_get_by_uuid',
return_server)
@ -500,7 +502,7 @@ class ServersControllerTest(ControllerTest):
self.ips_controller.index, req, server_id)
def test_get_server_list_empty(self):
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
return_servers_empty)
req = fakes.HTTPRequestV3.blank('/servers')
@ -584,7 +586,7 @@ class ServersControllerTest(ControllerTest):
self.controller.index, req)
def test_get_server_details_empty(self):
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
return_servers_empty)
req = fakes.HTTPRequestV3.blank('/servers/detail')
@ -617,7 +619,8 @@ class ServersControllerTest(ControllerTest):
def test_get_server_details_with_limit_and_other_params(self):
req = fakes.HTTPRequestV3.blank('/servers/detail'
'?limit=3&blah=2:t')
'?limit=3&blah=2:t'
'&sort_key=id1&sort_dir=asc')
res = self.controller.detail(req)
servers = res['servers']
@ -631,6 +634,7 @@ class ServersControllerTest(ControllerTest):
self.assertEqual('/v3/servers/detail', href_parts.path)
params = urlparse.parse_qs(href_parts.query)
expected = {'limit': ['3'], 'blah': ['2:t'],
'sort_key': ['id1'], 'sort_dir': ['asc'],
'marker': [fakes.get_fake_uuid(2)]}
self.assertThat(params, matchers.DictMatches(expected))
@ -665,9 +669,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
db_list = [fakes.stub_instance(100, uuid=server_uuid)]
return instance_obj._make_instance_list(
context, objects.InstanceList(), db_list, FIELDS)
@ -684,9 +687,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('image', search_opts)
self.assertEqual(search_opts['image'], '12345')
@ -703,46 +705,42 @@ class ServersControllerTest(ControllerTest):
self.assertEqual(servers[0]['id'], server_uuid)
def test_tenant_id_filter_converts_to_project_id_for_admin(self):
def fake_get_all(context, filters=None, sort_key=None,
sort_dir='desc', limit=None, marker=None,
def fake_get_all(context, filters=None, limit=None, marker=None,
columns_to_join=None, use_slave=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(filters)
self.assertEqual(filters['project_id'], 'newfake')
self.assertFalse(filters.get('tenant_id'))
return [fakes.stub_instance(100)]
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fake_get_all)
req = fakes.HTTPRequestV3.blank('/servers'
'?all_tenants=1&tenant_id=newfake',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_tenant_id_filter_no_admin_context(self):
def fake_get_all(context, filters=None, sort_key=None,
sort_dir='desc', limit=None, marker=None,
def fake_get_all(context, filters=None, limit=None, marker=None,
columns_to_join=None, use_slave=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertNotEqual(filters, None)
self.assertEqual(filters['project_id'], 'fake')
return [fakes.stub_instance(100)]
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fake_get_all)
req = fakes.HTTPRequestV3.blank('/servers?tenant_id=newfake')
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_tenant_id_filter_implies_all_tenants(self):
def fake_get_all(context, filters=None, sort_key=None,
sort_dir='desc', limit=None, marker=None,
def fake_get_all(context, filters=None, limit=None, marker=None,
columns_to_join=None, use_slave=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertNotEqual(filters, None)
# The project_id assertion checks that the project_id
# filter is set to that specified in the request url and
@ -752,91 +750,82 @@ class ServersControllerTest(ControllerTest):
self.assertFalse(filters.get('tenant_id'))
return [fakes.stub_instance(100)]
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fake_get_all)
req = fakes.HTTPRequestV3.blank('/servers?tenant_id=newfake',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_param_normal(self):
def fake_get_all(context, filters=None, sort_key=None,
sort_dir='desc', limit=None, marker=None,
def fake_get_all(context, filters=None, limit=None, marker=None,
columns_to_join=None, use_slave=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertNotIn('project_id', filters)
return [fakes.stub_instance(100)]
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fake_get_all)
req = fakes.HTTPRequestV3.blank('/servers?all_tenants',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_param_one(self):
def fake_get_all(context, filters=None, sort_key=None,
sort_dir='desc', limit=None, marker=None,
def fake_get_all(context, filters=None, limit=None, marker=None,
columns_to_join=None, use_slave=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertNotIn('project_id', filters)
return [fakes.stub_instance(100)]
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fake_get_all)
req = fakes.HTTPRequestV3.blank('/servers?all_tenants=1',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_param_zero(self):
def fake_get_all(context, filters=None, sort_key=None,
sort_dir='desc', limit=None, marker=None,
def fake_get_all(context, filters=None, limit=None, marker=None,
columns_to_join=None, use_slave=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertNotIn('all_tenants', filters)
return [fakes.stub_instance(100)]
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fake_get_all)
req = fakes.HTTPRequestV3.blank('/servers?all_tenants=0',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_param_false(self):
def fake_get_all(context, filters=None, sort_key=None,
sort_dir='desc', limit=None, marker=None,
def fake_get_all(context, filters=None, limit=None, marker=None,
columns_to_join=None, use_slave=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertNotIn('all_tenants', filters)
return [fakes.stub_instance(100)]
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fake_get_all)
req = fakes.HTTPRequestV3.blank('/servers?all_tenants=false',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_param_invalid(self):
def fake_get_all(context, filters=None, sort_key=None,
sort_dir='desc', limit=None, marker=None,
def fake_get_all(context, filters=None, limit=None, marker=None,
columns_to_join=None,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertNotIn('all_tenants', filters)
return [fakes.stub_instance(100)]
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fake_get_all)
req = fakes.HTTPRequestV3.blank('/servers?all_tenants=xxx',
@ -845,33 +834,30 @@ class ServersControllerTest(ControllerTest):
self.controller.index, req)
def test_admin_restricted_tenant(self):
def fake_get_all(context, filters=None, sort_key=None,
sort_dir='desc', limit=None, marker=None,
def fake_get_all(context, filters=None, limit=None, marker=None,
columns_to_join=None, use_slave=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(filters)
self.assertEqual(filters['project_id'], 'fake')
return [fakes.stub_instance(100)]
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fake_get_all)
req = fakes.HTTPRequestV3.blank('/servers',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_pass_policy(self):
def fake_get_all(context, filters=None, sort_key=None,
sort_dir='desc', limit=None, marker=None,
def fake_get_all(context, filters=None, limit=None, marker=None,
columns_to_join=None, use_slave=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(filters)
self.assertNotIn('project_id', filters)
return [fakes.stub_instance(100)]
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fake_get_all)
rules = {
@ -884,14 +870,12 @@ class ServersControllerTest(ControllerTest):
policy.set_rules(rules)
req = fakes.HTTPRequestV3.blank('/servers?all_tenants=1')
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_fail_policy(self):
def fake_get_all(context, filters=None, sort_key=None,
sort_dir='desc', limit=None, marker=None,
columns_to_join=None):
def fake_get_all(context, filters=None, limit=None, marker=None,
columns_to_join=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(filters)
return [fakes.stub_instance(100)]
@ -903,7 +887,7 @@ class ServersControllerTest(ControllerTest):
}
policy.set_rules(rules)
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
fake_get_all)
req = fakes.HTTPRequestV3.blank('/servers?all_tenants=1')
@ -914,9 +898,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('flavor', search_opts)
# flavor is an integer ID
@ -949,9 +932,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('vm_state', search_opts)
self.assertEqual(search_opts['vm_state'], [vm_states.ACTIVE])
@ -972,9 +954,8 @@ class ServersControllerTest(ControllerTest):
task_state = task_states.REBOOTING
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('task_state', search_opts)
self.assertEqual([task_states.REBOOT_PENDING,
@ -999,9 +980,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIn('vm_state', search_opts)
self.assertEqual(search_opts['vm_state'],
[vm_states.ACTIVE, vm_states.STOPPED])
@ -1035,9 +1015,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIn('vm_state', search_opts)
self.assertEqual(search_opts['vm_state'], ['deleted'])
@ -1058,9 +1037,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('name', search_opts)
self.assertEqual(search_opts['name'], 'whee.*')
@ -1089,9 +1067,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('changes-since', search_opts)
changes_since = datetime.datetime(2011, 1, 24, 17, 8, 1,
@ -1124,9 +1101,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
# Allowed by user
self.assertIn('name', search_opts)
@ -1156,9 +1132,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
# Allowed by user
self.assertIn('name', search_opts)
@ -1187,9 +1162,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('ip', search_opts)
self.assertEqual(search_opts['ip'], '10\..*')
@ -1212,9 +1186,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('ip6', search_opts)
self.assertEqual(search_opts['ip6'], 'ffff.*')
@ -1274,7 +1247,7 @@ class ServersControllerTest(ControllerTest):
uuid=fakes.get_fake_uuid(i))
for i in xrange(5)]
self.stubs.Set(db, 'instance_get_all_by_filters',
self.stubs.Set(db, 'instance_get_all_by_filters_sort',
return_servers_with_host)
req = fakes.HTTPRequestV3.blank('/servers/detail')
@ -1294,9 +1267,8 @@ class ServersControllerTest(ControllerTest):
self.expected_attrs = None
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False,
expected_attrs=None):
expected_attrs=None, sort_keys=None, sort_dirs=None):
self.expected_attrs = expected_attrs
return []

View File

@ -176,6 +176,9 @@ class ControllerTest(test.TestCase):
fake.stub_out_image_service(self.stubs)
return_server = fakes.fake_instance_get()
return_servers = fakes.fake_instance_get_all_by_filters()
# Server sort keys extension is not enabled in v2 test so no sort
# data is passed to the instance API and the non-sorted DB API is
# invoked
self.stubs.Set(db, 'instance_get_all_by_filters',
return_servers)
self.stubs.Set(db, 'instance_get_by_uuid',
@ -572,7 +575,8 @@ class ServersControllerTest(ControllerTest):
def test_get_server_details_with_limit_and_other_params(self):
req = fakes.HTTPRequest.blank('/fake/servers/detail'
'?limit=3&blah=2:t')
'?limit=3&blah=2:t'
'&sort_key=id1&sort_dir=asc')
res = self.controller.detail(req)
servers = res['servers']
@ -581,11 +585,16 @@ class ServersControllerTest(ControllerTest):
servers_links = res['servers_links']
self.assertEqual(servers_links[0]['rel'], 'next')
# Retrieve the parameters from the next link, they should contain the
# same limit, filter, and sort information as the original request as
# well as a marker; this ensures that the caller can simply use the
# "next" link and that they do not need to manually insert the limit
# and sort information.
href_parts = urlparse.urlparse(servers_links[0]['href'])
self.assertEqual('/v2/fake/servers/detail', href_parts.path)
params = urlparse.parse_qs(href_parts.query)
expected = {'limit': ['3'], 'blah': ['2:t'],
'sort_key': ['id1'], 'sort_dir': ['asc'],
'marker': [fakes.get_fake_uuid(2)]}
self.assertThat(params, matchers.DictMatches(expected))
@ -616,12 +625,38 @@ class ServersControllerTest(ControllerTest):
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.index, req)
@mock.patch('nova.compute.api.API.get_all')
def test_get_servers_with_sorting_enabled(self, mock_compute_get_all):
'''Sorting params honored if os-server-sort-keys is loaded.'''
self.ext_mgr.extensions = {'os-server-sort-keys': 'fake'}
req = fakes.HTTPRequest.blank('/fake/servers'
'?sort_key=id1&sort_dir=asc')
self.controller.index(req)
self.assertEqual(mock_compute_get_all.call_count, 1)
# Ensure that sort_dirs and sort_dirs is correct
kwargs = mock_compute_get_all.call_args[1]
self.assertEqual(['id1'], kwargs['sort_keys'])
self.assertEqual(['asc'], kwargs['sort_dirs'])
@mock.patch('nova.compute.api.API.get_all')
def test_get_servers_with_sorting_disabled(self, mock_compute_get_all):
'''Sorting params ignored if os-server-sort-keys is not loaded.'''
self.ext_mgr.extensions = {}
req = fakes.HTTPRequest.blank('/fake/servers'
'?sort_key=id1&sort_dir=asc')
self.controller.index(req)
self.assertEqual(mock_compute_get_all.call_count, 1)
# Ensure that sort_dirs and sort_dirs is None
kwargs = mock_compute_get_all.call_args[1]
self.assertIsNone(kwargs['sort_keys'])
self.assertIsNone(kwargs['sort_dirs'])
def test_get_servers_with_bad_option(self):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
db_list = [fakes.stub_instance(100, uuid=server_uuid)]
return instance_obj._make_instance_list(
context, objects.InstanceList(), db_list, FIELDS)
@ -638,8 +673,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('image', search_opts)
self.assertEqual(search_opts['image'], '12345')
@ -670,9 +705,8 @@ class ServersControllerTest(ControllerTest):
req = fakes.HTTPRequest.blank('/fake/servers'
'?all_tenants=1&tenant_id=newfake',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_param_normal(self):
def fake_get_all(context, filters=None, sort_key=None,
@ -686,9 +720,8 @@ class ServersControllerTest(ControllerTest):
req = fakes.HTTPRequest.blank('/fake/servers?all_tenants',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_param_one(self):
def fake_get_all(context, filters=None, sort_key=None,
@ -702,9 +735,8 @@ class ServersControllerTest(ControllerTest):
req = fakes.HTTPRequest.blank('/fake/servers?all_tenants=1',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_param_zero(self):
def fake_get_all(context, filters=None, sort_key=None,
@ -718,9 +750,8 @@ class ServersControllerTest(ControllerTest):
req = fakes.HTTPRequest.blank('/fake/servers?all_tenants=0',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_param_false(self):
def fake_get_all(context, filters=None, sort_key=None,
@ -734,9 +765,8 @@ class ServersControllerTest(ControllerTest):
req = fakes.HTTPRequest.blank('/fake/servers?all_tenants=false',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_param_invalid(self):
def fake_get_all(context, filters=None, sort_key=None,
@ -766,9 +796,8 @@ class ServersControllerTest(ControllerTest):
req = fakes.HTTPRequest.blank('/fake/servers',
use_admin_context=True)
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_pass_policy(self):
def fake_get_all(context, filters=None, sort_key=None,
@ -791,9 +820,8 @@ class ServersControllerTest(ControllerTest):
policy.set_rules(rules)
req = fakes.HTTPRequest.blank('/fake/servers?all_tenants=1')
res = self.controller.index(req)
self.assertIn('servers', res)
servers = self.controller.index(req)['servers']
self.assertEqual(len(servers), 1)
def test_all_tenants_fail_policy(self):
def fake_get_all(context, filters=None, sort_key=None,
@ -821,8 +849,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('flavor', search_opts)
# flavor is an integer ID
@ -855,8 +883,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('vm_state', search_opts)
self.assertEqual(search_opts['vm_state'], [vm_states.ACTIVE])
@ -893,7 +921,8 @@ class ServersControllerTest(ControllerTest):
project_id='fake')
get_all_mock.assert_called_once_with(mock.ANY,
search_opts=expected_search_opts, limit=mock.ANY,
marker=mock.ANY, want_objects=mock.ANY)
marker=mock.ANY, want_objects=mock.ANY,
sort_keys=mock.ANY, sort_dirs=mock.ANY)
@mock.patch.object(compute_api.API, 'get_all')
def test_get_servers_system_metadata_filter(self, get_all_mock):
@ -918,7 +947,8 @@ class ServersControllerTest(ControllerTest):
system_metadata=expected_system_metadata, project_id='fake')
get_all_mock.assert_called_once_with(mock.ANY,
search_opts=expected_search_opts, limit=mock.ANY,
marker=mock.ANY, want_objects=mock.ANY)
marker=mock.ANY, want_objects=mock.ANY,
sort_keys=mock.ANY, sort_dirs=mock.ANY)
@mock.patch.object(compute_api.API, 'get_all')
def test_get_servers_flavor_not_found(self, get_all_mock):
@ -949,15 +979,16 @@ class ServersControllerTest(ControllerTest):
project_id='fake')
get_all_mock.assert_called_once_with(mock.ANY,
search_opts=expected_search_opts, limit=mock.ANY,
marker=mock.ANY, want_objects=mock.ANY)
marker=mock.ANY, want_objects=mock.ANY,
sort_keys=mock.ANY, sort_dirs=mock.ANY)
def test_get_servers_allows_task_status(self):
server_uuid = str(uuid.uuid4())
task_state = task_states.REBOOTING
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('task_state', search_opts)
self.assertEqual([task_states.REBOOT_PENDING,
@ -982,8 +1013,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
self.assertIn('vm_state', search_opts)
self.assertEqual(search_opts['vm_state'],
[vm_states.ACTIVE, vm_states.STOPPED])
@ -1017,8 +1048,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
self.assertIn('vm_state', search_opts)
self.assertEqual(search_opts['vm_state'], ['deleted'])
@ -1039,8 +1070,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('name', search_opts)
self.assertEqual(search_opts['name'], 'whee.*')
@ -1060,8 +1091,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('changes-since', search_opts)
changes_since = datetime.datetime(2011, 1, 24, 17, 8, 1,
@ -1094,8 +1125,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
# Allowed by user
self.assertIn('name', search_opts)
@ -1125,8 +1156,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
# Allowed by user
self.assertIn('name', search_opts)
@ -1154,8 +1185,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('ip', search_opts)
self.assertEqual(search_opts['ip'], '10\..*')
@ -1178,8 +1209,8 @@ class ServersControllerTest(ControllerTest):
server_uuid = str(uuid.uuid4())
def fake_get_all(compute_self, context, search_opts=None,
sort_key=None, sort_dir='desc',
limit=None, marker=None, want_objects=False):
limit=None, marker=None, want_objects=False,
sort_keys=None, sort_dirs=None):
self.assertIsNotNone(search_opts)
self.assertIn('ip6', search_opts)
self.assertEqual(search_opts['ip6'], 'ffff.*')

View File

@ -399,6 +399,12 @@ def fake_instance_get_all_by_filters(num_servers=5, **kwargs):
if 'use_slave' in kwargs:
kwargs.pop('use_slave')
if 'sort_keys' in kwargs:
kwargs.pop('sort_keys')
if 'sort_dirs' in kwargs:
kwargs.pop('sort_dirs')
for i in xrange(num_servers):
uuid = get_fake_uuid(i)
server = stub_instance(id=i + 1, uuid=uuid,