From 29dc61b8f690af2ecbfca27a3a82f26b450baff4 Mon Sep 17 00:00:00 2001 From: Lin Yang Date: Fri, 13 Jan 2017 19:18:55 -0800 Subject: [PATCH] Use node uuid instead of index for /node(s) api Since valence will support multiply backend podm, obviously the node index is not unique from different podm, assign uuid for each composed node and store node info into etcd. User should use uuid instead of index for node operation. And in some cases, user or other service directly operate composed, which will result in inconsistent status between RSD and etcd. So check it when get node details, and update if necessary. Change-Id: Ie365d61083183fa0b699ce978aaa5fdeb740f11d Closes-Bug: #1656453 --- valence/api/route.py | 6 +- valence/api/v1/nodes.py | 41 ++++--- valence/common/utils.py | 6 + valence/controller/nodes.py | 99 +++++++++++++++++ valence/db/api.py | 44 ++++++++ valence/db/etcd_db.py | 3 +- valence/db/etcd_driver.py | 50 +++++++++ valence/db/models.py | 20 ++++ valence/redfish/redfish.py | 6 +- valence/tests/unit/controller/fakes.py | 52 +++++++++ valence/tests/unit/controller/test_nodes.py | 117 ++++++++++++++++++++ valence/tests/unit/db/test_db_api.py | 80 +++++++++++++ valence/tests/unit/db/utils.py | 18 +++ valence/tests/unit/redfish/test_redfish.py | 8 +- 14 files changed, 518 insertions(+), 32 deletions(-) create mode 100644 valence/controller/nodes.py create mode 100644 valence/tests/unit/controller/fakes.py create mode 100644 valence/tests/unit/controller/test_nodes.py diff --git a/valence/api/route.py b/valence/api/route.py index eaa7673..57a633e 100644 --- a/valence/api/route.py +++ b/valence/api/route.py @@ -66,8 +66,10 @@ api.add_resource(api_root.Root, '/', endpoint='root') api.add_resource(v1_version.V1, '/v1', endpoint='v1') # Node(s) operations -api.add_resource(v1_nodes.NodesList, '/v1/nodes', endpoint='nodes') -api.add_resource(v1_nodes.Nodes, '/v1/nodes/', endpoint='node') +api.add_resource(v1_nodes.Nodes, '/v1/nodes', endpoint='nodes') +api.add_resource(v1_nodes.Node, + '/v1/nodes/', + endpoint='node') api.add_resource(v1_nodes.NodesStorage, '/v1/nodes//storages', endpoint='nodes_storages') diff --git a/valence/api/v1/nodes.py b/valence/api/v1/nodes.py index 7552b00..dd93e3e 100644 --- a/valence/api/v1/nodes.py +++ b/valence/api/v1/nodes.py @@ -12,39 +12,36 @@ # License for the specific language governing permissions and limitations # under the License. -import logging - from flask import request from flask_restful import abort from flask_restful import Resource from six.moves import http_client from valence.common import utils -from valence.redfish import redfish - -LOG = logging.getLogger(__name__) - - -class NodesList(Resource): - - def get(self): - return utils.make_response(http_client.OK, - redfish.list_nodes()) - - def post(self): - return utils.make_response( - http_client.OK, redfish.compose_node(request.get_json())) +from valence.controller import nodes class Nodes(Resource): - def get(self, nodeid): - return utils.make_response(http_client.OK, - redfish.get_node_by_id(nodeid)) + def get(self): + return utils.make_response( + http_client.OK, nodes.Node.list_composed_nodes()) - def delete(self, nodeid): - return utils.make_response(http_client.OK, - redfish.delete_composednode(nodeid)) + def post(self): + return utils.make_response( + http_client.OK, nodes.Node.compose_node(request.get_json())) + + +class Node(Resource): + + def get(self, node_uuid): + return utils.make_response( + http_client.OK, + nodes.Node.get_composed_node_by_uuid(node_uuid)) + + def delete(self, node_uuid): + return utils.make_response( + http_client.OK, nodes.Node.delete_composed_node(node_uuid)) class NodesStorage(Resource): diff --git a/valence/common/utils.py b/valence/common/utils.py index 612df5f..36b8410 100644 --- a/valence/common/utils.py +++ b/valence/common/utils.py @@ -21,6 +21,7 @@ import logging import flask +from oslo_utils import uuidutils import six LOG = logging.getLogger(__name__) @@ -106,3 +107,8 @@ def make_response(status_code, content="", headers=None): raise ValueError("Response headers should be dict object.") return response + + +def generate_uuid(): + """Generate uniform format uuid""" + return uuidutils.generate_uuid() diff --git a/valence/controller/nodes.py b/valence/controller/nodes.py new file mode 100644 index 0000000..f075c2a --- /dev/null +++ b/valence/controller/nodes.py @@ -0,0 +1,99 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import six + +from valence.common import utils +from valence.db import api as db_api +from valence.redfish import redfish + + +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"]} + + @classmethod + def compose_node(cls, request_body): + """Compose new node + + param request_body: parameter for node composition + return: brief info of this new composed node + """ + + # Call redfish to compose new node + composed_node = redfish.compose_node(request_body) + + composed_node["uuid"] = utils.generate_uuid() + + # Only store the minimum set of composed node info into backend db, + # since other fields like power status may be changed and valence is + # not aware. + 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 + + Get the detail of specific composed node. In some cases db data may be + inconsistent with podm side, like user directly operate podm, not + through valence api. So compare it with node info from redfish, and + update db if it's inconsistent. + + param node_uuid: uuid of composed node + return: detail of this composed node + """ + + node_db = db_api.Connection.get_composed_node_by_uuid(node_uuid)\ + .as_dict() + node_hw = redfish.get_node_by_id(node_db["index"]) + + # Add those fields of composed node from db + node_hw.update(node_db) + + return node_hw + + @classmethod + def delete_composed_node(cls, node_uuid): + """Delete a composed node + + param node_uuid: uuid of composed node + return: message of this deletion + """ + + # Get node detail from db, and map node uuid to 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) + + return message + + @classmethod + def list_composed_nodes(cls): + """List all composed node + + return: brief info of all composed node + """ + return [cls._show_node_brief_info(node_info.as_dict()) + for node_info in db_api.Connection.list_composed_nodes()] diff --git a/valence/db/api.py b/valence/db/api.py index 20b104a..a083939 100644 --- a/valence/db/api.py +++ b/valence/db/api.py @@ -115,3 +115,47 @@ class Connection(object): :returns: A list of all flavors. """ return cls.dbdriver.list_flavors() + + @classmethod + def create_composed_node(cls, values): + """Create a new composed node. + + :values: The properties for this new composed node. + :returns: A composed node created. + """ + return cls.dbdriver.create_composed_node(values) + + @classmethod + def get_composed_node_by_uuid(cls, composed_node_uuid): + """Get specific composed node by its uuid + + :param composed_node_uuid: The uuid of composed node. + :returns: A composed node with this uuid. + """ + return cls.dbdriver.get_composed_node_by_uuid(composed_node_uuid) + + @classmethod + def delete_composed_node(cls, composed_node_uuid): + """Delete specific composed node by its uuid + + :param composed_node_uuid: The uuid of composed node. + """ + cls.dbdriver.delete_composed_node(composed_node_uuid) + + @classmethod + def update_composed_node(cls, composed_node_uuid, values): + """Update properties of a composed node. + + :param composed_node_uuid: The uuid of composed node. + :values: The properties to be updated. + :returns: A composed node model after updated. + """ + return cls.dbdriver.update_composed_node(composed_node_uuid, values) + + @classmethod + def list_composed_nodes(cls): + """Get a list of all composed nodes. + + :returns: A list of all composed node. + """ + return cls.dbdriver.list_composed_nodes() diff --git a/valence/db/etcd_db.py b/valence/db/etcd_db.py index ea4ec33..078400f 100644 --- a/valence/db/etcd_db.py +++ b/valence/db/etcd_db.py @@ -20,7 +20,8 @@ from valence.db import models etcd_directories = [ models.PodManager.path, - models.Flavor.path + models.Flavor.path, + models.ComposedNode.path ] etcd_client = etcd.Client(config.etcd_host, config.etcd_port) diff --git a/valence/db/etcd_driver.py b/valence/db/etcd_driver.py index 1e94047..79715db 100644 --- a/valence/db/etcd_driver.py +++ b/valence/db/etcd_driver.py @@ -39,6 +39,8 @@ def translate_to_models(etcd_resp, model_type): ret = models.PodManager(**data) elif model_type == models.Flavor.path: ret = models.Flavor(**data) + elif model_type == models.ComposedNode.path: + ret = models.ComposedNode(**data) else: # TODO(lin.a.yang): after exception module got merged, raise # valence specific InvalidParameter exception here @@ -149,3 +151,51 @@ class EtcdDriver(object): flavor, models.Flavor.path)) return flavors + + def create_composed_node(self, values): + composed_node = models.ComposedNode(**values) + composed_node.save() + + return composed_node + + def get_composed_node_by_uuid(self, composed_node_uuid): + try: + resp = self.client.read(models.ComposedNode.etcd_path( + composed_node_uuid)) + except etcd.EtcdKeyNotFound: + # TODO(lin.a.yang): after exception module got merged, raise + # valence specific DBNotFound exception here + raise Exception( + 'Composed node not found {0} in database.'.format( + composed_node_uuid)) + + return translate_to_models(resp, models.ComposedNode.path) + + def delete_composed_node(self, composed_node_uuid): + composed_node = self.get_composed_node_by_uuid(composed_node_uuid) + composed_node.delete() + + def update_composed_node(self, composed_node_uuid, values): + composed_node = self.get_composed_node_by_uuid(composed_node_uuid) + composed_node.update(values) + + return composed_node + + def list_composed_nodes(self): + # TODO(lin.a.yang): support filter for listing composed_node + + try: + resp = getattr(self.client.read(models.ComposedNode.path), + 'children', None) + except etcd.EtcdKeyNotFound: + LOG.error("Path '/nodes' does not exist, seems etcd server " + "was not initialized appropriately.") + raise + + composed_nodes = [] + for node in resp: + if node.value is not None: + composed_nodes.append(translate_to_models( + node, models.ComposedNode.path)) + + return composed_nodes diff --git a/valence/db/models.py b/valence/db/models.py index 4e47a8c..4d4f40a 100644 --- a/valence/db/models.py +++ b/valence/db/models.py @@ -189,3 +189,23 @@ class Flavor(ModelBaseWithTimeStamp): 'validate': types.Dict.validate } } + + +class ComposedNode(ModelBaseWithTimeStamp): + + path = "/nodes" + + fields = { + 'uuid': { + 'validate': types.Text.validate + }, + 'name': { + 'validate': types.Text.validate + }, + 'index': { + 'validate': types.Text.validate + }, + 'links': { + 'validate': types.List(types.Dict).validate + } + } diff --git a/valence/redfish/redfish.py b/valence/redfish/redfish.py index 5b40ef5..fb42c64 100644 --- a/valence/redfish/redfish.py +++ b/valence/redfish/redfish.py @@ -490,7 +490,7 @@ def compose_node(request_body): if assemble_resp.status_code != http_client.NO_CONTENT: # Delete node if assemble failed - delete_composednode(node_index) + delete_composed_node(node_index) raise exception.RedfishException(assemble_resp.json(), status_code=resp.status_code) else: @@ -498,10 +498,10 @@ def compose_node(request_body): LOG.debug('Successfully assembled node: ' + node_url) # Return new composed node index - return get_node_by_id(node_index, show_detail=False) + return get_node_by_id(node_index) -def delete_composednode(nodeid): +def delete_composed_node(nodeid): nodes_url = get_base_resource_url("Nodes") delete_url = nodes_url + '/' + str(nodeid) resp = send_request(delete_url, "DELETE") diff --git a/valence/tests/unit/controller/fakes.py b/valence/tests/unit/controller/fakes.py new file mode 100644 index 0000000..2e45132 --- /dev/null +++ b/valence/tests/unit/controller/fakes.py @@ -0,0 +1,52 @@ +# copyright (c) 2016 Intel, Inc. +# +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +def get_test_composed_node(**kwargs): + return { + 'uuid': kwargs.get('uuid', 'ea8e2a25-2901-438d-8157-de7ffd68d051'), + 'name': kwargs.get('name', 'fake_name'), + 'description': kwargs.get('description', 'fake_description'), + 'boot_source': kwargs.get('boot_source', 'Hdd'), + 'health_status': kwargs.get('health_status', 'OK'), + 'index': kwargs.get('index', '1'), + 'node_power_state': kwargs.get('node_power_state', 'On'), + 'node_state': kwargs.get('node_state', 'Assembling'), + 'pooled_group_id': kwargs.get('bookmark_link', 'None'), + 'target_boot_source': kwargs.get('target_boot_source', 'Hdd'), + 'links': kwargs.get( + 'links', + [{'href': 'http://127.0.0.1:8181/v1/nodes/' + '7be5bc10-dcdf-11e6-bd86-934bc6947c55/', + 'rel': 'self'}, + {'href': 'http://127.0.0.1:8181/nodes/' + '7be5bc10-dcdf-11e6-bd86-934bc6947c55/', + 'rel': 'bookmark'}]), + 'metadata': kwargs.get( + 'metadata', + {'memory': [{'data_width_bit': 0, + 'speed_mhz': 2400, + 'total_memory_mb': 8192}], + 'network': [{'ipv4': [{'address': '192.168.0.10', + 'gateway': '192.168.0.1', + 'subnet_mask': '255.255.252.0'}], + 'mac': 'e9:47:d3:60:64:66', + 'speed_mbps': 0, + 'status': 'Enabled', + 'vlans': [{'status': 'Enabled', + 'vlanid': 99}]}], + 'processor': [{'instruction_set': None, + 'model': None, + 'speed_mhz': 3700, + 'total_core': 0}]}) + } diff --git a/valence/tests/unit/controller/test_nodes.py b/valence/tests/unit/controller/test_nodes.py new file mode 100644 index 0000000..bc488f3 --- /dev/null +++ b/valence/tests/unit/controller/test_nodes.py @@ -0,0 +1,117 @@ +# copyright (c) 2016 Intel, Inc. +# +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy +import unittest + +import mock + +from valence.controller import nodes +from valence.tests.unit.controller import fakes +from valence.tests.unit.db import utils as test_utils + + +class TestAPINodes(unittest.TestCase): + + def test_show_node_brief_info(self): + """Test only show node brief info""" + node_info = fakes.get_test_composed_node() + expected = { + "name": "fake_name", + "uuid": "ea8e2a25-2901-438d-8157-de7ffd68d051", + "links": [{'href': 'http://127.0.0.1:8181/v1/nodes/' + '7be5bc10-dcdf-11e6-bd86-934bc6947c55/', + 'rel': 'self'}, + {'href': 'http://127.0.0.1:8181/nodes/' + '7be5bc10-dcdf-11e6-bd86-934bc6947c55/', + 'rel': 'bookmark'}] + } + self.assertEqual(expected, + nodes.Node._show_node_brief_info(node_info)) + + @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_db = {"uuid": node_hw["uuid"], + "index": node_hw["index"], + "name": node_hw["name"], + "links": node_hw["links"]} + + mock_redfish_compose_node.return_value = node_hw + uuid = 'ea8e2a25-2901-438d-8157-de7ffd68d051' + mock_generate_uuid.return_value = uuid + + result = nodes.Node.compose_node({"name": "test"}) + expected = nodes.Node._show_node_brief_info(node_hw) + + self.assertEqual(expected, result) + mock_db_create_composed_node.assert_called_once_with(node_db) + + @mock.patch("valence.redfish.redfish.get_node_by_id") + @mock.patch("valence.db.api.Connection.get_composed_node_by_uuid") + 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_db = test_utils.get_test_composed_node_db_info() + + mock_db_model = mock.MagicMock() + 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 + + result = nodes.Node.get_composed_node_by_uuid("fake_uuid") + + expected = copy.deepcopy(node_hw) + expected.update(node_db) + self.assertEqual(expected, result) + + @mock.patch("valence.db.api.Connection.delete_composed_node") + @mock.patch("valence.redfish.redfish.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""" + node_db = test_utils.get_test_composed_node_db_info() + + mock_db_model = mock.MagicMock() + 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( + node_db["uuid"]) + + @mock.patch("valence.db.api.Connection.list_composed_nodes") + def test_list_composed_nodes(self, mock_db_list_composed_nodes): + """Test list all composed nodes""" + node_db = test_utils.get_test_composed_node_db_info() + + mock_db_model = mock.MagicMock() + mock_db_model.as_dict.return_value = node_db + mock_db_list_composed_nodes.return_value = [mock_db_model] + + expected = [nodes.Node._show_node_brief_info(node_db)] + + result = nodes.Node.list_composed_nodes() + + self.assertEqual(expected, result) diff --git a/valence/tests/unit/db/test_db_api.py b/valence/tests/unit/db/test_db_api.py index 30be80c..c6f3ad0 100644 --- a/valence/tests/unit/db/test_db_api.py +++ b/valence/tests/unit/db/test_db_api.py @@ -178,3 +178,83 @@ class TestDBAPI(unittest.TestCase): mock_etcd_write.assert_called_with( '/flavors/' + flavor['uuid'], json.dumps(result.as_dict())) + + @freezegun.freeze_time("2017-01-01") + @mock.patch('etcd.Client.write') + @mock.patch('etcd.Client.read') + def test_create_composed_node(self, mock_etcd_read, mock_etcd_write): + node = utils.get_test_composed_node_db_info() + fake_utcnow = '2017-01-01 00:00:00 UTC' + node['created_at'] = fake_utcnow + node['updated_at'] = fake_utcnow + + # Mark this uuid don't exist in etcd db + mock_etcd_read.side_effect = etcd.EtcdKeyNotFound + + result = db_api.Connection.create_composed_node(node) + self.assertEqual(node, result.as_dict()) + mock_etcd_read.assert_called_once_with( + '/nodes/' + node['uuid']) + mock_etcd_write.assert_called_once_with( + '/nodes/' + node['uuid'], + json.dumps(result.as_dict())) + + @mock.patch('etcd.Client.read') + def test_get_composed_node_by_uuid(self, mock_etcd_read): + node = utils.get_test_composed_node_db_info() + + mock_etcd_read.return_value = utils.get_etcd_read_result( + node['uuid'], json.dumps(node)) + result = db_api.Connection.get_composed_node_by_uuid(node['uuid']) + + self.assertEqual(node, result.as_dict()) + mock_etcd_read.assert_called_once_with( + '/nodes/' + node['uuid']) + + @mock.patch('etcd.Client.read') + def test_get_composed_node_not_found(self, mock_etcd_read): + node = utils.get_test_composed_node_db_info() + mock_etcd_read.side_effect = etcd.EtcdKeyNotFound + + with self.assertRaises(Exception) as context: # noqa: H202 + db_api.Connection.get_composed_node_by_uuid(node['uuid']) + + self.assertTrue('Composed node not found {0} in database.'.format( + node['uuid']) in str(context.exception)) + mock_etcd_read.assert_called_once_with( + '/nodes/' + node['uuid']) + + @mock.patch('etcd.Client.delete') + @mock.patch('etcd.Client.read') + def test_delete_composed_node(self, mock_etcd_read, mock_etcd_delete): + node = utils.get_test_composed_node_db_info() + + mock_etcd_read.return_value = utils.get_etcd_read_result( + node['uuid'], json.dumps(node)) + db_api.Connection.delete_composed_node(node['uuid']) + + mock_etcd_delete.assert_called_with( + '/nodes/' + node['uuid']) + + @freezegun.freeze_time("2017-01-01") + @mock.patch('etcd.Client.write') + @mock.patch('etcd.Client.read') + def test_update_composed_node(self, mock_etcd_read, mock_etcd_write): + node = utils.get_test_composed_node_db_info() + + mock_etcd_read.return_value = utils.get_etcd_read_result( + node['uuid'], json.dumps(node)) + + fake_utcnow = '2017-01-01 00:00:00 UTC' + node['updated_at'] = fake_utcnow + node.update({'index': '2'}) + + result = db_api.Connection.update_composed_node( + node['uuid'], {'index': '2'}) + + self.assertEqual(node, result.as_dict()) + mock_etcd_read.assert_called_with( + '/nodes/' + node['uuid']) + mock_etcd_write.assert_called_with( + '/nodes/' + node['uuid'], + json.dumps(result.as_dict())) diff --git a/valence/tests/unit/db/utils.py b/valence/tests/unit/db/utils.py index faab582..d88c214 100644 --- a/valence/tests/unit/db/utils.py +++ b/valence/tests/unit/db/utils.py @@ -75,3 +75,21 @@ def get_test_flavor(**kwargs): 'created_at': kwargs.get('created_at', '2016-01-01 00:00:00 UTC'), 'updated_at': kwargs.get('updated_at', '2016-01-01 00:00:00 UTC'), } + + +def get_test_composed_node_db_info(**kwargs): + return { + 'uuid': kwargs.get('uuid', 'ea8e2a25-2901-438d-8157-de7ffd68d051'), + 'name': kwargs.get('name', 'fake_name'), + 'index': kwargs.get('index', '1'), + 'links': kwargs.get( + 'links', + [{'href': 'http://127.0.0.1:8181/v1/nodes/' + '7be5bc10-dcdf-11e6-bd86-934bc6947c55/', + 'rel': 'self'}, + {'href': 'http://127.0.0.1:8181/nodes/' + '7be5bc10-dcdf-11e6-bd86-934bc6947c55/', + 'rel': 'bookmark'}]), + 'created_at': kwargs.get('created_at', '2016-01-01 00:00:00 UTC'), + 'updated_at': kwargs.get('updated_at', '2016-01-01 00:00:00 UTC') + } diff --git a/valence/tests/unit/redfish/test_redfish.py b/valence/tests/unit/redfish/test_redfish.py index f4ad1be..2c94ab6 100644 --- a/valence/tests/unit/redfish/test_redfish.py +++ b/valence/tests/unit/redfish/test_redfish.py @@ -255,7 +255,7 @@ class TestRedfish(TestCase): fake_delete_response = fakes.mock_request_get(delete_result, http_client.NO_CONTENT) mock_request.return_value = fake_delete_response - result = redfish.delete_composednode(101) + result = redfish.delete_composed_node(101) mock_request.assert_called_with('/redfish/v1/Nodes/101', 'DELETE') expected = { "code": "DELETED", @@ -276,7 +276,7 @@ class TestRedfish(TestCase): http_client.INTERNAL_SERVER_ERROR) mock_request.return_value = fake_resp self.assertRaises(exception.RedfishException, - redfish.delete_composednode, 101) + redfish.delete_composed_node, 101) self.assertFalse(mock_make_response.called) @mock.patch('requests.get') @@ -340,7 +340,7 @@ class TestRedfish(TestCase): self.assertTrue("There are no computer systems available for this " "allocation request." in str(context.exception.detail)) - @mock.patch('valence.redfish.redfish.delete_composednode') + @mock.patch('valence.redfish.redfish.delete_composed_node') @mock.patch('valence.redfish.redfish.get_base_resource_url') @mock.patch('valence.redfish.redfish.send_request') def test_assemble_node_failed(self, mock_request, mock_get_url, @@ -375,7 +375,7 @@ class TestRedfish(TestCase): mock_delete_node.assert_called_once() @mock.patch('valence.redfish.redfish.get_node_by_id') - @mock.patch('valence.redfish.redfish.delete_composednode') + @mock.patch('valence.redfish.redfish.delete_composed_node') @mock.patch('valence.redfish.redfish.get_base_resource_url') @mock.patch('valence.redfish.redfish.send_request') def test_assemble_node_success(self, mock_request, mock_get_url,