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>`_
|
- `Flavor <#flavor>`_
|
||||||
- `ResourceClass <#resource_class>`_
|
- `ResourceClass <#resource_class>`_
|
||||||
- `DataCenter <#data_center>`_
|
- `DataCenter <#data_center>`_
|
||||||
|
- `Node <#node>`_
|
||||||
|
|
||||||
Rack
|
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
|
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/
|
curl -XPOST -H 'Content-Type:application/json' -H 'Accept: application/json' http://0.0.0.0:8585/v1/data_centers/
|
||||||
|
|
||||||
`back to top <#index>`_
|
`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.controller import Controller
|
||||||
from tuskar.api.controllers.v1.data_center import DataCenterController
|
from tuskar.api.controllers.v1.data_center import DataCenterController
|
||||||
from tuskar.api.controllers.v1.flavor import FlavorsController
|
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.rack import RacksController
|
||||||
from tuskar.api.controllers.v1.resource_class import ResourceClassesController
|
from tuskar.api.controllers.v1.resource_class import ResourceClassesController
|
||||||
|
|
||||||
__all__ = (Controller, DataCenterController, FlavorsController,
|
__all__ = (Controller, DataCenterController, FlavorsController,
|
||||||
RacksController, ResourceClassesController)
|
RacksController, ResourceClassesController,
|
||||||
|
NodesController)
|
||||||
|
@ -11,6 +11,7 @@ import pecan
|
|||||||
#from tuskar.openstack.common import log
|
#from tuskar.openstack.common import log
|
||||||
|
|
||||||
from tuskar.api.controllers.v1.data_center import DataCenterController
|
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.rack import RacksController
|
||||||
from tuskar.api.controllers.v1.resource_class import ResourceClassesController
|
from tuskar.api.controllers.v1.resource_class import ResourceClassesController
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ class Controller(object):
|
|||||||
racks = RacksController()
|
racks = RacksController()
|
||||||
resource_classes = ResourceClassesController()
|
resource_classes = ResourceClassesController()
|
||||||
data_centers = DataCenterController()
|
data_centers = DataCenterController()
|
||||||
|
nodes = NodesController()
|
||||||
|
|
||||||
@pecan.expose('json')
|
@pecan.expose('json')
|
||||||
def index(self):
|
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
#import wsme
|
import pecan
|
||||||
from wsme import types as wtypes
|
from wsme import types as wtypes
|
||||||
|
|
||||||
from tuskar.api.controllers.v1.types.base import Base
|
from tuskar.api.controllers.v1.types.base import Base
|
||||||
from tuskar.api.controllers.v1.types.link import Link
|
from tuskar.api.controllers.v1.types.link import Link
|
||||||
|
from tuskar.api.controllers.v1.types.relation import Relation
|
||||||
|
|
||||||
|
|
||||||
class Node(Base):
|
class Node(Base):
|
||||||
"""A Node representation."""
|
"""A Node representation."""
|
||||||
|
|
||||||
id = wtypes.text
|
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]
|
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)
|
unit=c.unit)
|
||||||
for c in rack.capacities]
|
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=[
|
links=[
|
||||||
Link.build_ironic_link('node', n.node_id)
|
Link.build('self',
|
||||||
|
pecan.request.host_url,
|
||||||
|
'nodes', n.id)
|
||||||
])
|
])
|
||||||
for n in rack.nodes]
|
for n in rack.nodes]
|
||||||
|
|
||||||
|
@ -507,3 +507,19 @@ class Connection(api.Connection):
|
|||||||
session.rollback()
|
session.rollback()
|
||||||
return False
|
return False
|
||||||
return True
|
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)
|
id = Column(Integer, primary_key=True)
|
||||||
rack_id = Column(Integer, ForeignKey('racks.id'))
|
rack_id = Column(Integer, ForeignKey('racks.id'))
|
||||||
node_id = Column(String(length=64), unique=True)
|
node_id = Column(String(length=64), unique=True)
|
||||||
|
rack = relationship("Rack")
|
||||||
|
|
||||||
|
|
||||||
class Rack(Base):
|
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