diff --git a/api-ref/source/v1/nodes.inc b/api-ref/source/v1/nodes.inc new file mode 100644 index 00000000..127c552f --- /dev/null +++ b/api-ref/source/v1/nodes.inc @@ -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 diff --git a/api-ref/source/v1/parameters.yaml b/api-ref/source/v1/parameters.yaml index 79f7dd42..8bb040c3 100644 --- a/api-ref/source/v1/parameters.yaml +++ b/api-ref/source/v1/parameters.yaml @@ -435,6 +435,12 @@ nics: in: body required: true type: dict +nodes: + description: | + The compute node name list. + in: body + required: true + type: dict personality: description: | The file path and contents, text only, to inject into the server at launch. The diff --git a/api-ref/source/v1/samples/nodes/node-list-resp.json b/api-ref/source/v1/samples/nodes/node-list-resp.json new file mode 100644 index 00000000..8d801f17 --- /dev/null +++ b/api-ref/source/v1/samples/nodes/node-list-resp.json @@ -0,0 +1,7 @@ +{ + "nodes" : [ + "node-0", + "node-1", + "node-2" + ] +} diff --git a/mogan/api/controllers/v1/__init__.py b/mogan/api/controllers/v1/__init__.py index 893028ef..d9409f5e 100644 --- a/mogan/api/controllers/v1/__init__.py +++ b/mogan/api/controllers/v1/__init__.py @@ -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 flavors from mogan.api.controllers.v1 import keypairs +from mogan.api.controllers.v1 import nodes from mogan.api.controllers.v1 import servers from mogan.api import expose @@ -54,6 +55,9 @@ class V1(base.APIBase): aggregates = [link.Link] """Links to the aggregates resource""" + nodes = [link.Link] + """Links to the nodes resource""" + @staticmethod def convert(): v1 = V1() @@ -96,6 +100,14 @@ class V1(base.APIBase): 'aggregates', '', 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 @@ -107,6 +119,7 @@ class Controller(rest.RestController): availability_zones = availability_zone.AvailabilityZoneController() keypairs = keypairs.KeyPairController() aggregates = aggregates.AggregateController() + nodes = nodes.NodeController() @expose.expose(V1) def get(self): diff --git a/mogan/api/controllers/v1/nodes.py b/mogan/api/controllers/v1/nodes.py new file mode 100644 index 00000000..00d04b94 --- /dev/null +++ b/mogan/api/controllers/v1/nodes.py @@ -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 diff --git a/mogan/common/policy.py b/mogan/common/policy.py index 67e8678e..383d729e 100644 --- a/mogan/common/policy.py +++ b/mogan/common/policy.py @@ -159,6 +159,9 @@ server_policies = [ policy.RuleDefault('mogan:aggregate:get_one', 'rule:admin_api', description='Show aggregate details'), + policy.RuleDefault('mogan:node:get_all', + 'rule:admin_api', + description='Get the nodes list'), ] diff --git a/mogan/engine/api.py b/mogan/engine/api.py index 88f3a310..9fdc1737 100644 --- a/mogan/engine/api.py +++ b/mogan/engine/api.py @@ -516,3 +516,7 @@ class API(object): def detach_interface(self, context, server, port_id): self.engine_rpcapi.detach_interface(context, server=server, port_id=port_id) + + def list_compute_nodes(self, context): + """Get compute node list.""" + return self.engine_rpcapi.list_compute_nodes(context) diff --git a/mogan/engine/manager.py b/mogan/engine/manager.py index e93c440d..83b7d9c9 100644 --- a/mogan/engine/manager.py +++ b/mogan/engine/manager.py @@ -580,3 +580,8 @@ class EngineManager(base_manager.BaseEngineManager): nic.delete(context) LOG.info('Interface was successfully detached') + + def list_compute_nodes(self, context): + nodes = self.scheduler_client.reportclient \ + .get_nodes_from_resource_providers() + return nodes diff --git a/mogan/engine/rpcapi.py b/mogan/engine/rpcapi.py index 16db2095..f3f30abf 100644 --- a/mogan/engine/rpcapi.py +++ b/mogan/engine/rpcapi.py @@ -92,3 +92,7 @@ class EngineAPI(object): cctxt = self.client.prepare(topic=self.topic, server=CONF.host) cctxt.call(context, 'detach_interface', server=server, 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') diff --git a/mogan/scheduler/client/report.py b/mogan/scheduler/client/report.py index 2612b9c0..739d0878 100644 --- a/mogan/scheduler/client/report.py +++ b/mogan/scheduler/client/report.py @@ -709,3 +709,8 @@ class SchedulerReportClient(object): LOG.info('Deleted allocation for resource provider %s', rp_uuid) for consumer_id in allocations: 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()]} diff --git a/mogan/tests/functional/api/v1/test_nodes.py b/mogan/tests/functional/api/v1/test_nodes.py new file mode 100644 index 00000000..2ce2b259 --- /dev/null +++ b/mogan/tests/functional/api/v1/test_nodes.py @@ -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']) diff --git a/mogan/tests/tempest/api/test_nodes.py b/mogan/tests/tempest/api/test_nodes.py new file mode 100644 index 00000000..c0186f1e --- /dev/null +++ b/mogan/tests/tempest/api/test_nodes.py @@ -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)