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:
parent
38e39fba8a
commit
8de738f3d4
@ -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>`_
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
66
tuskar/api/controllers/v1/overcloud.py
Normal file
66
tuskar/api/controllers/v1/overcloud.py
Normal 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
|
@ -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)
|
||||
|
23
tuskar/api/controllers/v1/types/overcloud.py
Normal file
23
tuskar/api/controllers/v1/types/overcloud.py
Normal 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]
|
@ -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:
|
||||
|
62
tuskar/tests/api/controllers/v1/test_overclouds.py
Normal file
62
tuskar/tests/api/controllers/v1/test_overclouds.py
Normal 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')
|
Loading…
x
Reference in New Issue
Block a user