Microversion 2.33 adds pagination support for hypervisors
When there are thousands of compute nodes, it would be slow to get the whole hypervisor list, and it is bad for user experience to display thousands of items in a table in horizon. This patch is proposed to support pagination for hypervisor by adding `limit` and `marker` to the list api. Implements blueprint: pagination-for-hypervisor Change-Id: Ie7f8b5c733b383f3e69fa23188e56257e503b5f7
This commit is contained in:
parent
06fcb47995
commit
ec53c6c0ec
|
@ -23,6 +23,14 @@ Normal response codes: 200
|
|||
|
||||
Error response codes: unauthorized(401), forbidden(403)
|
||||
|
||||
Request
|
||||
-------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- limit: hypervisor_limit
|
||||
- marker: hypervisor_marker
|
||||
|
||||
Response
|
||||
--------
|
||||
|
||||
|
@ -52,6 +60,14 @@ Normal response codes: 200
|
|||
|
||||
Error response codes: unauthorized(401), forbidden(403)
|
||||
|
||||
Request
|
||||
-------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- limit: hypervisor_limit
|
||||
- marker: hypervisor_marker
|
||||
|
||||
Response
|
||||
--------
|
||||
|
||||
|
|
|
@ -402,6 +402,25 @@ host_query:
|
|||
in: query
|
||||
required: false
|
||||
type: string
|
||||
hypervisor_limit:
|
||||
description: |
|
||||
Requests a page size of items. Returns a number of items up to a limit value.
|
||||
Use the ``limit`` parameter to make an initial limited request and use the ID
|
||||
of the last-seen item from the response as the ``marker`` parameter value in a
|
||||
subsequent limited request.
|
||||
in: query
|
||||
required: false
|
||||
type: integer
|
||||
min_version: 2.33
|
||||
hypervisor_marker:
|
||||
description: |
|
||||
The ID of the last-seen item. Use the ``limit`` parameter to make an initial limited
|
||||
request and use the ID of the last-seen item from the response as the ``marker``
|
||||
parameter value in a subsequent limited request.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
min_version: 2.33
|
||||
hypervisor_query:
|
||||
description: |
|
||||
Filters the response by a hypervisor type.
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"cpu_info": {
|
||||
"arch": "x86_64",
|
||||
"model": "Nehalem",
|
||||
"vendor": "Intel",
|
||||
"features": [
|
||||
"pge",
|
||||
"clflush"
|
||||
],
|
||||
"topology": {
|
||||
"cores": 1,
|
||||
"threads": 1,
|
||||
"sockets": 4
|
||||
}
|
||||
},
|
||||
"current_workload": 0,
|
||||
"status": "enabled",
|
||||
"state": "up",
|
||||
"disk_available_least": 0,
|
||||
"host_ip": "1.1.1.1",
|
||||
"free_disk_gb": 1028,
|
||||
"free_ram_mb": 7680,
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"hypervisor_type": "fake",
|
||||
"hypervisor_version": 1000,
|
||||
"id": 2,
|
||||
"local_gb": 1028,
|
||||
"local_gb_used": 0,
|
||||
"memory_mb": 8192,
|
||||
"memory_mb_used": 512,
|
||||
"running_vms": 0,
|
||||
"service": {
|
||||
"host": "host1",
|
||||
"id": 7,
|
||||
"disabled_reason": null
|
||||
},
|
||||
"vcpus": 1,
|
||||
"vcpus_used": 0
|
||||
}
|
||||
],
|
||||
"hypervisors_links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/hypervisors/detail?limit=1&marker=2",
|
||||
"rel": "next"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"id": 2,
|
||||
"state": "up",
|
||||
"status": "enabled"
|
||||
}
|
||||
],
|
||||
"hypervisors_links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/hypervisors?limit=1&marker=2",
|
||||
"rel": "next"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -19,7 +19,7 @@
|
|||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.32",
|
||||
"version": "2.33",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.32",
|
||||
"version": "2.33",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
|||
* 2.31 - Fix os-console-auth-tokens to work for all console types.
|
||||
* 2.32 - Add tag to networks and block_device_mapping_v2 in server boot
|
||||
request body.
|
||||
* 2.33 - Add pagination support for hypervisors.
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
|
@ -90,7 +91,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
|||
# Note(cyeoh): This only applies for the v2.1 API once microversions
|
||||
# support is fully merged. It does not affect the V2 API.
|
||||
_MIN_API_VERSION = "2.1"
|
||||
_MAX_API_VERSION = "2.32"
|
||||
_MAX_API_VERSION = "2.33"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import webob.exc
|
|||
|
||||
from nova.api.openstack import api_version_request
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack.compute.views import hypervisors as hyper_view
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova import compute
|
||||
|
@ -35,6 +36,8 @@ ALIAS = "os-hypervisors"
|
|||
class HypervisorsController(wsgi.Controller):
|
||||
"""The Hypervisors API controller for the OpenStack API."""
|
||||
|
||||
_view_builder_class = hyper_view.ViewBuilder
|
||||
|
||||
def __init__(self):
|
||||
self.host_api = compute.HostAPI()
|
||||
self.servicegroup_api = servicegroup.API()
|
||||
|
@ -80,28 +83,76 @@ class HypervisorsController(wsgi.Controller):
|
|||
|
||||
return hyp_dict
|
||||
|
||||
@wsgi.Controller.api_version("2.33") # noqa
|
||||
@extensions.expected_errors((400))
|
||||
def index(self, req):
|
||||
limit, marker = common.get_limit_and_marker(req)
|
||||
return self._index(req, limit=limit, marker=marker, links=True)
|
||||
|
||||
@wsgi.Controller.api_version("2.1", "2.32") # noqa
|
||||
@extensions.expected_errors(())
|
||||
def index(self, req):
|
||||
return self._index(req)
|
||||
|
||||
def _index(self, req, limit=None, marker=None, links=False):
|
||||
context = req.environ['nova.context']
|
||||
context.can(hv_policies.BASE_POLICY_NAME)
|
||||
compute_nodes = self.host_api.compute_node_get_all(context)
|
||||
req.cache_db_compute_nodes(compute_nodes)
|
||||
return dict(hypervisors=[self._view_hypervisor(
|
||||
hyp,
|
||||
self.host_api.service_get_by_compute_host(
|
||||
context, hyp.host),
|
||||
False, req)
|
||||
for hyp in compute_nodes])
|
||||
|
||||
try:
|
||||
compute_nodes = self.host_api.compute_node_get_all(
|
||||
context, limit=limit, marker=marker)
|
||||
except exception.MarkerNotFound:
|
||||
msg = _('marker [%s] not found') % marker
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
req.cache_db_compute_nodes(compute_nodes)
|
||||
hypervisors_list = [self._view_hypervisor(
|
||||
hyp,
|
||||
self.host_api.service_get_by_compute_host(
|
||||
context, hyp.host),
|
||||
False, req)
|
||||
for hyp in compute_nodes]
|
||||
|
||||
hypervisors_dict = dict(hypervisors=hypervisors_list)
|
||||
if links:
|
||||
hypervisors_links = self._view_builder.get_links(req,
|
||||
hypervisors_list)
|
||||
if hypervisors_links:
|
||||
hypervisors_dict['hypervisors_links'] = hypervisors_links
|
||||
return hypervisors_dict
|
||||
|
||||
@wsgi.Controller.api_version("2.33") # noqa
|
||||
@extensions.expected_errors((400))
|
||||
def detail(self, req):
|
||||
limit, marker = common.get_limit_and_marker(req)
|
||||
return self._detail(req, limit=limit, marker=marker, links=True)
|
||||
|
||||
@wsgi.Controller.api_version("2.1", "2.32") # noqa
|
||||
@extensions.expected_errors(())
|
||||
def detail(self, req):
|
||||
return self._detail(req)
|
||||
|
||||
def _detail(self, req, limit=None, marker=None, links=False):
|
||||
context = req.environ['nova.context']
|
||||
context.can(hv_policies.BASE_POLICY_NAME)
|
||||
compute_nodes = self.host_api.compute_node_get_all(context)
|
||||
|
||||
try:
|
||||
compute_nodes = self.host_api.compute_node_get_all(
|
||||
context, limit=limit, marker=marker)
|
||||
except exception.MarkerNotFound:
|
||||
msg = _('marker [%s] not found') % marker
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
req.cache_db_compute_nodes(compute_nodes)
|
||||
return dict(hypervisors=[self._view_hypervisor(
|
||||
hypervisors_list = [
|
||||
self._view_hypervisor(
|
||||
hyp, self.host_api.service_get_by_compute_host(context, hyp.host),
|
||||
True, req) for hyp in compute_nodes])
|
||||
True, req) for hyp in compute_nodes]
|
||||
hypervisors_dict = dict(hypervisors=hypervisors_list)
|
||||
if links:
|
||||
hypervisors_links = self._view_builder.get_links(
|
||||
req, hypervisors_list, detail=True)
|
||||
if hypervisors_links:
|
||||
hypervisors_dict['hypervisors_links'] = hypervisors_links
|
||||
return hypervisors_dict
|
||||
|
||||
@extensions.expected_errors(404)
|
||||
def show(self, req, id):
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Copyright 2016 Kylin Cloud
|
||||
# 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 nova.api.openstack import common
|
||||
|
||||
|
||||
class ViewBuilder(common.ViewBuilder):
|
||||
_collection_name = "hypervisors"
|
||||
|
||||
def get_links(self, request, hypervisors, detail=False):
|
||||
coll_name = (self._collection_name + '/detail' if detail else
|
||||
self._collection_name)
|
||||
return self._get_collection_links(request, hypervisors, coll_name,
|
||||
'id')
|
|
@ -335,3 +335,11 @@ user documentation.
|
|||
config drive. For example, a network interface on the virtual PCI bus tagged
|
||||
with 'nic1' will appear in the metadata along with its bus (PCI), bus address
|
||||
(ex: 0000:00:02.0), MAC address, and tag ('nic1').
|
||||
|
||||
2.33
|
||||
----
|
||||
|
||||
Support pagination for hypervisor by accepting limit and marker from the GET
|
||||
API request::
|
||||
|
||||
GET /v2.1/{tenant_id}/os-hypervisors?marker={hypervisor_id}&limit={limit}
|
||||
|
|
|
@ -3666,8 +3666,9 @@ class HostAPI(base.Base):
|
|||
"""Return compute node entry for particular integer ID."""
|
||||
return objects.ComputeNode.get_by_id(context, int(compute_id))
|
||||
|
||||
def compute_node_get_all(self, context):
|
||||
return objects.ComputeNodeList.get_all(context)
|
||||
def compute_node_get_all(self, context, limit=None, marker=None):
|
||||
return objects.ComputeNodeList.get_by_pagination(
|
||||
context, limit=limit, marker=marker)
|
||||
|
||||
def compute_node_search_by_hypervisor(self, context, hypervisor_match):
|
||||
return objects.ComputeNodeList.get_by_hypervisor(context,
|
||||
|
|
|
@ -601,7 +601,9 @@ class HostAPI(compute_api.HostAPI):
|
|||
except exception.CellRoutingInconsistency:
|
||||
raise exception.ComputeHostNotFound(host=compute_id)
|
||||
|
||||
def compute_node_get_all(self, context):
|
||||
def compute_node_get_all(self, context, limit=None, marker=None):
|
||||
# NOTE(lyj): No pagination for cells, just make sure the arguments
|
||||
# for the method are the same with the compute.api for now.
|
||||
return self.cells_rpcapi.compute_node_get_all(context)
|
||||
|
||||
def compute_node_search_by_hypervisor(self, context, hypervisor_match):
|
||||
|
|
|
@ -244,6 +244,19 @@ def compute_node_get_all(context):
|
|||
return IMPL.compute_node_get_all(context)
|
||||
|
||||
|
||||
def compute_node_get_all_by_pagination(context, limit=None, marker=None):
|
||||
"""Get compute nodes by pagination.
|
||||
:param context: The security context
|
||||
:param limit: Maximum number of items to return
|
||||
:param marker: The last item of the previous page, the next results after
|
||||
this value will be returned
|
||||
|
||||
:returns: List of dictionaries each containing compute node properties
|
||||
"""
|
||||
return IMPL.compute_node_get_all_by_pagination(context,
|
||||
limit=limit, marker=marker)
|
||||
|
||||
|
||||
def compute_node_get_all_by_host(context, host):
|
||||
"""Get compute nodes by host name
|
||||
|
||||
|
|
|
@ -564,7 +564,7 @@ def service_update(context, service_id, values):
|
|||
###################
|
||||
|
||||
|
||||
def _compute_node_select(context, filters=None):
|
||||
def _compute_node_select(context, filters=None, limit=None, marker=None):
|
||||
if filters is None:
|
||||
filters = {}
|
||||
|
||||
|
@ -582,11 +582,22 @@ def _compute_node_select(context, filters=None):
|
|||
if "hypervisor_hostname" in filters:
|
||||
hyp_hostname = filters["hypervisor_hostname"]
|
||||
select = select.where(cn_tbl.c.hypervisor_hostname == hyp_hostname)
|
||||
if marker is not None:
|
||||
try:
|
||||
compute_node_get(context, marker)
|
||||
except exception.ComputeHostNotFound:
|
||||
raise exception.MarkerNotFound(marker)
|
||||
select = select.where(cn_tbl.c.id > marker)
|
||||
if limit is not None:
|
||||
select = select.limit(limit)
|
||||
# Explictly order by id, so we're not dependent on the native sort
|
||||
# order of the underlying DB.
|
||||
select = select.order_by(asc("id"))
|
||||
return select
|
||||
|
||||
|
||||
def _compute_node_fetchall(context, filters=None):
|
||||
select = _compute_node_select(context, filters)
|
||||
def _compute_node_fetchall(context, filters=None, limit=None, marker=None):
|
||||
select = _compute_node_select(context, filters, limit=limit, marker=marker)
|
||||
engine = get_engine(context)
|
||||
conn = engine.connect()
|
||||
|
||||
|
@ -648,6 +659,11 @@ def compute_node_get_all(context):
|
|||
return _compute_node_fetchall(context)
|
||||
|
||||
|
||||
@pick_context_manager_reader
|
||||
def compute_node_get_all_by_pagination(context, limit=None, marker=None):
|
||||
return _compute_node_fetchall(context, limit=limit, marker=marker)
|
||||
|
||||
|
||||
@pick_context_manager_reader
|
||||
def compute_node_search_by_hypervisor(context, hypervisor_match):
|
||||
field = models.ComputeNode.hypervisor_hostname
|
||||
|
|
|
@ -452,7 +452,8 @@ class ComputeNodeList(base.ObjectListBase, base.NovaObject):
|
|||
# Version 1.12 ComputeNode version 1.12
|
||||
# Version 1.13 ComputeNode version 1.13
|
||||
# Version 1.14 ComputeNode version 1.14
|
||||
VERSION = '1.14'
|
||||
# Version 1.15 ComputeNode version 1.15
|
||||
VERSION = '1.15'
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('ComputeNode'),
|
||||
}
|
||||
|
@ -463,6 +464,13 @@ class ComputeNodeList(base.ObjectListBase, base.NovaObject):
|
|||
return base.obj_make_list(context, cls(context), objects.ComputeNode,
|
||||
db_computes)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_pagination(cls, context, limit=None, marker=None):
|
||||
db_computes = db.compute_node_get_all_by_pagination(
|
||||
context, limit=limit, marker=marker)
|
||||
return base.obj_make_list(context, cls(context), objects.ComputeNode,
|
||||
db_computes)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_hypervisor(cls, context, hypervisor_match):
|
||||
db_computes = db.compute_node_search_by_hypervisor(context,
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"cpu_info": {
|
||||
"arch": "x86_64",
|
||||
"model": "Nehalem",
|
||||
"vendor": "Intel",
|
||||
"features": [
|
||||
"pge",
|
||||
"clflush"
|
||||
],
|
||||
"topology": {
|
||||
"cores": 1,
|
||||
"threads": 1,
|
||||
"sockets": 4
|
||||
}
|
||||
},
|
||||
"current_workload": 0,
|
||||
"state": "up",
|
||||
"status": "enabled",
|
||||
"disk_available_least": 0,
|
||||
"host_ip": "%(ip)s",
|
||||
"free_disk_gb": 1028,
|
||||
"free_ram_mb": 7680,
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"hypervisor_type": "fake",
|
||||
"hypervisor_version": 1000,
|
||||
"id": %(hypervisor_id)s,
|
||||
"local_gb": 1028,
|
||||
"local_gb_used": 0,
|
||||
"memory_mb": 8192,
|
||||
"memory_mb_used": 512,
|
||||
"running_vms": 0,
|
||||
"service": {
|
||||
"host": "%(host_name)s",
|
||||
"id": 7,
|
||||
"disabled_reason": null
|
||||
},
|
||||
"vcpus": 1,
|
||||
"vcpus_used": 0
|
||||
}
|
||||
],
|
||||
"hypervisors_links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/hypervisors/detail?limit=1&marker=2",
|
||||
"rel": "next"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"id": 2,
|
||||
"state": "up",
|
||||
"status": "enabled"
|
||||
}
|
||||
],
|
||||
"hypervisors_links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/hypervisors?limit=1&marker=2",
|
||||
"rel": "next"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -140,3 +140,30 @@ class HypervisorsSampleJson228Tests(HypervisorsSampleJsonTests):
|
|||
def setUp(self):
|
||||
super(HypervisorsSampleJson228Tests, self).setUp()
|
||||
self.api.microversion = self.microversion
|
||||
|
||||
|
||||
class HypervisorsSampleJson233Tests(api_sample_base.ApiSampleTestBaseV21):
|
||||
ADMIN_API = True
|
||||
sample_dir = "os-hypervisors"
|
||||
microversion = '2.33'
|
||||
scenarios = [('v2_33', {'api_major_version': 'v2.1'})]
|
||||
|
||||
def setUp(self):
|
||||
super(HypervisorsSampleJson233Tests, self).setUp()
|
||||
self.api.microversion = self.microversion
|
||||
# Start a new compute service to fake a record with hypervisor id=2
|
||||
# for pagination test.
|
||||
self.start_service('compute', host='host1')
|
||||
|
||||
def test_hypervisors_list(self):
|
||||
response = self._do_get('os-hypervisors?limit=1&marker=1')
|
||||
self._verify_response('hypervisors-list-resp', {}, response, 200)
|
||||
|
||||
def test_hypervisors_detail(self):
|
||||
subs = {
|
||||
'hypervisor_id': '2',
|
||||
'host': 'host1',
|
||||
'host_name': 'host1'
|
||||
}
|
||||
response = self._do_get('os-hypervisors/detail?limit=1&marker=1')
|
||||
self._verify_response('hypervisors-detail-resp', subs, response, 200)
|
||||
|
|
|
@ -32,7 +32,7 @@ def fake_compute_node_get(context, compute_id):
|
|||
raise exception.ComputeHostNotFound(host=compute_id)
|
||||
|
||||
|
||||
def fake_compute_node_get_all(context):
|
||||
def fake_compute_node_get_all(context, limit=None, marker=None):
|
||||
return test_hypervisors.TEST_HYPERS_OBJ
|
||||
|
||||
|
||||
|
|
|
@ -108,8 +108,18 @@ TEST_SERVERS = [dict(name="inst1", uuid=uuids.instance_1, host="compute1"),
|
|||
dict(name="inst4", uuid=uuids.instance_4, host="compute2")]
|
||||
|
||||
|
||||
def fake_compute_node_get_all(context):
|
||||
return TEST_HYPERS_OBJ
|
||||
def fake_compute_node_get_all(context, limit=None, marker=None):
|
||||
if marker in ['99999']:
|
||||
raise exception.MarkerNotFound(marker)
|
||||
marker_found = True if marker is None else False
|
||||
output = []
|
||||
for hyper in TEST_HYPERS_OBJ:
|
||||
if not marker_found and marker == str(hyper.id):
|
||||
marker_found = True
|
||||
elif marker_found:
|
||||
if limit is None or len(output) < int(limit):
|
||||
output.append(hyper)
|
||||
return output
|
||||
|
||||
|
||||
def fake_compute_node_search_by_hypervisor(context, hypervisor_re):
|
||||
|
@ -191,8 +201,9 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
|||
dict(id=2, hypervisor_hostname="hyper2",
|
||||
state='up', status='enabled')]
|
||||
|
||||
def _get_request(self, use_admin_context):
|
||||
return fakes.HTTPRequest.blank('', use_admin_context=use_admin_context,
|
||||
def _get_request(self, use_admin_context, url=''):
|
||||
return fakes.HTTPRequest.blank(url,
|
||||
use_admin_context=use_admin_context,
|
||||
version=self.api_version)
|
||||
|
||||
def _set_up_controller(self):
|
||||
|
@ -477,7 +488,7 @@ class CellHypervisorsTestV21(HypervisorsTestV21):
|
|||
for hyp in INDEX_HYPER_DICTS]
|
||||
|
||||
@classmethod
|
||||
def fake_compute_node_get_all(cls, context):
|
||||
def fake_compute_node_get_all(cls, context, limit=None, marker=None):
|
||||
return cls.TEST_HYPERS_OBJ
|
||||
|
||||
@classmethod
|
||||
|
@ -532,3 +543,79 @@ class HypervisorsTestV228(HypervisorsTestV21):
|
|||
DETAIL_HYPERS_DICTS = copy.deepcopy(HypervisorsTestV21.DETAIL_HYPERS_DICTS)
|
||||
DETAIL_HYPERS_DICTS[0]['cpu_info'] = jsonutils.loads(CPU_INFO)
|
||||
DETAIL_HYPERS_DICTS[1]['cpu_info'] = jsonutils.loads(CPU_INFO)
|
||||
|
||||
|
||||
class HypervisorsTestV233(HypervisorsTestV228):
|
||||
api_version = '2.33'
|
||||
|
||||
def test_index_pagination(self):
|
||||
req = self._get_request(True,
|
||||
'/v2/1234/os-hypervisors?limit=1&marker=1')
|
||||
result = self.controller.index(req)
|
||||
expected = {
|
||||
'hypervisors': [
|
||||
{'hypervisor_hostname': 'hyper2',
|
||||
'id': 2,
|
||||
'state': 'up',
|
||||
'status': 'enabled'}
|
||||
],
|
||||
'hypervisors_links': [
|
||||
{'href': 'http://localhost/v2/hypervisors?limit=1&marker=2',
|
||||
'rel': 'next'}
|
||||
]
|
||||
}
|
||||
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_index_pagination_with_invalid_marker(self):
|
||||
req = self._get_request(True,
|
||||
'/v2/1234/os-hypervisors?marker=99999')
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.controller.index, req)
|
||||
|
||||
def test_detail_pagination(self):
|
||||
req = self._get_request(
|
||||
True, '/v2/1234/os-hypervisors/detail?limit=1&marker=1')
|
||||
result = self.controller.detail(req)
|
||||
link = 'http://localhost/v2/hypervisors/detail?limit=1&marker=2'
|
||||
expected = {
|
||||
'hypervisors': [
|
||||
{'cpu_info': {'arch': 'x86_64',
|
||||
'features': [],
|
||||
'model': '',
|
||||
'topology': {'cores': 1,
|
||||
'sockets': 1,
|
||||
'threads': 1},
|
||||
'vendor': 'fake'},
|
||||
'current_workload': 2,
|
||||
'disk_available_least': 100,
|
||||
'free_disk_gb': 125,
|
||||
'free_ram_mb': 5120,
|
||||
'host_ip': netaddr.IPAddress('2.2.2.2'),
|
||||
'hypervisor_hostname': 'hyper2',
|
||||
'hypervisor_type': 'xen',
|
||||
'hypervisor_version': 3,
|
||||
'id': 2,
|
||||
'local_gb': 250,
|
||||
'local_gb_used': 125,
|
||||
'memory_mb': 10240,
|
||||
'memory_mb_used': 5120,
|
||||
'running_vms': 2,
|
||||
'service': {'disabled_reason': None,
|
||||
'host': 'compute2',
|
||||
'id': 2},
|
||||
'state': 'up',
|
||||
'status': 'enabled',
|
||||
'vcpus': 4,
|
||||
'vcpus_used': 2}
|
||||
],
|
||||
'hypervisors_links': [{'href': link, 'rel': 'next'}]
|
||||
}
|
||||
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_detail_pagination_with_invalid_marker(self):
|
||||
req = self._get_request(True,
|
||||
'/v2/1234/os-hypervisors/detail?marker=99999')
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.controller.index, req)
|
||||
|
|
|
@ -7488,6 +7488,58 @@ class ComputeNodeTestCase(test.TestCase, ModelsObjectComparatorMixin):
|
|||
new_stats = jsonutils.loads(node['stats'])
|
||||
self.assertEqual(self.stats, new_stats)
|
||||
|
||||
def test_compute_node_get_all_by_pagination(self):
|
||||
service_dict = dict(host='host2', binary='nova-compute',
|
||||
topic=CONF.compute_topic, report_count=1,
|
||||
disabled=False)
|
||||
service = db.service_create(self.ctxt, service_dict)
|
||||
compute_node_dict = dict(vcpus=2, memory_mb=1024, local_gb=2048,
|
||||
uuid=uuidsentinel.fake_compute_node,
|
||||
vcpus_used=0, memory_mb_used=0,
|
||||
local_gb_used=0, free_ram_mb=1024,
|
||||
free_disk_gb=2048, hypervisor_type="xen",
|
||||
hypervisor_version=1, cpu_info="",
|
||||
running_vms=0, current_workload=0,
|
||||
service_id=service['id'],
|
||||
host=service['host'],
|
||||
disk_available_least=100,
|
||||
hypervisor_hostname='abcde11',
|
||||
host_ip='127.0.0.1',
|
||||
supported_instances='',
|
||||
pci_stats='',
|
||||
metrics='',
|
||||
extra_resources='',
|
||||
cpu_allocation_ratio=16.0,
|
||||
ram_allocation_ratio=1.5,
|
||||
disk_allocation_ratio=1.0,
|
||||
stats='', numa_topology='')
|
||||
stats = dict(num_instances=2, num_proj_12345=1,
|
||||
num_proj_23456=1, num_vm_building=2)
|
||||
compute_node_dict['stats'] = jsonutils.dumps(stats)
|
||||
db.compute_node_create(self.ctxt, compute_node_dict)
|
||||
|
||||
nodes = db.compute_node_get_all_by_pagination(self.ctxt,
|
||||
limit=1, marker=1)
|
||||
self.assertEqual(1, len(nodes))
|
||||
node = nodes[0]
|
||||
self._assertEqualObjects(compute_node_dict, node,
|
||||
ignored_keys=self._ignored_keys +
|
||||
['stats', 'service'])
|
||||
new_stats = jsonutils.loads(node['stats'])
|
||||
self.assertEqual(stats, new_stats)
|
||||
|
||||
nodes = db.compute_node_get_all_by_pagination(self.ctxt)
|
||||
self.assertEqual(2, len(nodes))
|
||||
node = nodes[0]
|
||||
self._assertEqualObjects(self.compute_node_dict, node,
|
||||
ignored_keys=self._ignored_keys +
|
||||
['stats', 'service'])
|
||||
new_stats = jsonutils.loads(node['stats'])
|
||||
self.assertEqual(self.stats, new_stats)
|
||||
self.assertRaises(exception.MarkerNotFound,
|
||||
db.compute_node_get_all_by_pagination,
|
||||
self.ctxt, limit=1, marker=999)
|
||||
|
||||
def test_compute_node_get_all_deleted_compute_node(self):
|
||||
# Create a service and compute node and ensure we can find its stats;
|
||||
# delete the service and compute node when done and loop again
|
||||
|
|
|
@ -382,6 +382,16 @@ class _TestComputeNodeObject(object):
|
|||
subs=self.subs(),
|
||||
comparators=self.comparators())
|
||||
|
||||
@mock.patch('nova.db.compute_node_get_all_by_pagination',
|
||||
return_value=[fake_compute_node])
|
||||
def test_get_by_pagination(self, fake_get_by_pagination):
|
||||
computes = compute_node.ComputeNodeList.get_by_pagination(
|
||||
self.context, limit=1, marker=1)
|
||||
self.assertEqual(1, len(computes))
|
||||
self.compare_obj(computes[0], fake_compute_node,
|
||||
subs=self.subs(),
|
||||
comparators=self.comparators())
|
||||
|
||||
@mock.patch('nova.db.compute_nodes_get_by_service_id')
|
||||
def test__get_by_service(self, cn_get_by_svc_id):
|
||||
cn_get_by_svc_id.return_value = [fake_compute_node]
|
||||
|
|
|
@ -1108,7 +1108,7 @@ object_data = {
|
|||
'CellMapping': '1.0-7f1a7e85a22bbb7559fc730ab658b9bd',
|
||||
'CellMappingList': '1.0-4ee0d9efdfd681fed822da88376e04d2',
|
||||
'ComputeNode': '1.16-2436e5b836fa0306a3c4e6d9e5ddacec',
|
||||
'ComputeNodeList': '1.14-3b6f4f5ade621c40e70cb116db237844',
|
||||
'ComputeNodeList': '1.15-4ec4ea3ed297edbd25c33e2aaf797cca',
|
||||
'DNSDomain': '1.0-7b0b2dab778454b6a7b6c66afe163a1a',
|
||||
'DNSDomainList': '1.0-4ee0d9efdfd681fed822da88376e04d2',
|
||||
'Destination': '1.0-4c59dd1288b2e7adbda6051a2de59183',
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
features:
|
||||
- Added microversion v2.33 which adds paging support for hypervisors, the
|
||||
admin is able to perform paginate query by using limit and marker to get
|
||||
a list of hypervisors. The result will be sorted by hypervisor id.
|
Loading…
Reference in New Issue