Implement Node Manage
This commit implements the ability for an admin to manage existing RSD composed nodes with Valence. In the event that an RSD pod manager contains composed nodes that were created without Valence, it's possible to use this function to add those nodes to the Valence database. Change-Id: I13e8076fb718ebd356cb7b4839cfb4b83c798672 Implements-blueprint: manage-node
This commit is contained in:
parent
36aabb9011
commit
5ce2d68ed7
@ -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.
|
||||
|
@ -48,6 +48,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:**
|
||||
@ -80,6 +81,7 @@ Response
|
||||
|
||||
- uuid: node_uuid
|
||||
- name: node_name
|
||||
- index: node_index
|
||||
- node_power_state: node_power_state
|
||||
- links: links
|
||||
|
||||
@ -298,8 +300,8 @@ Normal response codes: 200
|
||||
|
||||
Error response codes: badRequest(400), unauthorized(401), forbidden(403)
|
||||
|
||||
Requet
|
||||
------
|
||||
Request
|
||||
-------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
@ -321,3 +323,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
|
||||
|
@ -73,6 +73,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