Add API endpoint for overcloud Keystone

The logic of querying the overcloud entrypoints are specific to the
TripleO/Tuskar Heat templates. This API endpoint provides a way for the client
to be independent of this logic.

Change-Id: I552d007d4e1bd3a7558d16138ef96a23a25394ea
Implements: blueprint tuskar-api-return-endpoints
This commit is contained in:
Imre Farkas 2013-10-17 14:09:40 +02:00
parent 38e39fba8a
commit 8de738f3d4
8 changed files with 191 additions and 6 deletions

View File

@ -10,6 +10,7 @@ Resources
- `ResourceClass <#resource_class>`_
- `DataCenter <#data_center>`_
- `Node <#node>`_
- `Overcloud <#overcloud>`_
Rack
----
@ -418,4 +419,29 @@ response
}
}
Overcloud
----------
get Keystone URL for an overcloud
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
curl -X GET -H 'Content-Type:application/json' -H 'Accept: application/json' http://0.0.0.0:8585/v1/overclouds/cloudname
response
^^^^^^^^
::
{
"stack_name": "cloudname",
"links": [
{
"rel": "keystone",
"href": "http://192.0.2.5:5000/v2.0/"
}
]
}
`back to top <#index>`_

View File

@ -15,9 +15,10 @@ from tuskar.api.controllers.v1.controller import Controller
from tuskar.api.controllers.v1.data_center import DataCenterController
from tuskar.api.controllers.v1.flavor import FlavorsController
from tuskar.api.controllers.v1.node import NodesController
from tuskar.api.controllers.v1.overcloud import OvercloudsController
from tuskar.api.controllers.v1.rack import RacksController
from tuskar.api.controllers.v1.resource_class import ResourceClassesController
__all__ = (Controller, DataCenterController, FlavorsController,
RacksController, ResourceClassesController,
OvercloudsController, RacksController, ResourceClassesController,
NodesController)

View File

@ -12,6 +12,7 @@ import pecan
from tuskar.api.controllers.v1.data_center import DataCenterController
from tuskar.api.controllers.v1.node import NodesController
from tuskar.api.controllers.v1.overcloud import OvercloudsController
from tuskar.api.controllers.v1.rack import RacksController
from tuskar.api.controllers.v1.resource_class import ResourceClassesController
@ -22,6 +23,7 @@ class Controller(object):
racks = RacksController()
resource_classes = ResourceClassesController()
data_centers = DataCenterController()
overclouds = OvercloudsController()
nodes = NodesController()
@pecan.expose('json')

View File

@ -0,0 +1,66 @@
# 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 wsme import api
from wsmeext import pecan as wsme_pecan
import heatclient.exc
from tuskar.api.controllers.v1.types import Error
from tuskar.api.controllers.v1.types import Link
from tuskar.api.controllers.v1.types import Overcloud
import tuskar.heat.client
from tuskar.openstack.common.gettextutils import _
class OvercloudsController(pecan.rest.RestController):
"""Controller for Overcloud."""
@wsme_pecan.wsexpose(Overcloud, unicode)
def get_one(self, stack_name):
heat = tuskar.heat.client.HeatClient()
try:
stack = heat.get_stack(stack_name)
except heatclient.exc.HTTPNotFound as ex:
response = api.Response(
None,
error=Error(faultcode=ex.code, faultstring=str(ex)),
status_code=ex.code)
return response
if not hasattr(stack, 'outputs'):
faultstring = _('Failed to find Keystone URL.')
response = api.Response(
None,
error=Error(faultcode=404, faultstring=faultstring),
status_code=404)
return response
outputs = stack.outputs
keystone_param = filter(lambda x: x['output_key'] == 'KeystoneURL',
outputs)
if len(keystone_param) == 0:
faultstring = _('Failed to find Keystone URL.')
response = api.Response(
None,
error=Error(faultcode=404, faultstring=faultstring),
status_code=404)
return response
keystone_link = Link(rel='keystone',
href=keystone_param[0]['output_value'])
overcloud = Overcloud(stack_name=stack_name,
links=[keystone_link])
return overcloud

