Expose Nodes as a top-level object
Rack information is also shared when fetching node information Change-Id: I02361a6a89cf3b0bb031732eae893dbd5c0d46ea
This commit is contained in:
parent
ba1fd47cc9
commit
ee8443c885
@ -9,6 +9,7 @@ Resources
|
||||
- `Flavor <#flavor>`_
|
||||
- `ResourceClass <#resource_class>`_
|
||||
- `DataCenter <#data_center>`_
|
||||
- `Node <#node>`_
|
||||
|
||||
Rack
|
||||
----
|
||||
@ -325,9 +326,9 @@ delete
|
||||
|
||||
::
|
||||
|
||||
curl -X DELETE http://0.0.0.0:8585/v1/resource_classes/1`` `back to
|
||||
curl -X DELETE http://0.0.0.0:8585/v1/resource_classes/1
|
||||
|
||||
top <#index>`_
|
||||
`back to top <#index>`_
|
||||
|
||||
DataCenter
|
||||
----------
|
||||
@ -343,3 +344,78 @@ Tuskar.
|
||||
curl -XPOST -H 'Content-Type:application/json' -H 'Accept: application/json' http://0.0.0.0:8585/v1/data_centers/
|
||||
|
||||
`back to top <#index>`_
|
||||
|
||||
Node
|
||||
----
|
||||
|
||||
Get Collection
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
curl http://0.0.0.0:8585/v1/nodes/
|
||||
|
||||
response
|
||||
^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
[
|
||||
{
|
||||
"nova_baremetal_node_id": "0e3ab3d3-bd85-40bd-b6a1-fae484040825",
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://127.0.0.1:8585/v1/nodes/1",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"rack": {
|
||||
"id": 1,
|
||||
"links":
|
||||
[
|
||||
{
|
||||
"href": "http://127.0.0.1:8585/v1/racks/1",
|
||||
"rel": "self"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Retrieve a single Node
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
curl http://0.0.0.0:8585/v1/nodes/1
|
||||
|
||||
response
|
||||
^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"nova_baremetal_node_id": "0e3ab3d3-bd85-40bd-b6a1-fae484040825",
|
||||
"id": "1",
|
||||
"links":
|
||||
[
|
||||
{
|
||||
"href": "http://127.0.0.1:8585/v1/nodes/1",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"rack":
|
||||
{
|
||||
"id": 1,
|
||||
"links":
|
||||
[
|
||||
{
|
||||
"href": "http://127.0.0.1:8585/v1/racks/1",
|
||||
"rel": "self"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
`back to top <#index>`_
|
||||
|
@ -14,8 +14,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.rack import RacksController
|
||||
from tuskar.api.controllers.v1.resource_class import ResourceClassesController
|
||||
|
||||
__all__ = (Controller, DataCenterController, FlavorsController,
|
||||
RacksController, ResourceClassesController)
|
||||
RacksController, ResourceClassesController,
|
||||
NodesController)
|
||||
|
@ -11,6 +11,7 @@ import pecan
|
||||
#from tuskar.openstack.common import log
|
||||
|
||||
from tuskar.api.controllers.v1.data_center import DataCenterController
|
||||
from tuskar.api.controllers.v1.node import NodesController
|
||||
from tuskar.api.controllers.v1.rack import RacksController
|
||||
from tuskar.api.controllers.v1.resource_class import ResourceClassesController
|
||||
|
||||
@ -21,6 +22,7 @@ class Controller(object):
|
||||
racks = RacksController()
|
||||
resource_classes = ResourceClassesController()
|
||||
data_centers = DataCenterController()
|
||||
nodes = NodesController()
|
||||
|
||||
@pecan.expose('json')
|
||||
def index(self):
|
||||
|
42
tuskar/api/controllers/v1/node.py
Normal file
42
tuskar/api/controllers/v1/node.py
Normal file
@ -0,0 +1,42 @@
|
||||
import pecan
|
||||
from pecan import rest
|
||||
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from tuskar.api.controllers.v1.types import Error
|
||||
from tuskar.api.controllers.v1.types import Node
|
||||
from tuskar.common import exception
|
||||
from tuskar.openstack.common import log
|
||||
from wsme import api
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class NodesController(rest.RestController):
|
||||
"""REST controller for Node."""
|
||||
|
||||
@wsme_pecan.wsexpose([Node])
|
||||
def get_all(self):
|
||||
"""Retrieve a list of all nodes."""
|
||||
result = []
|
||||
db_api = pecan.request.dbapi
|
||||
|
||||
for node in db_api.get_nodes(None):
|
||||
result.append(Node.convert(node))
|
||||
|
||||
return result
|
||||
|
||||
@wsme_pecan.wsexpose(Node, unicode)
|
||||
def get_one(self, node_id):
|
||||
"""Retrieve an instance of a Node."""
|
||||
db_api = pecan.request.dbapi
|
||||
|
||||
try:
|
||||
node = db_api.get_node(node_id)
|
||||
except exception.TuskarException, e:
|
||||
response = api.Response(None,
|
||||
error=Error(faultcode=e.code,
|
||||
faultstring=str(e)),
|
||||
status_code=e.code)
|
||||
return response
|
||||
return Node.convert(node)
|
@ -12,15 +12,32 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
#import wsme
|
||||
import pecan
|
||||
from wsme import types as wtypes
|
||||
|
||||
from tuskar.api.controllers.v1.types.base import Base
|
||||
from tuskar.api.controllers.v1.types.link import Link
|
||||
from tuskar.api.controllers.v1.types.relation import Relation
|
||||
|
||||
|
||||
class Node(Base):
|
||||
"""A Node representation."""
|
||||
|
||||
id = wtypes.text
|
||||
# FIXME: We expose this as nova_baremetal_node_id, but are not yet changing
|
||||
# the column name in the database, because this is a more involved change.
|
||||
nova_baremetal_node_id = wtypes.text
|
||||
rack = Relation
|
||||
links = [Link]
|
||||
|
||||
@classmethod
|
||||
def convert(self, node):
|
||||
kwargs = node.as_dict()
|
||||
links = [Link.build('self', pecan.request.host_url, 'nodes',
|
||||
node.id)]
|
||||
rack_link = [Link.build('self', pecan.request.host_url,
|
||||
'racks', node.rack_id)]
|
||||
kwargs['rack'] = Relation(id=node.rack_id, links=rack_link)
|
||||
kwargs['id'] = str(node.id)
|
||||
kwargs['nova_baremetal_node_id'] = str(node.node_id)
|
||||
return Node(links=links, **kwargs)
|
||||
|
@ -60,9 +60,12 @@ class Rack(Base):
|
||||
unit=c.unit)
|
||||
for c in rack.capacities]
|
||||
|
||||
kwargs['nodes'] = [Node(id=n.node_id,
|
||||
kwargs['nodes'] = [Node(id=str(n.id),
|
||||
node_id=n.node_id,
|
||||
links=[
|
||||
Link.build_ironic_link('node', n.node_id)
|
||||
Link.build('self',
|
||||
pecan.request.host_url,
|
||||
'nodes', n.id)
|
||||
])
|
||||
for n in rack.nodes]
|
||||
|
||||
|
@ -507,3 +507,19 @@ class Connection(api.Connection):
|
||||
session.rollback()
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_nodes(self, columns):
|
||||
session = get_session()
|
||||
result = session.query(models.Node).options(
|
||||
joinedload('rack')).all()
|
||||
session.close()
|
||||
return result
|
||||
|
||||
def get_node(self, node_id):
|
||||
session = get_session()
|
||||
try:
|
||||
result = session.query(models.Node).options(
|
||||
joinedload('rack')).filter_by(id=node_id).one()
|
||||
except NoResultFound:
|
||||
raise exception.NodeNotFound(node=node_id)
|
||||
return result
|
||||
|
@ -123,6 +123,7 @@ class Node(Base):
|
||||
id = Column(Integer, primary_key=True)
|
||||
rack_id = Column(Integer, ForeignKey('racks.id'))
|
||||
node_id = Column(String(length=64), unique=True)
|
||||
rack = relationship("Rack")
|
||||
|
||||
|
||||
class Rack(Base):
|
||||
|
62
tuskar/tests/api/controllers/v1/test_nodes.py
Normal file
62
tuskar/tests/api/controllers/v1/test_nodes.py
Normal file
@ -0,0 +1,62 @@
|
||||
from tuskar.api.controllers.v1.types import Capacity
|
||||
from tuskar.api.controllers.v1.types import Chassis
|
||||
from tuskar.api.controllers.v1.types import Node
|
||||
from tuskar.api.controllers.v1.types import Rack
|
||||
from tuskar.db.sqlalchemy import api as dbapi
|
||||
from tuskar.tests.api import api
|
||||
|
||||
|
||||
class TestNodes(api.FunctionalTest):
|
||||
|
||||
test_node = None
|
||||
db = dbapi.get_backend()
|
||||
|
||||
def setUp(self):
|
||||
"""Create 'test_rack'."""
|
||||
|
||||
super(TestNodes, self).setUp()
|
||||
# self.test_resource_class = None
|
||||
self.test_node = Node(id='1', name='test_node', node_id='1')
|
||||
self.test_rack = self.db.create_rack(
|
||||
Rack(name='test-rack',
|
||||
slots=1,
|
||||
subnet='10.0.0.0/24',
|
||||
location='nevada',
|
||||
chassis=Chassis(id='123'),
|
||||
capacities=[Capacity(name='cpu', value='10',
|
||||
unit='count')],
|
||||
nodes=[self.test_node]
|
||||
))
|
||||
|
||||
def tearDown(self):
|
||||
self.db.delete_rack(self.test_rack.id)
|
||||
super(TestNodes, self).tearDown()
|
||||
|
||||
def valid_node_json(self, node_json, test_node=None):
|
||||
node = self.test_node if test_node is None else test_node
|
||||
|
||||
self.assertEqual(node_json['nova_baremetal_node_id'], node.node_id)
|
||||
self.assertEqual(node_json['id'], node.id)
|
||||
|
||||
def test_it_returns_single_node(self):
|
||||
response = self.get_json('/nodes/' + str(self.test_node.id),
|
||||
expect_errors=True)
|
||||
|
||||
self.assertEqual(response.status_int, 200)
|
||||
self.assertEqual(response.content_type, 'application/json')
|
||||
self.valid_node_json(response.json)
|
||||
|
||||
# it should contain a rack
|
||||
rack = response.json['rack']
|
||||
self.assertEqual(rack['id'], self.test_rack.id)
|
||||
|
||||
def test_it_returns_node_list(self):
|
||||
response = self.get_json('/nodes/', expect_errors=True)
|
||||
self.assertEqual(response.status_int, 200)
|
||||
self.assertEqual(response.content_type, 'application/json')
|
||||
|
||||
# It should consist solely of one node:
|
||||
self.assertEqual(len(response.json), 1)
|
||||
|
||||
# And that node should pass our JSON test:
|
||||
self.valid_node_json(response.json[0])
|
Loading…
Reference in New Issue
Block a user