Add node list support

This adds support for listing node names from resource providers,
which will only available for admins.

Partially Implements: bp node-aggregate

Change-Id: I414bf176302fc076288e6a6fbfd88a7090541622
This commit is contained in:
Zhenguo Niu 2017-07-21 15:05:46 +08:00
parent 9627106519
commit 8a8dc20660
12 changed files with 184 additions and 0 deletions

View File

@ -0,0 +1,32 @@
.. -*- rst -*-
===============
Compute Nodes
===============
Lists compute nodes.
List Compute Node information
=============================
.. rest_method:: GET /nodes
Lists compute nodes, including name.
Normal response codes: 200
Error response codes: unauthorized(401), forbidden(403)
Response
--------
.. rest_parameters:: parameters.yaml
- nodes: nodes
|
**Example List compute node information**
.. literalinclude:: samples/nodes/node-list-resp.json
:language: javascript

View File

@ -435,6 +435,12 @@ nics:
in: body in: body
required: true required: true
type: dict type: dict
nodes:
description: |
The compute node name list.
in: body
required: true
type: dict
personality: personality:
description: | description: |
The file path and contents, text only, to inject into the server at launch. The The file path and contents, text only, to inject into the server at launch. The

View File

@ -0,0 +1,7 @@
{
"nodes" : [
"node-0",
"node-1",
"node-2"
]
}

View File

@ -29,6 +29,7 @@ from mogan.api.controllers.v1 import aggregates
from mogan.api.controllers.v1 import availability_zone from mogan.api.controllers.v1 import availability_zone
from mogan.api.controllers.v1 import flavors from mogan.api.controllers.v1 import flavors
from mogan.api.controllers.v1 import keypairs from mogan.api.controllers.v1 import keypairs
from mogan.api.controllers.v1 import nodes
from mogan.api.controllers.v1 import servers from mogan.api.controllers.v1 import servers
from mogan.api import expose from mogan.api import expose
@ -54,6 +55,9 @@ class V1(base.APIBase):
aggregates = [link.Link] aggregates = [link.Link]
"""Links to the aggregates resource""" """Links to the aggregates resource"""
nodes = [link.Link]
"""Links to the nodes resource"""
@staticmethod @staticmethod
def convert(): def convert():
v1 = V1() v1 = V1()
@ -96,6 +100,14 @@ class V1(base.APIBase):
'aggregates', '', 'aggregates', '',
bookmark=True) bookmark=True)
] ]
v1.nodes = [link.Link.make_link('self',
pecan.request.public_url,
'nodes', ''),
link.Link.make_link('bookmark',
pecan.request.public_url,
'nodes', '',
bookmark=True)
]
return v1 return v1
@ -107,6 +119,7 @@ class Controller(rest.RestController):
availability_zones = availability_zone.AvailabilityZoneController() availability_zones = availability_zone.AvailabilityZoneController()
keypairs = keypairs.KeyPairController() keypairs = keypairs.KeyPairController()
aggregates = aggregates.AggregateController() aggregates = aggregates.AggregateController()
nodes = nodes.NodeController()
@expose.expose(V1) @expose.expose(V1)
def get(self): def get(self):

View File

@ -0,0 +1,46 @@
# Copyright 2017 Huawei Technologies Co.,LTD.
# 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.
import pecan
from pecan import rest
from wsme import types as wtypes
from mogan.api.controllers import base
from mogan.api import expose
from mogan.common import policy
class Nodes(base.APIBase):
"""API representation of a collection of nodes."""
nodes = [wtypes.text]
"""A list containing compute node names"""
class NodeController(rest.RestController):
"""REST controller for Node."""
@policy.authorize_wsgi("mogan:node", "get_all",
need_target=False)
@expose.expose(Nodes)
def get_all(self):
"""Retrieve a list of nodes."""
nodes = pecan.request.engine_api.list_compute_nodes(
pecan.request.context)
collection = Nodes()
collection.nodes = nodes['nodes']
return collection

View File

@ -159,6 +159,9 @@ server_policies = [
policy.RuleDefault('mogan:aggregate:get_one', policy.RuleDefault('mogan:aggregate:get_one',
'rule:admin_api', 'rule:admin_api',
description='Show aggregate details'), description='Show aggregate details'),
policy.RuleDefault('mogan:node:get_all',
'rule:admin_api',
description='Get the nodes list'),
] ]

View File

@ -516,3 +516,7 @@ class API(object):
def detach_interface(self, context, server, port_id): def detach_interface(self, context, server, port_id):
self.engine_rpcapi.detach_interface(context, server=server, self.engine_rpcapi.detach_interface(context, server=server,
port_id=port_id) port_id=port_id)
def list_compute_nodes(self, context):
"""Get compute node list."""
return self.engine_rpcapi.list_compute_nodes(context)

View File

@ -580,3 +580,8 @@ class EngineManager(base_manager.BaseEngineManager):
nic.delete(context) nic.delete(context)
LOG.info('Interface was successfully detached') LOG.info('Interface was successfully detached')
def list_compute_nodes(self, context):
nodes = self.scheduler_client.reportclient \
.get_nodes_from_resource_providers()
return nodes

View File

@ -92,3 +92,7 @@ class EngineAPI(object):
cctxt = self.client.prepare(topic=self.topic, server=CONF.host) cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
cctxt.call(context, 'detach_interface', server=server, cctxt.call(context, 'detach_interface', server=server,
port_id=port_id) port_id=port_id)
def list_compute_nodes(self, context):
cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
return cctxt.call(context, 'list_compute_nodes')

View File

@ -709,3 +709,8 @@ class SchedulerReportClient(object):
LOG.info('Deleted allocation for resource provider %s', rp_uuid) LOG.info('Deleted allocation for resource provider %s', rp_uuid)
for consumer_id in allocations: for consumer_id in allocations:
self.delete_allocation_for_server(consumer_id) self.delete_allocation_for_server(consumer_id)
def get_nodes_from_resource_providers(self):
# Use the rps we cached
rps = self._resource_providers
return {'nodes': [rp['name'] for id, rp in rps.items()]}

View File

@ -0,0 +1,30 @@
# Copyright 2017 Huawei Technologies Co., Ltd.
#
# 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 mogan.tests.functional.api import v1 as v1_test
class TestNode(v1_test.APITestV1):
def setUp(self):
super(TestNode, self).setUp()
@mock.patch('mogan.engine.rpcapi.EngineAPI.list_compute_nodes')
def test_node_get_all(self, get_nodes):
get_nodes.return_value = {'nodes': ['node-0', 'node-1']}
headers = self.gen_headers(self.context, roles="admin")
resp = self.get_json('/nodes', headers=headers)
self.assertItemsEqual(['node-0', 'node-1'], resp['nodes'])

View File

@ -0,0 +1,29 @@
# Copyright 2017 Huawei Technologies Co., Ltd.
#
# 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 mogan.tests.tempest.api import base
class BaremetalComputeAPINodesTest(base.BaseBaremetalComputeTest):
@classmethod
def resource_setup(cls):
pass
@classmethod
def resource_cleanup(cls):
pass
def test_nodes_list(self):
nodes = self.baremetal_compute_client.list_nodes()
self.assertIsInstance(nodes, list)
self.assertEqual(len(nodes), 1)