View File

@ -18,9 +18,10 @@ from tuskar.api.controllers.v1.types.error import Error
from tuskar.api.controllers.v1.types.flavor import Flavor
from tuskar.api.controllers.v1.types.link import Link
from tuskar.api.controllers.v1.types.node import Node
from tuskar.api.controllers.v1.types.overcloud import Overcloud
from tuskar.api.controllers.v1.types.rack import Rack
from tuskar.api.controllers.v1.types.relation import Relation
from tuskar.api.controllers.v1.types.resource_class import ResourceClass
__all__ = (Base, Capacity, Chassis, Error, Flavor, Link, Node, Rack,
__all__ = (Base, Capacity, Chassis, Error, Flavor, Link, Node, Overcloud, Rack,
Relation, ResourceClass)

View File

@ -0,0 +1,23 @@
# 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 wsme import types as wtypes
from tuskar.api.controllers.v1.types.base import Base
from tuskar.api.controllers.v1.types.link import Link
class Overcloud(Base):
"""An Overcloud representation."""
stack_name = wtypes.text
links = [Link]

View File

@ -102,10 +102,12 @@ class HeatClient(object):
LOG.exception(e)
return False
def get_stack(self):
def get_stack(self, name=None):
"""Get overcloud Heat template."""
if name is None:
name = CONF.heat['stack_name']
if self.connection:
return self.connection.stacks.get(CONF.heat['stack_name'])
return self.connection.stacks.get(name)
def get_template(self):
"""Get JSON representation of the Heat overcloud template."""
@ -135,9 +137,11 @@ class HeatClient(object):
LOG.exception(e)
return False
def exists_stack(self):
def exists_stack(self, name=None):
if name is None:
name = CONF.heat['stack_name']
try:
self.get_stack()
self.get_stack(name)
return True
#return false if 404
except HeatStackNotFound:

View File

@ -0,0 +1,62 @@
# 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 mox
import heatclient.exc
import heatclient.v1.stacks
import tuskar.heat.client
from tuskar.tests.api import api
class TestOverclouds(api.FunctionalTest):
def test_it_returns_the_overcloud_endpoints(self):
heat_outputs = [
{'output_value': 'http://192.0.2.5:5000/v2.0/',
'description': 'URL for the Overcloud Keystone service',
'output_key': 'KeystoneURL'},
]
heat_stack = mox.MockAnything()
heat_stack.outputs = heat_outputs
self.mox.StubOutWithMock(tuskar.heat.client.HeatClient, 'get_stack')
tuskar.heat.client.HeatClient.get_stack('stack_name').AndReturn(
heat_stack)
self.mox.ReplayAll()
response = self.app.get('/v1/overclouds/stack_name')
self.assertEqual(response.status, '200 OK')
self.assertRegexpMatches(response.body, 'http://192.0.2.5:5000/v2.0/')
def test_it_returns_404_for_nonexisting_overcloud(self):
self.mox.StubOutWithMock(tuskar.heat.client.HeatClient, 'get_stack')
tuskar.heat.client.HeatClient.get_stack(
'stack_name').AndRaise(heatclient.exc.HTTPNotFound())
self.mox.ReplayAll()
response = self.app.get('/v1/overclouds/stack_name',
expect_errors=True)
self.assertEqual(response.status, '404 Not Found')
def test_it_returns_404_during_provisioning(self):
heat_stack = self.mox.CreateMock(heatclient.v1.stacks.Stack)
self.mox.StubOutWithMock(tuskar.heat.client.HeatClient, 'get_stack')
tuskar.heat.client.HeatClient.get_stack('stack_name').AndReturn(
heat_stack)
self.mox.ReplayAll()
response = self.app.get('/v1/overclouds/stack_name',
expect_errors=True)
self.assertEqual(response.status, '404 Not Found')