Add show composed node detail command

Add new command 'rsd node show' to allow user to get composed node
details by specifying node ID.

Change-Id: I26e4adbbf18519742cc215371e8f86fbb4b7d93e
This commit is contained in:
Lin Yang 2017-08-21 17:21:25 -07:00
parent 6e9aeaaa00
commit a69faf00c4
9 changed files with 185 additions and 3 deletions

View File

@ -1,4 +1,4 @@
# Copyright 2017 99cloud, Inc.
# Copyright 2017 Intel, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

43
rsdclient/common/utils.py Normal file
View File

@ -0,0 +1,43 @@
# Copyright 2017 Intel, Inc.
# All Rights Reserved.
#
# 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
def extract_attr(redfish_obj):
'''Extract all public attributes of python redfish object
:param redfish_obj: python redfish object returned by rsd_lib
:returns: python dict of that object
'''
if isinstance(redfish_obj, (int, six.string_types)):
return redfish_obj
if isinstance(redfish_obj, list):
return [extract_attr(i) for i in redfish_obj]
if isinstance(redfish_obj, dict):
return {i: extract_attr(redfish_obj[i]) for i in redfish_obj}
result = {}
try:
for key, value in vars(redfish_obj).items():
# Skip all private attributes
if key.startswith('_'):
continue
result[key] = extract_attr(value)
except TypeError:
return None
return result

View File

@ -13,6 +13,8 @@
# under the License.
#
import json
from osc_lib.command import command
@ -59,3 +61,22 @@ class DeleteNode(command.Command):
for node in parsed_args.node:
rsd_client.node.delete(node)
print("Node {0} has been deleted.".format(node))
class ShowNode(command.Command):
_description = "Display node details"
def get_parser(self, prog_name):
parser = super(ShowNode, self).get_parser(prog_name)
parser.add_argument(
'node',
metavar='<node>',
help='ID of the node.')
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
rsd_client = self.app.client_manager.rsd
node_detail = rsd_client.node.show(parsed_args.node)
print("{0}".format(json.dumps(node_detail, indent=2)))

View File

View File

@ -0,0 +1,77 @@
# Copyright 2017 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.
#
FAKE_NODE_PYTHON_DICT = {
"description": "Node for testing",
"processor_summary": {
"count": 1,
"model": "fake processor model",
"health": "OK"
},
"composed_node_state": "allocated",
"boot": {
"mode": "fake boot mode",
"enabled": "once",
"target": "pxe",
"allowed_values": ["pxe", "hdd"]
},
"uuid": "fd011520-86a2-11e7-b4d4-5d323196a3e4",
"power_state": "on",
"memory_summary": {
"size_gib": 8,
"health": "OK"
},
"identity": "1",
"name": "Test"
}
class FakeProcessorSummary(object):
def __init__(self):
self.count = 1
self.model = "fake processor model"
self.health = "OK"
class FakeBoot(object):
def __init__(self):
self.mode = "fake boot mode"
self.enabled = "once"
self.target = "pxe"
self.allowed_values = ["pxe", "hdd"]
class FakeMemorySummary(object):
def __init__(self):
self.size_gib = 8
self.health = "OK"
class FakeNode(object):
def __init__(self):
self.name = "Test"
self.description = "Node for testing"
self.identity = "1"
self.power_state = "on"
self.composed_node_state = "allocated"
self.boot = FakeBoot()
self.processor_summary = FakeProcessorSummary()
self.memory_summary = FakeMemorySummary()
self.uuid = "fd011520-86a2-11e7-b4d4-5d323196a3e4"

View File

@ -0,0 +1,28 @@
# Copyright 2017 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 testtools
from rsdclient.common import utils
from rsdclient.tests.common import fakes
class UtilsTest(testtools.TestCase):
def test_compose_node(self):
fake_node = fakes.FakeNode()
result = utils.extract_attr(fake_node)
expected = fakes.FAKE_NODE_PYTHON_DICT
self.assertEqual(result, expected)

View File

@ -16,13 +16,14 @@
import mock
import testtools
from rsdclient.tests.common import fakes
from rsdclient.v1 import node
class ClusterManagerTest(testtools.TestCase):
class NodeTest(testtools.TestCase):
def setUp(self):
super(ClusterManagerTest, self).setUp()
super(NodeTest, self).setUp()
self.client = mock.Mock()
self.client._nodes_path = '/redfish/v1/Nodes'
self.mgr = node.NodeManager(self.client)
@ -44,3 +45,9 @@ class ClusterManagerTest(testtools.TestCase):
self.mgr.delete(node_id)
self.mgr.client.get_node.assert_called_once_with('/redfish/v1/Nodes/1')
mock_node.delete_node.assert_called_once()
def test_show_node(self):
self.client.get_node.return_value = fakes.FakeNode()
result = self.mgr.show('1')
expected = fakes.FAKE_NODE_PYTHON_DICT
self.assertEqual(result, expected)

View File

@ -16,6 +16,7 @@
import os
from rsdclient.common import base
from rsdclient.common import utils
class NodeManager(base.Manager):
@ -35,3 +36,7 @@ class NodeManager(base.Manager):
def delete(self, node_id):
self.client.get_node(self._get_node_uri(node_id)).delete_node()
def show(self, node_id):
node = self.client.get_node(self._get_node_uri(node_id))
return utils.extract_attr(node)

View File

@ -30,6 +30,7 @@ openstack.cli.extension =
openstack.rsd.v1 =
rsd_node_compose = rsdclient.osc.v1.node:ComposeNode
rsd_node_delete = rsdclient.osc.v1.node:DeleteNode
rsd_node_show = rsdclient.osc.v1.node:ShowNode
[build_sphinx]
all-files = 1