Merge "Implement Node Manage"
This commit is contained in:
commit
3a1b4e6d7b
@ -35,6 +35,12 @@ pod_uuid:
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
node_index:
|
||||
description: |
|
||||
The redfish index of composed node.
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
system_ident:
|
||||
description: |
|
||||
The UUID or name of Compute System.
|
||||
|
@ -53,6 +53,7 @@ The list and example below are representative of the response as of API
|
||||
|
||||
- uuid: node_uuid
|
||||
- name: node_name
|
||||
- index: node_index
|
||||
- links: links
|
||||
|
||||
**Example JSON representation of a Node:**
|
||||
@ -85,6 +86,7 @@ Response
|
||||
|
||||
- uuid: node_uuid
|
||||
- name: node_name
|
||||
- index: node_index
|
||||
- node_power_state: node_power_state
|
||||
- links: links
|
||||
|
||||
@ -303,8 +305,8 @@ Normal response codes: 200
|
||||
|
||||
Error response codes: badRequest(400), unauthorized(401), forbidden(403)
|
||||
|
||||
Requet
|
||||
------
|
||||
Request
|
||||
-------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
@ -326,3 +328,33 @@ Response
|
||||
.. literalinclude:: mockup/composed-node-get-asset.json
|
||||
:language: javascript
|
||||
|
||||
|
||||
Manage Node
|
||||
===========
|
||||
|
||||
.. rest_method:: POST /v1/nodes/managed
|
||||
|
||||
Manage a composed node already existing in the RSD rack by creating a
|
||||
Valence database entry for it, allowing Valence to perform all operations
|
||||
on it.
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), conflict(409)
|
||||
|
||||
Request
|
||||
-------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- node_index: node_index
|
||||
|
||||
Response
|
||||
--------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- uuid: node_uuid
|
||||
- name: node_name
|
||||
- index: node_index
|
||||
- links: links
|
||||
|
@ -78,6 +78,8 @@ api.add_resource(v1_nodes.Node,
|
||||
api.add_resource(v1_nodes.NodeAction,
|
||||
'/v1/nodes/<string:node_uuid>/action',
|
||||
endpoint='node_action')
|
||||
api.add_resource(v1_nodes.NodeManage, '/v1/nodes/manage',
|
||||
endpoint='node_manage')
|
||||
api.add_resource(v1_nodes.NodesStorage,
|
||||
'/v1/nodes/<string:nodeid>/storages',
|
||||
endpoint='nodes_storages')
|
||||
|
@ -52,6 +52,13 @@ class NodeAction(Resource):
|
||||
nodes.Node.node_action(node_uuid, request.get_json()))
|
||||
|
||||
|
||||
class NodeManage(Resource):
|
||||
|
||||
def post(self):
|
||||
return utils.make_response(
|
||||
http_client.OK, nodes.Node.manage_node(request.get_json()))
|
||||
|
||||
|
||||
class NodesStorage(Resource):
|
||||
|
||||
def get(self, nodeid):
|
||||
|
@ -86,6 +86,15 @@ class RedfishException(ValenceError):
|
||||
self.detail = message_detail
|
||||
|
||||
|
||||
class ResourceExists(ValenceError):
|
||||
def __init__(self, detail='resource already exists', request_id=None):
|
||||
self.request_id = request_id
|
||||
self.status_code = http_client.METHOD_NOT_ALLOWED
|
||||
self.code = http_client.METHOD_NOT_ALLOWED
|
||||
self.title = "Resource already exists"
|
||||
self.detail = detail
|
||||
|
||||
|
||||
class NotFound(ValenceError):
|
||||
|
||||
def __init__(self, detail='resource not found',
|
||||
|
@ -12,20 +12,25 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
import six
|
||||
|
||||
from valence.common import exception
|
||||
from valence.common import utils
|
||||
from valence.controller import flavors
|
||||
from valence.db import api as db_api
|
||||
from valence.redfish import redfish
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Node(object):
|
||||
|
||||
@staticmethod
|
||||
def _show_node_brief_info(node_info):
|
||||
return {key: node_info[key] for key in six.iterkeys(node_info)
|
||||
if key in ["uuid", "name", "links"]}
|
||||
if key in ["uuid", "name", "index", "links"]}
|
||||
|
||||
@staticmethod
|
||||
def _create_compose_request(name, description, requirements):
|
||||
@ -95,6 +100,40 @@ class Node(object):
|
||||
|
||||
return cls._show_node_brief_info(composed_node)
|
||||
|
||||
@classmethod
|
||||
def manage_node(cls, request_body):
|
||||
"""Manage existing RSD node.
|
||||
|
||||
param request_body: Parameters for node to manage.
|
||||
|
||||
Required JSON body:
|
||||
|
||||
{
|
||||
'node_index': <Redfish index of node to manage>
|
||||
}
|
||||
|
||||
return: Info on managed node.
|
||||
"""
|
||||
|
||||
composed_node = redfish.get_node_by_id(request_body["node_index"])
|
||||
# Check to see that the node to manage doesn't already exist in the
|
||||
# Valence database.
|
||||
current_nodes = cls.list_composed_nodes()
|
||||
for node in current_nodes:
|
||||
if node['index'] == composed_node['index']:
|
||||
raise exception.ResourceExists(
|
||||
detail="Node already managed by Valence.")
|
||||
|
||||
composed_node["uuid"] = utils.generate_uuid()
|
||||
|
||||
node_db = {"uuid": composed_node["uuid"],
|
||||
"name": composed_node["name"],
|
||||
"index": composed_node["index"],
|
||||
"links": composed_node["links"]}
|
||||
db_api.Connection.create_composed_node(node_db)
|
||||
|
||||
return cls._show_node_brief_info(composed_node)
|
||||
|
||||
@classmethod
|
||||
def get_composed_node_by_uuid(cls, node_uuid):
|
||||
"""Get composed node details
|
||||
|
@ -16,18 +16,20 @@ import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from valence.common import exception
|
||||
from valence.controller import nodes
|
||||
from valence.tests.unit.controller import fakes
|
||||
from valence.tests.unit.db import utils as test_utils
|
||||
from valence.tests.unit.fakes import flavor_fakes
|
||||
from valence.tests.unit.fakes import node_fakes
|
||||
|
||||
|
||||
class TestAPINodes(unittest.TestCase):
|
||||
|
||||
def test_show_node_brief_info(self):
|
||||
"""Test only show node brief info"""
|
||||
node_info = fakes.get_test_composed_node()
|
||||
node_info = node_fakes.get_test_composed_node()
|
||||
expected = {
|
||||
"index": "1",
|
||||
"name": "fake_name",
|
||||
"uuid": "ea8e2a25-2901-438d-8157-de7ffd68d051",
|
||||
"links": [{'href': 'http://127.0.0.1:8181/v1/nodes/'
|
||||
@ -71,13 +73,53 @@ class TestAPINodes(unittest.TestCase):
|
||||
requirements)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch("valence.db.api.Connection.create_composed_node")
|
||||
@mock.patch("valence.common.utils.generate_uuid")
|
||||
@mock.patch("valence.controller.nodes.Node.list_composed_nodes")
|
||||
@mock.patch("valence.redfish.redfish.get_node_by_id")
|
||||
def test_manage_node(self, mock_get_node, mock_list_nodes,
|
||||
mock_generate_uuid, mock_db_create_composed_node):
|
||||
manage_node = node_fakes.get_test_composed_node()
|
||||
mock_get_node.return_value = manage_node
|
||||
node_list = node_fakes.get_test_node_list()
|
||||
# Change the index of node 1 so that the node to manage
|
||||
# doesn't appear in the list of nodes already managed by Valence.
|
||||
node_list[0]["index"] = '4'
|
||||
mock_list_nodes.return_value = node_list
|
||||
|
||||
uuid = "ea8e2a25-2901-438d-8157-de7ffd68d051"
|
||||
mock_generate_uuid.return_value = uuid
|
||||
|
||||
node_db = {"uuid": manage_node["uuid"],
|
||||
"index": manage_node["index"],
|
||||
"name": manage_node["name"],
|
||||
"links": manage_node["links"]}
|
||||
|
||||
nodes.Node.manage_node({"node_index": "1"})
|
||||
mock_db_create_composed_node.assert_called_once_with(node_db)
|
||||
|
||||
@mock.patch("valence.controller.nodes.Node.list_composed_nodes")
|
||||
@mock.patch("valence.redfish.redfish.get_node_by_id")
|
||||
def test_manage_already_managed_node(self, mock_get_node, mock_list_nodes):
|
||||
manage_node = node_fakes.get_test_composed_node()
|
||||
mock_get_node.return_value = manage_node
|
||||
# Leave the index of node 1 as '1' so that it conflicts with the node
|
||||
# being managed, meaning we're trying to manage a node that already
|
||||
# exists in the Valence DB.
|
||||
node_list = node_fakes.get_test_node_list()
|
||||
mock_list_nodes.return_value = node_list
|
||||
|
||||
self.assertRaises(exception.ResourceExists,
|
||||
nodes.Node.manage_node,
|
||||
{"node_index": "1"})
|
||||
|
||||
@mock.patch("valence.db.api.Connection.create_composed_node")
|
||||
@mock.patch("valence.common.utils.generate_uuid")
|
||||
@mock.patch("valence.redfish.redfish.compose_node")
|
||||
def test_compose_node(self, mock_redfish_compose_node, mock_generate_uuid,
|
||||
mock_db_create_composed_node):
|
||||
"""Test compose node successfully"""
|
||||
node_hw = fakes.get_test_composed_node()
|
||||
node_hw = node_fakes.get_test_composed_node()
|
||||
node_db = {"uuid": node_hw["uuid"],
|
||||
"index": node_hw["index"],
|
||||
"name": node_hw["name"],
|
||||
@ -104,7 +146,7 @@ class TestAPINodes(unittest.TestCase):
|
||||
mock_generate_uuid,
|
||||
mock_db_create_composed_node):
|
||||
"""Test node composition using a flavor for requirements"""
|
||||
node_hw = fakes.get_test_composed_node()
|
||||
node_hw = node_fakes.get_test_composed_node()
|
||||
node_db = {"uuid": node_hw["uuid"],
|
||||
"index": node_hw["index"],
|
||||
"name": node_hw["name"],
|
||||
@ -132,7 +174,7 @@ class TestAPINodes(unittest.TestCase):
|
||||
def test_get_composed_node_by_uuid(
|
||||
self, mock_db_get_composed_node, mock_redfish_get_node):
|
||||
"""Test get composed node detail"""
|
||||
node_hw = fakes.get_test_composed_node()
|
||||
node_hw = node_fakes.get_test_composed_node()
|
||||
node_db = test_utils.get_test_composed_node_db_info()
|
||||
|
||||
mock_db_model = mock.MagicMock()
|
||||
|
@ -1,5 +1,4 @@
|
||||
# copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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
|
||||
#
|
||||
@ -50,3 +49,23 @@ def get_test_composed_node(**kwargs):
|
||||
'speed_mhz': 3700,
|
||||
'total_core': 2}]})
|
||||
}
|
||||
|
||||
|
||||
def get_test_node_list(**kwargs):
|
||||
return [
|
||||
{
|
||||
'uuid': kwargs.get('uuid', '11111111-1111-1111-1111-111111111111'),
|
||||
'name': kwargs.get('name', 'node_1'),
|
||||
'index': kwargs.get('index', '1')
|
||||
},
|
||||
{
|
||||
'uuid': kwargs.get('uuid', '22222222-2222-2222-2222-222222222222'),
|
||||
'name': kwargs.get('name', 'node_2'),
|
||||
'index': kwargs.get('index', '2')
|
||||
},
|
||||
{
|
||||
'uuid': kwargs.get('uuid', '33333333-3333-3333-3333-333333333333'),
|
||||
'name': kwargs.get('name', 'node_3'),
|
||||
'index': kwargs.get('index', '3')
|
||||
}
|
||||
]
|
Loading…
x
Reference in New Issue
Block a user