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
This commit is contained in:
Zhenguo Niu 2016-11-24 17:40:04 +08:00
parent 80983ab56b
commit 44ac91540f
10 changed files with 153 additions and 4 deletions

View File

@ -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

View File

@ -9,3 +9,4 @@
.. include:: urls.inc
.. include:: instances.inc
.. include:: types.inc
.. include:: availability_zones.inc

View File

@ -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

View File

@ -0,0 +1,7 @@
{
"availability_zones": [
"az1",
"az2",
"az3"
]
}

View File

@ -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',)

View File

@ -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

View File

@ -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)

View File

@ -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)}

View File

@ -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')

View File

@ -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'])