Add server sub-resource topology API
Add a new server topology API to show server NUMA information: - GET /servers/{server_id}/topology Add new policy to control the default behavior: - compute:server:topology:index - compute:server:topology:host:index Change-Id: Ie647ef96597195b0ef00f77cece16c2bef8a78d4 Implements: blueprint show-server-numa-topology Signed-off-by: Yongli He <yongli.he@intel.com>
This commit is contained in:
parent
eb6fcb2191
commit
3dcb404b1f
@ -53,6 +53,7 @@ the `API guide <https://docs.openstack.org/api-guide/compute/index.html>`_.
|
||||
.. include:: os-services.inc
|
||||
.. include:: os-simple-tenant-usage.inc
|
||||
.. include:: os-server-external-events.inc
|
||||
.. include:: server-topology.inc
|
||||
|
||||
===============
|
||||
Deprecated APIs
|
||||
|
@ -6354,6 +6354,77 @@ server_tags_create:
|
||||
required: false
|
||||
type: array
|
||||
min_version: 2.52
|
||||
server_topology_nodes:
|
||||
description: |
|
||||
NUMA nodes information of a server.
|
||||
in: body
|
||||
required: true
|
||||
type: array
|
||||
server_topology_nodes_cpu_pinning:
|
||||
description: |
|
||||
The mapping of server cores to host physical CPU. for example::
|
||||
|
||||
cpu_pinning: { 0: 0, 1: 5}
|
||||
|
||||
This means vcpu 0 is mapped to physical CPU 0, and vcpu 1 is mapped
|
||||
physical CPU 5.
|
||||
|
||||
By default the ``cpu_pinning`` field is only visible to users with the
|
||||
administrative role. You can change the default behavior via the policy
|
||||
rule::
|
||||
|
||||
compute:server:topology:host:index
|
||||
in: body
|
||||
required: false
|
||||
type: dict
|
||||
server_topology_nodes_cpu_siblings:
|
||||
description: |
|
||||
A mapping of host cpus thread sibling. For example::
|
||||
|
||||
siblings: [[0,1],[2,3]]
|
||||
|
||||
This means vcpu 0 and vcpu 1 belong to same CPU core, vcpu 2, vcpu 3
|
||||
belong to another CPU core.
|
||||
|
||||
By default the ``siblings`` field is only visible to users with the
|
||||
administrative role. You can change the default behavior via the policy
|
||||
rule::
|
||||
|
||||
compute:server:topology:host:index
|
||||
in: body
|
||||
required: false
|
||||
type: list
|
||||
server_topology_nodes_host_numa_node:
|
||||
description: |
|
||||
The host NUMA node the virtual NUMA node is map to.
|
||||
|
||||
By default the ``host_numa_node`` field is only visible to users with the
|
||||
administrator role. You can change the default behavior via the policy
|
||||
rule::
|
||||
|
||||
compute:server:topology:host:index
|
||||
in: body
|
||||
required: false
|
||||
type: integer
|
||||
server_topology_nodes_memory_mb:
|
||||
description: |
|
||||
The amount of memory assigned to this NUMA node in MB.
|
||||
in: body
|
||||
required: false
|
||||
type: integer
|
||||
server_topology_nodes_vcpu_set:
|
||||
description: |
|
||||
A list of IDs of the virtual CPU assigned to this NUMA node.
|
||||
in: body
|
||||
required: false
|
||||
type: list
|
||||
server_topology_pagesize_kb:
|
||||
description: |
|
||||
The page size in KB of a server. This field is ``null`` if the
|
||||
page size information is not available.
|
||||
in: body
|
||||
required: true
|
||||
type: integer
|
||||
server_trusted_image_certificates_create_req:
|
||||
description: |
|
||||
A list of trusted certificate IDs, which are used during image
|
||||
|
52
api-ref/source/server-topology.inc
Normal file
52
api-ref/source/server-topology.inc
Normal file
@ -0,0 +1,52 @@
|
||||
.. -*- rst -*-
|
||||
|
||||
=====================================
|
||||
Servers Topology (servers, topology)
|
||||
=====================================
|
||||
|
||||
Shows the NUMA topology information for a server.
|
||||
|
||||
Show Server Topology
|
||||
====================
|
||||
|
||||
.. rest_method:: GET /servers/{server_id}/topology
|
||||
.. versionadded:: 2.78
|
||||
|
||||
Shows NUMA topology information for a server.
|
||||
|
||||
Policy defaults enable only users with the administrative role or the owners
|
||||
of the server to perform this operation. Cloud providers can change these
|
||||
permissions through the ``policy.json`` file.
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
Error response codes: unauthorized(401), notfound(404), forbidden(403)
|
||||
|
||||
Request
|
||||
-------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- server_id: server_id_path
|
||||
|
||||
Response
|
||||
--------
|
||||
|
||||
All response fields are listed below. If some information is not available or
|
||||
not allow by policy, the corresponding key value will not exist in response.
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- nodes: server_topology_nodes
|
||||
- nodes.cpu_pinning: server_topology_nodes_cpu_pinning
|
||||
- nodes.vcpu_set: server_topology_nodes_vcpu_set
|
||||
- nodes.siblings: server_topology_nodes_cpu_siblings
|
||||
- nodes.memory_mb: server_topology_nodes_memory_mb
|
||||
- nodes.host_numa_node: server_topology_nodes_host_numa_node
|
||||
- pagesize_kb: server_topology_pagesize_kb
|
||||
|
||||
**Example Server topology (2.xx)**
|
||||
|
||||
.. literalinclude:: ../../doc/api_samples/os-server-topology/v2.78/servers-topology-resp.json
|
||||
:language: javascript
|
||||
|
@ -0,0 +1,31 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"memory_mb": 1024,
|
||||
"siblings": [
|
||||
[
|
||||
0,
|
||||
1
|
||||
]
|
||||
],
|
||||
"vcpu_set": [
|
||||
0,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"memory_mb": 2048,
|
||||
"siblings": [
|
||||
[
|
||||
2,
|
||||
3
|
||||
]
|
||||
],
|
||||
"vcpu_set": [
|
||||
2,
|
||||
3
|
||||
]
|
||||
}
|
||||
],
|
||||
"pagesize_kb": 4
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"cpu_pinning": {
|
||||
"0": 0,
|
||||
"1": 5
|
||||
},
|
||||
"host_node": 0,
|
||||
"memory_mb": 1024,
|
||||
"siblings": [
|
||||
[
|
||||
0,
|
||||
1
|
||||
]
|
||||
],
|
||||
"vcpu_set": [
|
||||
0,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"cpu_pinning": {
|
||||
"2": 1,
|
||||
"3": 8
|
||||
},
|
||||
"host_node": 1,
|
||||
"memory_mb": 2048,
|
||||
"siblings": [
|
||||
[
|
||||
2,
|
||||
3
|
||||
]
|
||||
],
|
||||
"vcpu_set": [
|
||||
2,
|
||||
3
|
||||
]
|
||||
}
|
||||
],
|
||||
"pagesize_kb": 4
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.77",
|
||||
"version": "2.78",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.77",
|
||||
"version": "2.78",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -203,6 +203,8 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
``GET /servers/{server_id}/os-instance-actions/{request_id}``.
|
||||
* 2.77 - Add support for specifying ``availability_zone`` to unshelve of a
|
||||
shelved offload server.
|
||||
* 2.78 - Adds new API ``GET /servers/{server_id}/topology`` which shows
|
||||
NUMA topology of a given server.
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
@ -211,7 +213,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.77"
|
||||
_MAX_API_VERSION = "2.78"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
# Almost all proxy APIs which are related to network, images and baremetal
|
||||
|
@ -998,3 +998,15 @@ through ``GET /servers/{server_id}/os-instance-actions`` and
|
||||
----
|
||||
API microversion 2.77 adds support for specifying availability zone when
|
||||
unshelving a shelved offloaded server.
|
||||
|
||||
2.78
|
||||
----
|
||||
|
||||
Add server sub-resource ``topology`` to show server NUMA information.
|
||||
|
||||
* ``GET /servers/{server_id}/topology``
|
||||
|
||||
The default behavior is configurable using two new policies:
|
||||
|
||||
* ``compute:server:topology:index``
|
||||
* ``compute:server:topology:host:index``
|
||||
|
@ -75,6 +75,7 @@ from nova.api.openstack.compute import server_metadata
|
||||
from nova.api.openstack.compute import server_migrations
|
||||
from nova.api.openstack.compute import server_password
|
||||
from nova.api.openstack.compute import server_tags
|
||||
from nova.api.openstack.compute import server_topology
|
||||
from nova.api.openstack.compute import servers
|
||||
from nova.api.openstack.compute import services
|
||||
from nova.api.openstack.compute import shelve
|
||||
@ -316,6 +317,8 @@ server_security_groups_controller = functools.partial(_create_controller,
|
||||
server_tags_controller = functools.partial(_create_controller,
|
||||
server_tags.ServerTagsController, [])
|
||||
|
||||
server_topology_controller = functools.partial(_create_controller,
|
||||
server_topology.ServerTopologyController, [])
|
||||
|
||||
server_volume_attachments_controller = functools.partial(_create_controller,
|
||||
volumes.VolumeAttachmentController, [])
|
||||
@ -832,6 +835,9 @@ ROUTE_LIST = (
|
||||
'PUT': [server_tags_controller, 'update'],
|
||||
'DELETE': [server_tags_controller, 'delete']
|
||||
}),
|
||||
('/servers/{server_id}/topology', {
|
||||
'GET': [server_topology_controller, 'index']
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
|
73
nova/api/openstack/compute/server_topology.py
Normal file
73
nova/api/openstack/compute/server_topology.py
Normal file
@ -0,0 +1,73 @@
|
||||
#
|
||||
# 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
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.compute import api as compute
|
||||
from nova.policies import server_topology as st_policies
|
||||
|
||||
|
||||
class ServerTopologyController(wsgi.Controller):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ServerTopologyController, self).__init__(*args, **kwargs)
|
||||
self.compute_api = compute.API()
|
||||
|
||||
@wsgi.Controller.api_version("2.78")
|
||||
@wsgi.expected_errors((404))
|
||||
def index(self, req, server_id):
|
||||
context = req.environ["nova.context"]
|
||||
context.can(st_policies.BASE_POLICY_NAME % 'index')
|
||||
host_policy = (st_policies.BASE_POLICY_NAME % 'host:index')
|
||||
show_host_info = context.can(host_policy, fatal=False)
|
||||
|
||||
instance = common.get_instance(self.compute_api, context, server_id,
|
||||
expected_attrs=['numa_topology',
|
||||
'vcpu_model'])
|
||||
|
||||
return self._get_numa_topology(context, instance, show_host_info)
|
||||
|
||||
def _get_numa_topology(self, context, instance, show_host_info):
|
||||
|
||||
if instance.numa_topology is None:
|
||||
return {
|
||||
'nodes': [],
|
||||
'pagesize_kb': None
|
||||
}
|
||||
|
||||
topo = {}
|
||||
cells = []
|
||||
pagesize_kb = None
|
||||
|
||||
for cell_ in instance.numa_topology.cells:
|
||||
cell = {}
|
||||
cell['vcpu_set'] = cell_.cpuset
|
||||
cell['siblings'] = cell_.siblings
|
||||
cell['memory_mb'] = cell_.memory
|
||||
|
||||
if show_host_info:
|
||||
cell['host_node'] = cell_.id
|
||||
if cell_.cpu_pinning is None:
|
||||
cell['cpu_pinning'] = {}
|
||||
else:
|
||||
cell['cpu_pinning'] = cell_.cpu_pinning
|
||||
|
||||
if cell_.pagesize:
|
||||
pagesize_kb = cell_.pagesize
|
||||
|
||||
cells.append(cell)
|
||||
|
||||
topo['nodes'] = cells
|
||||
topo['pagesize_kb'] = pagesize_kb
|
||||
|
||||
return topo
|
@ -61,6 +61,7 @@ from nova.policies import server_groups
|
||||
from nova.policies import server_metadata
|
||||
from nova.policies import server_password
|
||||
from nova.policies import server_tags
|
||||
from nova.policies import server_topology
|
||||
from nova.policies import servers
|
||||
from nova.policies import servers_migrations
|
||||
from nova.policies import services
|
||||
@ -123,6 +124,7 @@ def list_rules():
|
||||
server_metadata.list_rules(),
|
||||
server_password.list_rules(),
|
||||
server_tags.list_rules(),
|
||||
server_topology.list_rules(),
|
||||
servers.list_rules(),
|
||||
servers_migrations.list_rules(),
|
||||
services.list_rules(),
|
||||
|
48
nova/policies/server_topology.py
Normal file
48
nova/policies/server_topology.py
Normal file
@ -0,0 +1,48 @@
|
||||
#
|
||||
# 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 oslo_policy import policy
|
||||
|
||||
from nova.policies import base
|
||||
|
||||
|
||||
BASE_POLICY_NAME = 'compute:server:topology:%s'
|
||||
|
||||
server_topology_policies = [
|
||||
policy.DocumentedRuleDefault(
|
||||
BASE_POLICY_NAME % 'index',
|
||||
base.RULE_ADMIN_OR_OWNER,
|
||||
"Show the NUMA topology data for a server",
|
||||
[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/servers/{server_id}/topology'
|
||||
}
|
||||
]),
|
||||
policy.DocumentedRuleDefault(
|
||||
# Control host NUMA node and cpu pinning information
|
||||
BASE_POLICY_NAME % 'host:index',
|
||||
base.RULE_ADMIN_API,
|
||||
"Show the NUMA topology data for a server with host NUMA ID and CPU "
|
||||
"pinning information",
|
||||
[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/servers/{server_id}/topology'
|
||||
}
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
def list_rules():
|
||||
return server_topology_policies
|
@ -0,0 +1,31 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"memory_mb": 1024,
|
||||
"siblings": [
|
||||
[
|
||||
0,
|
||||
1
|
||||
]
|
||||
],
|
||||
"vcpu_set": [
|
||||
0,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"memory_mb": 2048,
|
||||
"siblings": [
|
||||
[
|
||||
2,
|
||||
3
|
||||
]
|
||||
],
|
||||
"vcpu_set": [
|
||||
2,
|
||||
3
|
||||
]
|
||||
}
|
||||
],
|
||||
"pagesize_kb": 4
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"cpu_pinning": {
|
||||
"0": 0,
|
||||
"1": 5
|
||||
},
|
||||
"host_node": 0,
|
||||
"memory_mb": 1024,
|
||||
"siblings": [
|
||||
[
|
||||
0,
|
||||
1
|
||||
]
|
||||
],
|
||||
"vcpu_set": [
|
||||
0,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"cpu_pinning": {
|
||||
"2": 1,
|
||||
"3": 8
|
||||
},
|
||||
"host_node": 1,
|
||||
"memory_mb": 2048,
|
||||
"siblings": [
|
||||
[
|
||||
2,
|
||||
3
|
||||
]
|
||||
],
|
||||
"vcpu_set": [
|
||||
2,
|
||||
3
|
||||
]
|
||||
}
|
||||
],
|
||||
"pagesize_kb": 4
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
#
|
||||
# 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.objects import instance_numa as numa
|
||||
from nova.objects import virt_cpu_topology as cpu_topo
|
||||
from nova.tests.functional.api_sample_tests import test_servers
|
||||
|
||||
|
||||
def fake_get_numa():
|
||||
cpu_info = {'sockets': 2, 'cores': 1, 'threads': 2}
|
||||
cpu_topology = cpu_topo.VirtCPUTopology.from_dict(cpu_info)
|
||||
cell_0 = numa.InstanceNUMACell(node=0, memory=1024, pagesize=4, id=0,
|
||||
cpu_topology=cpu_topology,
|
||||
cpu_pinning={0: 0, 1: 5},
|
||||
cpuset=set([0, 1]))
|
||||
|
||||
cell_1 = numa.InstanceNUMACell(node=1, memory=2048, pagesize=4, id=1,
|
||||
cpu_topology=cpu_topology,
|
||||
cpu_pinning={2: 1, 3: 8},
|
||||
cpuset=set([2, 3]))
|
||||
|
||||
return numa.InstanceNUMATopology(cells=[cell_0, cell_1])
|
||||
|
||||
|
||||
class ServerTopologySamplesJson(test_servers.ServersSampleBase):
|
||||
microversion = '2.78'
|
||||
scenarios = [('v2_78', {'api_major_version': 'v2.1'})]
|
||||
sample_dir = "os-server-topology"
|
||||
|
||||
def setUp(self):
|
||||
super(ServerTopologySamplesJson, self).setUp()
|
||||
|
||||
def _load_numa(self, *args, **argv):
|
||||
self.numa_topology = fake_get_numa()
|
||||
|
||||
self.stub_out('nova.objects.instance.Instance._load_numa_topology',
|
||||
_load_numa)
|
||||
|
||||
|
||||
class ServerTopologySamplesJsonTestV278_Admin(ServerTopologySamplesJson):
|
||||
ADMIN_API = True
|
||||
|
||||
def test_get_servers_topology_admin(self):
|
||||
uuid = self._post_server()
|
||||
response = self._do_get('servers/%s/topology' % uuid)
|
||||
self._verify_response(
|
||||
'servers-topology-resp', {}, response, 200)
|
||||
|
||||
|
||||
class ServerTopologySamplesJsonTestV278(ServerTopologySamplesJson):
|
||||
ADMIN_API = False
|
||||
|
||||
def test_get_servers_topology_user(self):
|
||||
uuid = self._post_server()
|
||||
response = self._do_get('servers/%s/topology' % uuid)
|
||||
self._verify_response(
|
||||
'servers-topology-resp-user', {}, response, 200)
|
116
nova/tests/unit/api/openstack/compute/test_server_topology.py
Normal file
116
nova/tests/unit/api/openstack/compute/test_server_topology.py
Normal file
@ -0,0 +1,116 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
from oslo_utils.fixture import uuidsentinel as uuids
|
||||
from webob import exc
|
||||
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack.compute import server_topology
|
||||
from nova import exception
|
||||
from nova import objects
|
||||
from nova.objects import instance_numa as numa
|
||||
from nova import test
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
|
||||
|
||||
class ServerTopologyTestV278(test.NoDBTestCase):
|
||||
mock_method = 'get_by_instance_uuid'
|
||||
api_version = '2.78'
|
||||
|
||||
def setUp(self):
|
||||
super(ServerTopologyTestV278, self).setUp()
|
||||
self.uuid = uuids.instance
|
||||
self.req = fakes.HTTPRequest.blank(
|
||||
'/v2/fake/servers/%s/topology' % self.uuid,
|
||||
version=self.api_version,
|
||||
use_admin_context=True)
|
||||
self.controller = server_topology.ServerTopologyController()
|
||||
|
||||
def _fake_numa(self, cpu_pinning=None):
|
||||
ce0 = numa.InstanceNUMACell(node=0, memory=1024, pagesize=4, id=0,
|
||||
cpu_topology=None,
|
||||
cpu_pinning=cpu_pinning,
|
||||
cpuset=set([0, 1]))
|
||||
|
||||
return numa.InstanceNUMATopology(cells=[ce0])
|
||||
|
||||
@mock.patch.object(common, 'get_instance',
|
||||
side_effect=exc.HTTPNotFound('Fake'))
|
||||
def test_get_topology_with_invalid_instance(self, mock_get):
|
||||
excep = self.assertRaises(exc.HTTPNotFound,
|
||||
self.controller.index,
|
||||
self.req,
|
||||
self.uuid)
|
||||
self.assertEqual("Fake", str(excep))
|
||||
|
||||
@mock.patch.object(common, 'get_instance')
|
||||
def test_get_topology_with_no_topology(self, fake_get):
|
||||
expect = {'nodes': [], 'pagesize_kb': None}
|
||||
inst = objects.instance.Instance(uuid=self.uuid, host='123')
|
||||
inst.numa_topology = None
|
||||
fake_get.return_value = inst
|
||||
|
||||
output = self.controller.index(self.req, self.uuid)
|
||||
self.assertEqual(expect, output)
|
||||
|
||||
@mock.patch.object(common, 'get_instance')
|
||||
def test_get_topology_cpu_pinning_with_none(self, fake_get):
|
||||
expect = {'nodes': [
|
||||
{'memory_mb': 1024,
|
||||
'siblings': [],
|
||||
'vcpu_set': set([0, 1]),
|
||||
'host_node': 0,
|
||||
'cpu_pinning':{}}],
|
||||
'pagesize_kb': 4}
|
||||
|
||||
inst = objects.instance.Instance(uuid=self.uuid, host='123')
|
||||
inst.numa_topology = self._fake_numa(cpu_pinning=None)
|
||||
fake_get.return_value = inst
|
||||
|
||||
output = self.controller.index(self.req, self.uuid)
|
||||
self.assertEqual(expect, output)
|
||||
|
||||
inst.numa_topology = self._fake_numa(cpu_pinning={})
|
||||
fake_get.return_value = inst
|
||||
output = self.controller.index(self.req, self.uuid)
|
||||
self.assertEqual(expect, output)
|
||||
|
||||
def test_hit_topology_before278(self):
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/v2/fake/servers/%s/topology' % self.uuid,
|
||||
version='2.77')
|
||||
excep = self.assertRaises(exception.VersionNotFoundForAPIMethod,
|
||||
self.controller.index,
|
||||
req,
|
||||
self.uuid)
|
||||
self.assertEqual(400, excep.code)
|
||||
|
||||
|
||||
class ServerTopologyEnforcementV278(test.NoDBTestCase):
|
||||
api_version = '2.78'
|
||||
|
||||
def setUp(self):
|
||||
super(ServerTopologyEnforcementV278, self).setUp()
|
||||
self.controller = server_topology.ServerTopologyController()
|
||||
self.req = fakes.HTTPRequest.blank('', version=self.api_version)
|
||||
|
||||
def test_get_topology_policy_failed(self):
|
||||
rule_name = "compute:server:topology:index"
|
||||
self.policy.set_rules({rule_name: "project:non_fake"})
|
||||
exc = self.assertRaises(
|
||||
exception.PolicyNotAuthorized,
|
||||
self.controller.index, self.req, fakes.FAKE_UUID)
|
||||
self.assertEqual(
|
||||
"Policy doesn't allow %s to be performed." % rule_name,
|
||||
exc.format_message())
|
@ -288,6 +288,7 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
|
||||
self.fake_policy = jsonutils.loads(fake_policy.policy_data)
|
||||
|
||||
self.admin_only_rules = (
|
||||
"compute:server:topology:host:index",
|
||||
"network:attach_external_network",
|
||||
"os_compute_api:servers:create:forced_host",
|
||||
"compute:servers:create:requested_destination",
|
||||
@ -351,6 +352,7 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
|
||||
)
|
||||
|
||||
self.admin_or_owner_rules = (
|
||||
"compute:server:topology:index",
|
||||
"os_compute_api:servers:start",
|
||||
"os_compute_api:servers:stop",
|
||||
"os_compute_api:servers:trigger_crash_dump",
|
||||
|
@ -0,0 +1,21 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Microversion 2.78 adds a new ``topology`` sub-resource to the
|
||||
servers API:
|
||||
|
||||
- ``GET /servers/{server_id}/topology``
|
||||
|
||||
This API provides information about the NUMA topology of a server,
|
||||
including instance to host CPU pin mappings, if CPU pinning is used, and
|
||||
pagesize information.
|
||||
|
||||
The information exposed by this API is admin or owner only by default,
|
||||
controlled by rule:
|
||||
|
||||
- ``compute:server:topology:index``
|
||||
|
||||
And following fine control policy use to keep host only information to
|
||||
admin:
|
||||
|
||||
- ``compute:server:topology:host:index``
|
Loading…
Reference in New Issue
Block a user