Update Node controller to make it generic
This commit updates node controller code to use generic manager interface instead of redfish directly Partially-Implements blueprint add-vendor-extensible-framework Change-Id: Ic551167798306a1ed6b5c609117e6d19e6de6574
This commit is contained in:
parent
570086d34a
commit
1afb9f0cbf
@ -30,8 +30,12 @@ class Nodes(Resource):
|
|||||||
|
|
||||||
@validator.check_input('compose_node_schema')
|
@validator.check_input('compose_node_schema')
|
||||||
def post(self):
|
def post(self):
|
||||||
|
# TODO(): podm_id should be passed in request body, if not passed
|
||||||
|
# scheduler will decide on podm_id
|
||||||
|
req = request.get_json()
|
||||||
return utils.make_response(
|
return utils.make_response(
|
||||||
http_client.OK, nodes.Node.compose_node(request.get_json()))
|
http_client.OK,
|
||||||
|
nodes.Node(podm_id=req['podm_id']).compose_node(req))
|
||||||
|
|
||||||
|
|
||||||
class Node(Resource):
|
class Node(Resource):
|
||||||
@ -39,11 +43,12 @@ class Node(Resource):
|
|||||||
def get(self, node_uuid):
|
def get(self, node_uuid):
|
||||||
return utils.make_response(
|
return utils.make_response(
|
||||||
http_client.OK,
|
http_client.OK,
|
||||||
nodes.Node.get_composed_node_by_uuid(node_uuid))
|
nodes.Node(node_id=node_uuid).get_composed_node_by_uuid(node_uuid))
|
||||||
|
|
||||||
def delete(self, node_uuid):
|
def delete(self, node_uuid):
|
||||||
return utils.make_response(
|
return utils.make_response(
|
||||||
http_client.OK, nodes.Node.delete_composed_node(node_uuid))
|
http_client.OK,
|
||||||
|
nodes.Node(node_id=node_uuid).delete_composed_node(node_uuid))
|
||||||
|
|
||||||
|
|
||||||
class NodeAction(Resource):
|
class NodeAction(Resource):
|
||||||
@ -52,15 +57,18 @@ class NodeAction(Resource):
|
|||||||
def post(self, node_uuid):
|
def post(self, node_uuid):
|
||||||
return utils.make_response(
|
return utils.make_response(
|
||||||
http_client.NO_CONTENT,
|
http_client.NO_CONTENT,
|
||||||
nodes.Node.node_action(node_uuid, request.get_json()))
|
nodes.Node(node_id=node_uuid).node_action(node_uuid,
|
||||||
|
request.get_json()))
|
||||||
|
|
||||||
|
|
||||||
class NodeManage(Resource):
|
class NodeManage(Resource):
|
||||||
|
|
||||||
@validator.check_input('node_manage_schema')
|
@validator.check_input('node_manage_schema')
|
||||||
def post(self):
|
def post(self):
|
||||||
|
req = request.get_json()
|
||||||
return utils.make_response(
|
return utils.make_response(
|
||||||
http_client.OK, nodes.Node.manage_node(request.get_json()))
|
http_client.OK,
|
||||||
|
nodes.Node(podm_id=req['podm_id']).manage_node(req))
|
||||||
|
|
||||||
|
|
||||||
class NodesStorage(Resource):
|
class NodesStorage(Resource):
|
||||||
|
@ -18,18 +18,36 @@ from valence.common import exception
|
|||||||
from valence.common import utils
|
from valence.common import utils
|
||||||
from valence.controller import flavors
|
from valence.controller import flavors
|
||||||
from valence.db import api as db_api
|
from valence.db import api as db_api
|
||||||
|
from valence.podmanagers import manager
|
||||||
from valence.provision import driver
|
from valence.provision import driver
|
||||||
from valence.redfish import redfish
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Node(object):
|
class Node(object):
|
||||||
|
|
||||||
|
def __init__(self, node_id=None, podm_id=None):
|
||||||
|
"""Create node object
|
||||||
|
|
||||||
|
node uuid or podmanager uuid is required to create the
|
||||||
|
podmanager connection object.
|
||||||
|
|
||||||
|
:param node_id Node uuid
|
||||||
|
:param podm_id podmanager id
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.podm_id = podm_id
|
||||||
|
if node_id:
|
||||||
|
self.node = db_api.Connection.get_composed_node_by_uuid(node_id).\
|
||||||
|
as_dict()
|
||||||
|
self.podm_id = self.node['podm_id']
|
||||||
|
|
||||||
|
self.connection = manager.get_connection(self.podm_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _show_node_brief_info(node_info):
|
def _show_node_brief_info(node_info):
|
||||||
return {key: node_info[key] for key in node_info.keys()
|
return {key: node_info[key] for key in node_info.keys()
|
||||||
if key in ["uuid", "name", "index", "uri"]}
|
if key in ["uuid", "name", "podm_id", "index", "resource_uri"]}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _create_compose_request(name, description, requirements):
|
def _create_compose_request(name, description, requirements):
|
||||||
@ -57,8 +75,7 @@ class Node(object):
|
|||||||
|
|
||||||
return request
|
return request
|
||||||
|
|
||||||
@classmethod
|
def compose_node(self, request_body):
|
||||||
def compose_node(cls, request_body):
|
|
||||||
"""Compose new node
|
"""Compose new node
|
||||||
|
|
||||||
param request_body: parameter for node composition
|
param request_body: parameter for node composition
|
||||||
@ -80,28 +97,25 @@ class Node(object):
|
|||||||
# "description" is optional
|
# "description" is optional
|
||||||
description = request_body.get("description", "")
|
description = request_body.get("description", "")
|
||||||
|
|
||||||
compose_request = cls._create_compose_request(name,
|
compose_request = self._create_compose_request(name, description,
|
||||||
description,
|
requirements)
|
||||||
requirements)
|
|
||||||
|
|
||||||
# Call redfish to compose new node
|
|
||||||
composed_node = redfish.compose_node(compose_request)
|
|
||||||
|
|
||||||
|
composed_node = self.connection.compose_node(compose_request)
|
||||||
composed_node["uuid"] = utils.generate_uuid()
|
composed_node["uuid"] = utils.generate_uuid()
|
||||||
|
|
||||||
# Only store the minimum set of composed node info into backend db,
|
# Only store the minimum set of composed node info into backend db,
|
||||||
# since other fields like power status may be changed and valence is
|
# since other fields like power status may be changed and valence is
|
||||||
# not aware.
|
# not aware.
|
||||||
node_db = {"uuid": composed_node["uuid"],
|
node_db = {"uuid": composed_node["uuid"],
|
||||||
|
"podm_id": self.podm_id,
|
||||||
"name": composed_node["name"],
|
"name": composed_node["name"],
|
||||||
"index": composed_node["index"],
|
"index": composed_node["index"],
|
||||||
"uri": composed_node["resource_uri"]}
|
"resource_uri": composed_node["resource_uri"]}
|
||||||
db_api.Connection.create_composed_node(node_db)
|
db_api.Connection.create_composed_node(node_db)
|
||||||
|
|
||||||
return cls._show_node_brief_info(composed_node)
|
return self._show_node_brief_info(composed_node)
|
||||||
|
|
||||||
@classmethod
|
def manage_node(self, request_body):
|
||||||
def manage_node(cls, request_body):
|
|
||||||
"""Manage existing RSD node.
|
"""Manage existing RSD node.
|
||||||
|
|
||||||
param request_body: Parameters for node to manage.
|
param request_body: Parameters for node to manage.
|
||||||
@ -110,32 +124,34 @@ class Node(object):
|
|||||||
|
|
||||||
{
|
{
|
||||||
'node_index': <Redfish index of node to manage>
|
'node_index': <Redfish index of node to manage>
|
||||||
|
'podm_id': <podmanager id with which node is managed>
|
||||||
}
|
}
|
||||||
|
|
||||||
return: Info on managed node.
|
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
|
# Check to see that the node to manage doesn't already exist in the
|
||||||
# Valence database.
|
# Valence database.
|
||||||
|
node_index = request_body["node_index"]
|
||||||
error_msg = ("Node '%s' already managed by Valence")
|
error_msg = ("Node '%s' already managed by Valence")
|
||||||
current_nodes = cls.list_composed_nodes()
|
current_nodes = self.list_composed_nodes()
|
||||||
for node in current_nodes:
|
for node in current_nodes:
|
||||||
if node['index'] == composed_node['index']:
|
if node['index'] == node_index:
|
||||||
raise exception.ResourceExists(
|
raise exception.ResourceExists(error_msg % node_index)
|
||||||
error_msg % node['index'])
|
|
||||||
|
|
||||||
|
# Get podm connection with which node should be managed
|
||||||
|
composed_node = self.connection.get_node_info(node_index)
|
||||||
composed_node["uuid"] = utils.generate_uuid()
|
composed_node["uuid"] = utils.generate_uuid()
|
||||||
|
|
||||||
node_db = {"uuid": composed_node["uuid"],
|
node_db = {"uuid": composed_node["uuid"],
|
||||||
"name": composed_node["name"],
|
"name": composed_node["name"],
|
||||||
|
"podm_id": self.podm_id,
|
||||||
"index": composed_node["index"],
|
"index": composed_node["index"],
|
||||||
"uri": composed_node["resource_uri"]}
|
"resource_uri": composed_node["resource_uri"]}
|
||||||
db_api.Connection.create_composed_node(node_db)
|
db_api.Connection.create_composed_node(node_db)
|
||||||
|
|
||||||
return cls._show_node_brief_info(composed_node)
|
return self._show_node_brief_info(composed_node)
|
||||||
|
|
||||||
@classmethod
|
def get_composed_node_by_uuid(self, node_uuid):
|
||||||
def get_composed_node_by_uuid(cls, node_uuid):
|
|
||||||
"""Get composed node details
|
"""Get composed node details
|
||||||
|
|
||||||
Get the detail of specific composed node. In some cases db data may be
|
Get the detail of specific composed node. In some cases db data may be
|
||||||
@ -147,28 +163,20 @@ class Node(object):
|
|||||||
return: detail of this composed node
|
return: detail of this composed node
|
||||||
"""
|
"""
|
||||||
|
|
||||||
node_db = db_api.Connection.get_composed_node_by_uuid(node_uuid)\
|
# Get podm connection to retrieve details
|
||||||
.as_dict()
|
node_hw = self.connection.get_node_info(self.node['index'])
|
||||||
node_hw = redfish.get_node_by_id(node_db["index"])
|
|
||||||
|
|
||||||
# Add those fields of composed node from db
|
# Add those fields of composed node from db
|
||||||
node_hw.update(node_db)
|
node_hw.update(self.node)
|
||||||
|
|
||||||
return node_hw
|
return node_hw
|
||||||
|
|
||||||
@classmethod
|
def delete_composed_node(self, node_uuid):
|
||||||
def delete_composed_node(cls, node_uuid):
|
|
||||||
"""Delete a composed node
|
"""Delete a composed node
|
||||||
|
|
||||||
param node_uuid: uuid of composed node
|
param node_uuid: uuid of composed node
|
||||||
return: message of this deletion
|
return: message of this deletion
|
||||||
"""
|
"""
|
||||||
|
# Call podmanager to delete node, and delete corresponding entry in db
|
||||||
# Get node detail from db, and map node uuid to index
|
message = self.connection.delete_composed_node(self.node['index'])
|
||||||
index = db_api.Connection.get_composed_node_by_uuid(node_uuid).index
|
|
||||||
|
|
||||||
# Call redfish to delete node, and delete corresponding entry in db
|
|
||||||
message = redfish.delete_composed_node(index)
|
|
||||||
db_api.Connection.delete_composed_node(node_uuid)
|
db_api.Connection.delete_composed_node(node_uuid)
|
||||||
|
|
||||||
return message
|
return message
|
||||||
@ -182,17 +190,15 @@ class Node(object):
|
|||||||
return [cls._show_node_brief_info(node_info.as_dict())
|
return [cls._show_node_brief_info(node_info.as_dict())
|
||||||
for node_info in db_api.Connection.list_composed_nodes()]
|
for node_info in db_api.Connection.list_composed_nodes()]
|
||||||
|
|
||||||
@classmethod
|
def node_action(self, node_uuid, request_body):
|
||||||
def node_action(cls, node_uuid, request_body):
|
|
||||||
"""Post action to a composed node
|
"""Post action to a composed node
|
||||||
|
|
||||||
param node_uuid: uuid of composed node
|
param node_uuid: uuid of composed node
|
||||||
param request_body: parameter of node action
|
param request_body: parameter of node action
|
||||||
return: message of this deletion
|
return: message of this deletion
|
||||||
"""
|
"""
|
||||||
# Get node detail from db, and map node uuid to index
|
node_index = self.node['index']
|
||||||
index = db_api.Connection.get_composed_node_by_uuid(node_uuid).index
|
return self.connection.node_action(node_index, request_body)
|
||||||
return redfish.node_action(index, request_body)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def node_register(cls, node_uuid, request_body):
|
def node_register(cls, node_uuid, request_body):
|
||||||
|
@ -17,7 +17,7 @@ podm_connections = {}
|
|||||||
podm_modules = {}
|
podm_modules = {}
|
||||||
|
|
||||||
|
|
||||||
def get_podm_connection(podm_id):
|
def get_connection(podm_id):
|
||||||
podm_connection = podm_connections.get(podm_id, None)
|
podm_connection = podm_connections.get(podm_id, None)
|
||||||
if podm_connection:
|
if podm_connection:
|
||||||
return podm_connection
|
return podm_connection
|
||||||
@ -25,7 +25,7 @@ def get_podm_connection(podm_id):
|
|||||||
username, password = utils.get_basic_auth_credentials(
|
username, password = utils.get_basic_auth_credentials(
|
||||||
podm_db['authentication'])
|
podm_db['authentication'])
|
||||||
podm_connection = Manager(podm_db['url'], username, password,
|
podm_connection = Manager(podm_db['url'], username, password,
|
||||||
podm_db['driver'])
|
podm_db['driver']).podm
|
||||||
podm_connections[podm_id] = podm_connection
|
podm_connections[podm_id] = podm_connection
|
||||||
return podm_connection
|
return podm_connection
|
||||||
|
|
||||||
|
@ -26,6 +26,18 @@ class PodManagerBase(object):
|
|||||||
def get_podm_info(self):
|
def get_podm_info(self):
|
||||||
return self.get_resource_info_by_url(self.podm_url)
|
return self.get_resource_info_by_url(self.podm_url)
|
||||||
|
|
||||||
|
# TODO(): use rsd_lib here
|
||||||
|
def compose_node(self, request_body):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# TODO(): use rsd_lib here
|
||||||
|
def delete_composed_node(self, node_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# TODO(): use rsd_lib here
|
||||||
|
def node_action(self, index, request_body):
|
||||||
|
pass
|
||||||
|
|
||||||
def get_resource_info_by_url(self, resource_url):
|
def get_resource_info_by_url(self, resource_url):
|
||||||
return self.driver.get_resources_by_url(resource_url)
|
return self.driver.get_resources_by_url(resource_url)
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import mock
|
|||||||
|
|
||||||
from valence.common import exception
|
from valence.common import exception
|
||||||
from valence.controller import nodes
|
from valence.controller import nodes
|
||||||
|
from valence.podmanagers import podm_base
|
||||||
from valence.tests.unit.db import utils as test_utils
|
from valence.tests.unit.db import utils as test_utils
|
||||||
from valence.tests.unit.fakes import flavor_fakes
|
from valence.tests.unit.fakes import flavor_fakes
|
||||||
from valence.tests.unit.fakes import node_fakes
|
from valence.tests.unit.fakes import node_fakes
|
||||||
@ -25,15 +26,22 @@ from valence.tests.unit.fakes import node_fakes
|
|||||||
|
|
||||||
class TestAPINodes(unittest.TestCase):
|
class TestAPINodes(unittest.TestCase):
|
||||||
|
|
||||||
|
@mock.patch('valence.podmanagers.manager.get_connection')
|
||||||
|
@mock.patch('valence.redfish.sushy.sushy_instance.RedfishInstance')
|
||||||
|
def setUp(self, mock_redfish, mock_connection):
|
||||||
|
self.node_controller = nodes.Node(podm_id='test-podm-1')
|
||||||
|
self.node_controller.connection = podm_base.PodManagerBase(
|
||||||
|
'fake', 'fake-pass', 'http://fake-url')
|
||||||
|
|
||||||
def test_show_node_brief_info(self):
|
def test_show_node_brief_info(self):
|
||||||
"""Test only show node brief info"""
|
"""Test only show node brief info"""
|
||||||
node_info = node_fakes.get_test_composed_node()
|
node_info = node_fakes.get_test_composed_node()
|
||||||
node_info['uri'] = 'nodes/7be5bc10-dcdf-11e6-bd86-934bc6947c55/'
|
|
||||||
expected = {
|
expected = {
|
||||||
"index": "1",
|
"index": "1",
|
||||||
"name": "fake_name",
|
"name": "fake_name",
|
||||||
|
"podm_id": "78e2a25-2901-438d-8157-de7ffd68d05",
|
||||||
"uuid": "ea8e2a25-2901-438d-8157-de7ffd68d051",
|
"uuid": "ea8e2a25-2901-438d-8157-de7ffd68d051",
|
||||||
"uri": 'nodes/7be5bc10-dcdf-11e6-bd86-934bc6947c55/',
|
"resource_uri": 'nodes/7be5bc10-dcdf-11e6-bd86-934bc6947c55/',
|
||||||
}
|
}
|
||||||
self.assertEqual(expected,
|
self.assertEqual(expected,
|
||||||
nodes.Node._show_node_brief_info(node_info))
|
nodes.Node._show_node_brief_info(node_info))
|
||||||
@ -72,10 +80,11 @@ class TestAPINodes(unittest.TestCase):
|
|||||||
@mock.patch("valence.db.api.Connection.create_composed_node")
|
@mock.patch("valence.db.api.Connection.create_composed_node")
|
||||||
@mock.patch("valence.common.utils.generate_uuid")
|
@mock.patch("valence.common.utils.generate_uuid")
|
||||||
@mock.patch("valence.controller.nodes.Node.list_composed_nodes")
|
@mock.patch("valence.controller.nodes.Node.list_composed_nodes")
|
||||||
@mock.patch("valence.redfish.redfish.get_node_by_id")
|
@mock.patch("valence.podmanagers.podm_base.PodManagerBase.get_node_info")
|
||||||
def test_manage_node(self, mock_get_node, mock_list_nodes,
|
def test_manage_node(self, mock_get_node, mock_list_nodes,
|
||||||
mock_generate_uuid, mock_db_create_composed_node):
|
mock_generate_uuid, mock_db_create_composed_node):
|
||||||
manage_node = node_fakes.get_test_composed_node()
|
manage_node = node_fakes.get_test_composed_node()
|
||||||
|
manage_node['podm_id'] = 'test-podm-1'
|
||||||
mock_get_node.return_value = manage_node
|
mock_get_node.return_value = manage_node
|
||||||
node_list = node_fakes.get_test_node_list()
|
node_list = node_fakes.get_test_node_list()
|
||||||
# Change the index of node 1 so that the node to manage
|
# Change the index of node 1 so that the node to manage
|
||||||
@ -85,47 +94,45 @@ class TestAPINodes(unittest.TestCase):
|
|||||||
|
|
||||||
uuid = "ea8e2a25-2901-438d-8157-de7ffd68d051"
|
uuid = "ea8e2a25-2901-438d-8157-de7ffd68d051"
|
||||||
mock_generate_uuid.return_value = uuid
|
mock_generate_uuid.return_value = uuid
|
||||||
|
|
||||||
node_db = {"uuid": manage_node["uuid"],
|
node_db = {"uuid": manage_node["uuid"],
|
||||||
"index": manage_node["index"],
|
"index": manage_node["index"],
|
||||||
|
"podm_id": manage_node["podm_id"],
|
||||||
"name": manage_node["name"],
|
"name": manage_node["name"],
|
||||||
"uri": manage_node["resource_uri"]}
|
"resource_uri": manage_node["resource_uri"]}
|
||||||
|
|
||||||
nodes.Node.manage_node({"node_index": "1"})
|
req = {"node_index": "1"}
|
||||||
|
self.node_controller.manage_node(req)
|
||||||
mock_db_create_composed_node.assert_called_once_with(node_db)
|
mock_db_create_composed_node.assert_called_once_with(node_db)
|
||||||
|
|
||||||
@mock.patch("valence.controller.nodes.Node.list_composed_nodes")
|
@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_list_nodes):
|
||||||
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
|
# 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
|
# being managed, meaning we're trying to manage a node that already
|
||||||
# exists in the Valence DB.
|
# exists in the Valence DB.
|
||||||
node_list = node_fakes.get_test_node_list()
|
node_list = node_fakes.get_test_node_list()
|
||||||
mock_list_nodes.return_value = node_list
|
mock_list_nodes.return_value = node_list
|
||||||
|
|
||||||
self.assertRaises(exception.ResourceExists,
|
self.assertRaises(exception.ResourceExists,
|
||||||
nodes.Node.manage_node,
|
self.node_controller.manage_node,
|
||||||
{"node_index": "1"})
|
{"node_index": "1"})
|
||||||
|
|
||||||
@mock.patch("valence.db.api.Connection.create_composed_node")
|
@mock.patch("valence.db.api.Connection.create_composed_node")
|
||||||
@mock.patch("valence.common.utils.generate_uuid")
|
@mock.patch("valence.common.utils.generate_uuid")
|
||||||
@mock.patch("valence.redfish.redfish.compose_node")
|
@mock.patch("valence.podmanagers.podm_base.PodManagerBase.compose_node")
|
||||||
def test_compose_node(self, mock_redfish_compose_node, mock_generate_uuid,
|
def test_compose_node(self, mock_redfish_compose_node, mock_generate_uuid,
|
||||||
mock_db_create_composed_node):
|
mock_db_create_composed_node):
|
||||||
"""Test compose node successfully"""
|
"""Test compose node successfully"""
|
||||||
node_hw = node_fakes.get_test_composed_node()
|
node_hw = node_fakes.get_test_composed_node()
|
||||||
node_db = {"uuid": node_hw["uuid"],
|
node_db = {"uuid": node_hw["uuid"],
|
||||||
|
"podm_id": 'test-podm-1',
|
||||||
"index": node_hw["index"],
|
"index": node_hw["index"],
|
||||||
"name": node_hw["name"],
|
"name": node_hw["name"],
|
||||||
"uri": node_hw["resource_uri"]}
|
"resource_uri": node_hw["resource_uri"]}
|
||||||
|
|
||||||
mock_redfish_compose_node.return_value = node_hw
|
mock_redfish_compose_node.return_value = node_hw
|
||||||
uuid = 'ea8e2a25-2901-438d-8157-de7ffd68d051'
|
uuid = 'ea8e2a25-2901-438d-8157-de7ffd68d051'
|
||||||
mock_generate_uuid.return_value = uuid
|
mock_generate_uuid.return_value = uuid
|
||||||
|
|
||||||
result = nodes.Node.compose_node(
|
result = self.node_controller.compose_node(
|
||||||
{"name": node_hw["name"],
|
{"name": node_hw["name"],
|
||||||
"description": node_hw["description"]})
|
"description": node_hw["description"]})
|
||||||
expected = nodes.Node._show_node_brief_info(node_hw)
|
expected = nodes.Node._show_node_brief_info(node_hw)
|
||||||
@ -135,7 +142,7 @@ class TestAPINodes(unittest.TestCase):
|
|||||||
|
|
||||||
@mock.patch("valence.db.api.Connection.create_composed_node")
|
@mock.patch("valence.db.api.Connection.create_composed_node")
|
||||||
@mock.patch("valence.common.utils.generate_uuid")
|
@mock.patch("valence.common.utils.generate_uuid")
|
||||||
@mock.patch("valence.redfish.redfish.compose_node")
|
@mock.patch("valence.podmanagers.podm_base.PodManagerBase.compose_node")
|
||||||
@mock.patch("valence.controller.flavors.get_flavor")
|
@mock.patch("valence.controller.flavors.get_flavor")
|
||||||
def test_compose_node_with_flavor(self, mock_get_flavor,
|
def test_compose_node_with_flavor(self, mock_get_flavor,
|
||||||
mock_redfish_compose_node,
|
mock_redfish_compose_node,
|
||||||
@ -144,9 +151,10 @@ class TestAPINodes(unittest.TestCase):
|
|||||||
"""Test node composition using a flavor for requirements"""
|
"""Test node composition using a flavor for requirements"""
|
||||||
node_hw = node_fakes.get_test_composed_node()
|
node_hw = node_fakes.get_test_composed_node()
|
||||||
node_db = {"uuid": node_hw["uuid"],
|
node_db = {"uuid": node_hw["uuid"],
|
||||||
|
"podm_id": 'test-podm-1',
|
||||||
"index": node_hw["index"],
|
"index": node_hw["index"],
|
||||||
"name": node_hw["name"],
|
"name": node_hw["name"],
|
||||||
"uri": node_hw["resource_uri"]}
|
"resource_uri": node_hw["resource_uri"]}
|
||||||
|
|
||||||
mock_redfish_compose_node.return_value = node_hw
|
mock_redfish_compose_node.return_value = node_hw
|
||||||
uuid = 'ea8e2a25-2901-438d-8157-de7ffd68d051'
|
uuid = 'ea8e2a25-2901-438d-8157-de7ffd68d051'
|
||||||
@ -155,7 +163,7 @@ class TestAPINodes(unittest.TestCase):
|
|||||||
flavor = flavor_fakes.fake_flavor()
|
flavor = flavor_fakes.fake_flavor()
|
||||||
mock_get_flavor.return_value = flavor
|
mock_get_flavor.return_value = flavor
|
||||||
|
|
||||||
result = nodes.Node.compose_node(
|
result = self.node_controller.compose_node(
|
||||||
{"name": node_hw["name"],
|
{"name": node_hw["name"],
|
||||||
"description": node_hw["description"],
|
"description": node_hw["description"],
|
||||||
"flavor_id": flavor["uuid"]})
|
"flavor_id": flavor["uuid"]})
|
||||||
@ -165,43 +173,26 @@ class TestAPINodes(unittest.TestCase):
|
|||||||
mock_db_create_composed_node.assert_called_once_with(node_db)
|
mock_db_create_composed_node.assert_called_once_with(node_db)
|
||||||
mock_get_flavor.assert_called_once_with(flavor["uuid"])
|
mock_get_flavor.assert_called_once_with(flavor["uuid"])
|
||||||
|
|
||||||
@mock.patch("valence.redfish.redfish.get_node_by_id")
|
@mock.patch("valence.podmanagers.podm_base.PodManagerBase.get_node_info")
|
||||||
@mock.patch("valence.db.api.Connection.get_composed_node_by_uuid")
|
def test_get_composed_node_by_uuid(self, mock_redfish_get_node):
|
||||||
def test_get_composed_node_by_uuid(
|
|
||||||
self, mock_db_get_composed_node, mock_redfish_get_node):
|
|
||||||
"""Test get composed node detail"""
|
"""Test get composed node detail"""
|
||||||
node_hw = node_fakes.get_test_composed_node()
|
node_hw = node_fakes.get_test_composed_node()
|
||||||
node_db = test_utils.get_test_composed_node_db_info()
|
node_db = test_utils.get_test_composed_node_db_info()
|
||||||
|
|
||||||
mock_db_model = mock.MagicMock()
|
self.node_controller.node = node_db
|
||||||
mock_db_model.as_dict.return_value = node_db
|
|
||||||
mock_db_get_composed_node.return_value = mock_db_model
|
|
||||||
|
|
||||||
mock_redfish_get_node.return_value = node_hw
|
mock_redfish_get_node.return_value = node_hw
|
||||||
|
|
||||||
result = nodes.Node.get_composed_node_by_uuid("fake_uuid")
|
result = self.node_controller.get_composed_node_by_uuid("fake_uuid")
|
||||||
|
|
||||||
expected = copy.deepcopy(node_hw)
|
expected = copy.deepcopy(node_hw)
|
||||||
expected.update(node_db)
|
expected.update(node_db)
|
||||||
self.assertEqual(expected, result)
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
@mock.patch("valence.db.api.Connection.delete_composed_node")
|
@mock.patch("valence.db.api.Connection.delete_composed_node")
|
||||||
@mock.patch("valence.redfish.redfish.delete_composed_node")
|
def test_delete_composed_node(self, mock_db_delete_composed_node):
|
||||||
@mock.patch("valence.db.api.Connection.get_composed_node_by_uuid")
|
|
||||||
def test_delete_composed_node(
|
|
||||||
self, mock_db_get_composed_node, mock_redfish_delete_composed_node,
|
|
||||||
mock_db_delete_composed_node):
|
|
||||||
"""Test delete composed node"""
|
"""Test delete composed node"""
|
||||||
node_db = test_utils.get_test_composed_node_db_info()
|
node_db = test_utils.get_test_composed_node_db_info()
|
||||||
|
self.node_controller.node = node_db
|
||||||
mock_db_model = mock.MagicMock()
|
self.node_controller.delete_composed_node(node_db["uuid"])
|
||||||
mock_db_model.index = node_db["index"]
|
|
||||||
mock_db_get_composed_node.return_value = mock_db_model
|
|
||||||
|
|
||||||
nodes.Node.delete_composed_node(node_db["uuid"])
|
|
||||||
|
|
||||||
mock_redfish_delete_composed_node.assert_called_once_with(
|
|
||||||
node_db["index"])
|
|
||||||
mock_db_delete_composed_node.assert_called_once_with(
|
mock_db_delete_composed_node.assert_called_once_with(
|
||||||
node_db["uuid"])
|
node_db["uuid"])
|
||||||
|
|
||||||
@ -220,17 +211,12 @@ class TestAPINodes(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(expected, result)
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
@mock.patch("valence.redfish.redfish.node_action")
|
@mock.patch("valence.podmanagers.podm_base.PodManagerBase.node_action")
|
||||||
@mock.patch("valence.db.api.Connection.get_composed_node_by_uuid")
|
def test_node_action(self, mock_node_action):
|
||||||
def test_node_action(
|
|
||||||
self, mock_db_get_composed_node, mock_node_action):
|
|
||||||
"""Test reset composed node status"""
|
"""Test reset composed node status"""
|
||||||
action = {"Reset": {"Type": "On"}}
|
action = {"Reset": {"Type": "On"}}
|
||||||
mock_db_model = mock.MagicMock()
|
self.node_controller.node = {'index': '1', 'name': 'test-node'}
|
||||||
mock_db_model.index = "1"
|
self.node_controller.node_action("fake_uuid", action)
|
||||||
mock_db_get_composed_node.return_value = mock_db_model
|
|
||||||
|
|
||||||
nodes.Node.node_action("fake_uuid", action)
|
|
||||||
mock_node_action.assert_called_once_with("1", action)
|
mock_node_action.assert_called_once_with("1", action)
|
||||||
|
|
||||||
@mock.patch("valence.provision.driver.node_register")
|
@mock.patch("valence.provision.driver.node_register")
|
||||||
|
@ -15,6 +15,7 @@ def get_test_composed_node(**kwargs):
|
|||||||
return {
|
return {
|
||||||
'uuid': kwargs.get('uuid', 'ea8e2a25-2901-438d-8157-de7ffd68d051'),
|
'uuid': kwargs.get('uuid', 'ea8e2a25-2901-438d-8157-de7ffd68d051'),
|
||||||
'name': kwargs.get('name', 'fake_name'),
|
'name': kwargs.get('name', 'fake_name'),
|
||||||
|
'podm_id': kwargs.get('podm_id', '78e2a25-2901-438d-8157-de7ffd68d05'),
|
||||||
'description': kwargs.get('description', 'fake_description'),
|
'description': kwargs.get('description', 'fake_description'),
|
||||||
'boot_source': kwargs.get('boot_source', 'Hdd'),
|
'boot_source': kwargs.get('boot_source', 'Hdd'),
|
||||||
'health_status': kwargs.get('health_status', 'OK'),
|
'health_status': kwargs.get('health_status', 'OK'),
|
||||||
|
@ -43,7 +43,6 @@ class TestManager(base.BaseTestCase):
|
|||||||
@mock.patch("valence.redfish.sushy.sushy_instance.RedfishInstance")
|
@mock.patch("valence.redfish.sushy.sushy_instance.RedfishInstance")
|
||||||
def test_get_podm_connection(self, redfish_mock, get_podm_mock):
|
def test_get_podm_connection(self, redfish_mock, get_podm_mock):
|
||||||
get_podm_mock.return_value = podmanager_fakes.fake_podm_object()
|
get_podm_mock.return_value = podmanager_fakes.fake_podm_object()
|
||||||
inst = manager.get_podm_connection("fake-id")
|
inst = manager.get_connection("fake-id")
|
||||||
self.assertTrue(isinstance(inst, manager.Manager))
|
self.assertTrue(isinstance(inst, podm_base.PodManagerBase))
|
||||||
self.assertTrue(isinstance(inst.podm, podm_base.PodManagerBase))
|
|
||||||
self.assertTrue(manager.podm_connections['fake-id'])
|
self.assertTrue(manager.podm_connections['fake-id'])
|
||||||
|
@ -130,10 +130,11 @@ class TestPodmanagerApi(TestApiValidation):
|
|||||||
self.assertEqual('Validation Error', response['title'])
|
self.assertEqual('Validation Error', response['title'])
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('valence.podmanagers.manager.get_connection')
|
||||||
class TestNodeApi(TestApiValidation):
|
class TestNodeApi(TestApiValidation):
|
||||||
|
|
||||||
@mock.patch('valence.controller.nodes.Node.compose_node')
|
@mock.patch('valence.controller.nodes.Node.compose_node')
|
||||||
def test_compose_request_using_properties(self, mock_compose):
|
def test_compose_request_using_properties(self, mock_compose, mock_conn):
|
||||||
req = {
|
req = {
|
||||||
"name": "test_request",
|
"name": "test_request",
|
||||||
"podm_id": "test-podm",
|
"podm_id": "test-podm",
|
||||||
@ -152,21 +153,24 @@ class TestNodeApi(TestApiValidation):
|
|||||||
resp = self.app.post('/v1/nodes',
|
resp = self.app.post('/v1/nodes',
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
data=json.dumps(req))
|
data=json.dumps(req))
|
||||||
|
mock_conn.assert_called_once_with('test-podm')
|
||||||
self.assertEqual(http_client.OK, resp.status_code)
|
self.assertEqual(http_client.OK, resp.status_code)
|
||||||
|
|
||||||
@mock.patch('valence.controller.nodes.Node.compose_node')
|
@mock.patch('valence.controller.nodes.Node.compose_node')
|
||||||
def test_compose_request_using_flavor(self, mock_compose):
|
def test_compose_request_using_flavor(self, mock_compose, mock_connection):
|
||||||
req = {
|
req = {
|
||||||
"name": "test_request1",
|
"name": "test_request1",
|
||||||
"flavor_id": "test_flavor"
|
"flavor_id": "test_flavor",
|
||||||
|
"podm_id": "test-podm-1"
|
||||||
}
|
}
|
||||||
mock_compose.return_value = req
|
mock_compose.return_value = req
|
||||||
resp = self.app.post('/v1/nodes',
|
resp = self.app.post('/v1/nodes',
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
data=json.dumps(req))
|
data=json.dumps(req))
|
||||||
|
mock_connection.assert_called_once_with('test-podm-1')
|
||||||
self.assertEqual(http_client.OK, resp.status_code)
|
self.assertEqual(http_client.OK, resp.status_code)
|
||||||
|
|
||||||
def test_compose_request_invalid_params(self):
|
def test_compose_request_invalid_params(self, mock_conn):
|
||||||
req = {
|
req = {
|
||||||
"name": "test_request1",
|
"name": "test_request1",
|
||||||
"properties": {"invalid_key": "invalid_value"}
|
"properties": {"invalid_key": "invalid_value"}
|
||||||
@ -179,7 +183,7 @@ class TestNodeApi(TestApiValidation):
|
|||||||
self.assertEqual('Validation Error', response['title'])
|
self.assertEqual('Validation Error', response['title'])
|
||||||
|
|
||||||
@mock.patch('valence.controller.nodes.Node.manage_node')
|
@mock.patch('valence.controller.nodes.Node.manage_node')
|
||||||
def test_node_manage_request(self, mock_manage):
|
def test_node_manage_request(self, mock_manage, mock_connection):
|
||||||
req = {"node_index": "fake-index",
|
req = {"node_index": "fake-index",
|
||||||
"podm_id": "test-podm-id"}
|
"podm_id": "test-podm-id"}
|
||||||
mock_manage.return_value = {"uuid": "ea8e2a25-2901-438d-8157-de7ffd",
|
mock_manage.return_value = {"uuid": "ea8e2a25-2901-438d-8157-de7ffd",
|
||||||
@ -190,10 +194,11 @@ class TestNodeApi(TestApiValidation):
|
|||||||
resp = self.app.post('/v1/nodes/manage',
|
resp = self.app.post('/v1/nodes/manage',
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
data=json.dumps(req))
|
data=json.dumps(req))
|
||||||
|
mock_connection.assert_called_once_with('test-podm-id')
|
||||||
mock_manage.assert_called_once_with(req)
|
mock_manage.assert_called_once_with(req)
|
||||||
self.assertEqual(http_client.OK, resp.status_code)
|
self.assertEqual(http_client.OK, resp.status_code)
|
||||||
|
|
||||||
def test_node_manage_request_invalid(self):
|
def test_node_manage_request_invalid(self, mock_conn):
|
||||||
req = {"node_id": "fake-index"}
|
req = {"node_id": "fake-index"}
|
||||||
resp = self.app.post('/v1/nodes/manage',
|
resp = self.app.post('/v1/nodes/manage',
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
@ -202,8 +207,9 @@ class TestNodeApi(TestApiValidation):
|
|||||||
self.assertEqual(http_client.BAD_REQUEST, response['status'])
|
self.assertEqual(http_client.BAD_REQUEST, response['status'])
|
||||||
self.assertEqual('Validation Error', response['title'])
|
self.assertEqual('Validation Error', response['title'])
|
||||||
|
|
||||||
|
@mock.patch('valence.db.api.Connection.get_composed_node_by_uuid')
|
||||||
@mock.patch('valence.controller.nodes.Node.node_action')
|
@mock.patch('valence.controller.nodes.Node.node_action')
|
||||||
def test_node_action_request(self, mock_action):
|
def test_node_action_request(self, mock_action, m_node, mock_connection):
|
||||||
req = {
|
req = {
|
||||||
"Reset": {
|
"Reset": {
|
||||||
"Type": "On"
|
"Type": "On"
|
||||||
@ -217,7 +223,9 @@ class TestNodeApi(TestApiValidation):
|
|||||||
self.assertEqual(http_client.NO_CONTENT, resp.status_code)
|
self.assertEqual(http_client.NO_CONTENT, resp.status_code)
|
||||||
|
|
||||||
@mock.patch('valence.controller.nodes.Node.node_action')
|
@mock.patch('valence.controller.nodes.Node.node_action')
|
||||||
def test_node_action_attach_request(self, mock_action):
|
@mock.patch('valence.db.api.Connection.get_composed_node_by_uuid')
|
||||||
|
def test_node_action_attach_request(self, mock_node, mock_action,
|
||||||
|
mock_connection):
|
||||||
req = {
|
req = {
|
||||||
"attach": {
|
"attach": {
|
||||||
"resource_id": "test-device-1"
|
"resource_id": "test-device-1"
|
||||||
@ -231,7 +239,9 @@ class TestNodeApi(TestApiValidation):
|
|||||||
self.assertEqual(http_client.NO_CONTENT, resp.status_code)
|
self.assertEqual(http_client.NO_CONTENT, resp.status_code)
|
||||||
|
|
||||||
@mock.patch('valence.controller.nodes.Node.node_action')
|
@mock.patch('valence.controller.nodes.Node.node_action')
|
||||||
def test_node_action_detach_request(self, mock_action):
|
@mock.patch('valence.db.api.Connection.get_composed_node_by_uuid')
|
||||||
|
def test_node_action_detach_request(self, mock_node, mock_action,
|
||||||
|
mock_connection):
|
||||||
req = {
|
req = {
|
||||||
"detach": {
|
"detach": {
|
||||||
"resource_id": "test-device-1"
|
"resource_id": "test-device-1"
|
||||||
@ -244,7 +254,8 @@ class TestNodeApi(TestApiValidation):
|
|||||||
mock_action.assert_called_once_with('fake-node', req)
|
mock_action.assert_called_once_with('fake-node', req)
|
||||||
self.assertEqual(http_client.NO_CONTENT, resp.status_code)
|
self.assertEqual(http_client.NO_CONTENT, resp.status_code)
|
||||||
|
|
||||||
def test_node_action_request_invalid(self):
|
@mock.patch('valence.db.api.Connection.get_composed_node_by_uuid')
|
||||||
|
def test_node_action_request_invalid(self, mock_node, mock_connection):
|
||||||
req = {
|
req = {
|
||||||
"Boot": {
|
"Boot": {
|
||||||
"Type": "On"
|
"Type": "On"
|
||||||
|
Loading…
Reference in New Issue
Block a user