From 44ac91540f6ff393541965b4e49c60a2fdff14a1 Mon Sep 17 00:00:00 2001 From: Zhenguo Niu Date: Thu, 24 Nov 2016 17:40:04 +0800 Subject: [PATCH] Add availability zones API Users should have the ability of listing all availability zones, so they can specify which az they want the instance to be built. Change-Id: Id04f2e0fb64d6e88c4860e695d258239d2349dea Implements: blueprint availability-zone --- api-ref/source/availability_zones.inc | 32 +++++++++++++ api-ref/source/index.rst | 1 + api-ref/source/parameters.yaml | 6 +++ .../availability-zone-list-resp.json | 7 +++ nimble/api/controllers/v1/__init__.py | 2 + .../api/controllers/v1/availability_zone.py | 47 +++++++++++++++++++ nimble/engine/api.py | 8 ++++ nimble/engine/manager.py | 20 ++++++-- nimble/engine/rpcapi.py | 5 ++ .../api/v1/test_availability_zones.py | 29 ++++++++++++ 10 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 api-ref/source/availability_zones.inc create mode 100644 doc/api_samples/availability_zones/availability-zone-list-resp.json create mode 100644 nimble/api/controllers/v1/availability_zone.py create mode 100644 nimble/tests/functional/api/v1/test_availability_zones.py diff --git a/api-ref/source/availability_zones.inc b/api-ref/source/availability_zones.inc new file mode 100644 index 00000000..5f2b615f --- /dev/null +++ b/api-ref/source/availability_zones.inc @@ -0,0 +1,32 @@ +.. -*- rst -*- + +==================== + Availability Zones +==================== + +Lists availability zones. + +List Availability Zone information +================================== + +.. rest_method:: GET /availability_zones + +Lists availability zone information. + +Normal response codes: 200 + +Error response codes: unauthorized(401), forbidden(403) + +Response +-------- + +.. rest_parameters:: parameters.yaml + + - availability_zones: availability_zones + +| + +**Example List availability zone information** + +.. literalinclude:: ../../doc/api_samples/availability_zones/availability-zone-list-resp.json + :language: javascript diff --git a/api-ref/source/index.rst b/api-ref/source/index.rst index da02c07d..e638258a 100644 --- a/api-ref/source/index.rst +++ b/api-ref/source/index.rst @@ -9,3 +9,4 @@ .. include:: urls.inc .. include:: instances.inc .. include:: types.inc +.. include:: availability_zones.inc diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index 53fcbcba..0569f9d1 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -79,6 +79,12 @@ availability_zone: in: body required: false type: string +availability_zones: + description: | + An array of availability zone name. + in: body + required: true + type: array created_at: description: | The date and time when the resource was created. The date and time diff --git a/doc/api_samples/availability_zones/availability-zone-list-resp.json b/doc/api_samples/availability_zones/availability-zone-list-resp.json new file mode 100644 index 00000000..ebfd134a --- /dev/null +++ b/doc/api_samples/availability_zones/availability-zone-list-resp.json @@ -0,0 +1,7 @@ +{ + "availability_zones": [ + "az1", + "az2", + "az3" + ] +} diff --git a/nimble/api/controllers/v1/__init__.py b/nimble/api/controllers/v1/__init__.py index afc15bca..41eab344 100644 --- a/nimble/api/controllers/v1/__init__.py +++ b/nimble/api/controllers/v1/__init__.py @@ -21,6 +21,7 @@ Specification can be found at doc/source/webapi/v1.rst from pecan import rest +from nimble.api.controllers.v1 import availability_zone from nimble.api.controllers.v1 import instance_types from nimble.api.controllers.v1 import instances @@ -30,6 +31,7 @@ class Controller(rest.RestController): types = instance_types.InstanceTypeController() instances = instances.InstanceController() + availability_zones = availability_zone.AvailabilityZoneController() __all__ = ('Controller',) diff --git a/nimble/api/controllers/v1/availability_zone.py b/nimble/api/controllers/v1/availability_zone.py new file mode 100644 index 00000000..f16c7e63 --- /dev/null +++ b/nimble/api/controllers/v1/availability_zone.py @@ -0,0 +1,47 @@ +# Copyright 2016 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 nimble.api.controllers import base +from nimble.api import expose +from nimble.engine import api as engineapi + + +class AvailabilityZones(base.APIBase): + """API representation of a collection of availability zone.""" + + availability_zones = [wtypes.text] + """A list containing availability zone names""" + + +class AvailabilityZoneController(rest.RestController): + """REST controller for Availability Zone.""" + + def __init__(self, **kwargs): + super(AvailabilityZoneController, self).__init__(**kwargs) + self.engine_api = engineapi.API() + + @expose.expose(AvailabilityZones) + def get_all(self): + """Retrieve a list of availability zone.""" + + azs = self.engine_api.list_availability_zones(pecan.request.context) + + collection = AvailabilityZones() + collection.availability_zones = azs['availability_zones'] + return collection diff --git a/nimble/engine/api.py b/nimble/engine/api.py index fa72e575..04327465 100644 --- a/nimble/engine/api.py +++ b/nimble/engine/api.py @@ -110,15 +110,23 @@ class API(object): self._delete_instance(context, instance) def states(self, context, instance): + """Get instance states.""" return self.engine_rpcapi.instance_states(context, instance) def power(self, context, instance, target): + """Set power state of an instance.""" self.engine_rpcapi.set_power_state(context, instance, target) def get_ironic_node(self, context, instance_uuid, fields): + """Get a ironic node by instance UUID.""" return self.engine_rpcapi.get_ironic_node(context, instance_uuid, fields) def get_ironic_node_list(self, context, fields): + """Get a list of ironic node.""" return self.engine_rpcapi.get_ironic_node_list(context, fields) + + def list_availability_zones(self, context): + """Get a list of availability zones.""" + return self.engine_rpcapi.list_availability_zones(context) diff --git a/nimble/engine/manager.py b/nimble/engine/manager.py index 5ae9f106..0b95423a 100644 --- a/nimble/engine/manager.py +++ b/nimble/engine/manager.py @@ -155,7 +155,7 @@ class EngineManager(base_manager.BaseEngineManager): def create_instance(self, context, instance, requested_networks, instance_type): - """Signal to engine service to perform a deployment.""" + """Perform a deployment.""" LOG.debug("Starting instance...") # Populate request spec @@ -212,7 +212,7 @@ class EngineManager(base_manager.BaseEngineManager): return instance def delete_instance(self, context, instance): - """Signal to engine service to delete an instance.""" + """Delete an instance.""" LOG.debug("Deleting instance...") self._destroy_networks(context, instance) @@ -228,7 +228,7 @@ class EngineManager(base_manager.BaseEngineManager): return states.to_dict() def instance_states(self, context, instance): - """Signal to engine service to get an instance states.""" + """Get an instance states.""" LOG.debug("get instance states") return self._instance_states(context, instance) @@ -239,17 +239,29 @@ class EngineManager(base_manager.BaseEngineManager): state) def set_power_state(self, context, instance, state): - """Signal to engine service to get an instance states.""" + """Get an instance states.""" LOG.debug("set power state...") return self._set_power_state(context, instance, state) def get_ironic_node(self, context, instance_uuid, fields): + """Get a ironic node.""" node = ironic.get_node_by_instance(self.ironicclient, instance_uuid, fields) return node.to_dict() def get_ironic_node_list(self, context, fields): + """Get a ironic node list.""" nodes = ironic.get_node_list(self.ironicclient, associated=True, limit=0, fields=fields) return {'nodes': [node.to_dict() for node in nodes]} + + def list_availability_zones(self, context): + """Get availability zone list.""" + azs = set() + for node in self.node_cache: + az = node.properties.get('availability_zone') + if az is not None: + azs.add(az) + + return {'availability_zones': list(azs)} diff --git a/nimble/engine/rpcapi.py b/nimble/engine/rpcapi.py index f797e00e..4e984930 100644 --- a/nimble/engine/rpcapi.py +++ b/nimble/engine/rpcapi.py @@ -86,3 +86,8 @@ class EngineAPI(object): cctxt = self.client.prepare(topic=self.topic, server=CONF.host) return cctxt.call(context, 'get_ironic_node_list', fields=fields) + + def list_availability_zones(self, context): + """Signal to engine service to get availability zone list.""" + cctxt = self.client.prepare(topic=self.topic, server=CONF.host) + return cctxt.call(context, 'list_availability_zones') diff --git a/nimble/tests/functional/api/v1/test_availability_zones.py b/nimble/tests/functional/api/v1/test_availability_zones.py new file mode 100644 index 00000000..bd16b55e --- /dev/null +++ b/nimble/tests/functional/api/v1/test_availability_zones.py @@ -0,0 +1,29 @@ +# Copyright 2016 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 nimble.tests.functional.api import v1 as v1_test + + +class TestAvailabilityZone(v1_test.APITestV1): + + def setUp(self): + super(TestAvailabilityZone, self).setUp() + + @mock.patch('nimble.engine.api.API.list_availability_zones') + def test_availability_zone_get_all(self, list_azs): + list_azs.return_value = {'availability_zones': ['az1', 'az2']} + resp = self.get_json('/availability_zones') + self.assertItemsEqual(['az1', 'az2'], resp['availability_zones'])