Add FieldList class to resource base

Add a class capable of handling lists of fields in resource
body JSON. This is a general purpose class, and is being added
for use in the rsd-lib sushy extension as there are JSON responses
such as {"Initiator": [{"iSCSI": 1}, {"iSCSI": 2}]} that need to
be handled.

Change-Id: Ia5976d8646162e8558dc63584f89e4a3cabbf1f2
This commit is contained in:
Nate Potter 2017-09-01 11:46:51 -07:00
parent 6093a87aca
commit 262f96e160
2 changed files with 146 additions and 0 deletions

48
rsd_lib/resources/base.py Normal file
View File

@ -0,0 +1,48 @@
# 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 copy
from sushy.resources import base
class FieldList(base.CompositeField):
"""Base class for fields consisting of a list of several sub-fields."""
def _load(self, body, resource, nested_in=None):
"""Load the field list.
:param body: parent JSON body.
:param resource: parent resource.
:param nested_in: parent resource name (for error reporting only).
:returns: a new list object containing subfields.
"""
nested_in = (nested_in or []) + self._path
values = base.Field._load(self, body, resource)
if values is None:
return None
# Initialize the list that will contain each field instance
instances = []
for value in values:
instance = copy.copy(self)
for attr, field in self._subfields.items():
# Hide the Field object behind the real value
setattr(instance, attr, field._load(value,
resource,
nested_in))
instances.append(instance)
return instances

View File

@ -0,0 +1,98 @@
# 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 copy
import mock
from sushy.resources import base as resource_base
from sushy.tests.unit import base
from rsd_lib.resources import base as rsd_resource_base
TEST_JSON = {
'String': 'a string',
'Integer': '42',
'List': ['a string', 42],
'Nested': {
'String': 'another string',
'Integer': 0,
'Object': {
'Field': 'field value'
},
'Mapped': 'raw'
},
'FieldList': [
{
'String': 'a third string',
'Integer': 1
},
{
'String': 'a fourth string',
'Integer': 2
}
]
}
MAPPING = {
'raw': 'real'
}
class NestedTestField(resource_base.CompositeField):
string = resource_base.Field('String', required=True)
integer = resource_base.Field('Integer', adapter=int)
nested_field = resource_base.Field(['Object', 'Field'], required=True)
mapped = resource_base.MappedField('Mapped', MAPPING)
non_existing = resource_base.Field('NonExisting', default=3.14)
class TestFieldList(rsd_resource_base.FieldList):
string = resource_base.Field('String')
integer = resource_base.Field('Integer')
class ComplexResource(resource_base.ResourceBase):
string = resource_base.Field('String', required=True)
integer = resource_base.Field('Integer', adapter=int)
nested = NestedTestField('Nested')
field_list = TestFieldList('FieldList')
non_existing_nested = NestedTestField('NonExistingNested')
non_existing_mapped = resource_base.MappedField('NonExistingMapped',
MAPPING)
class FieldTestCase(base.TestCase):
def setUp(self):
super(FieldTestCase, self).setUp()
self.conn = mock.Mock()
self.json = copy.deepcopy(TEST_JSON)
self.conn.get.return_value.json.return_value = self.json
self.test_resource = ComplexResource(self.conn,
redfish_version='1.0.x')
def test_ok(self):
self.assertEqual('a string', self.test_resource.string)
self.assertEqual(42, self.test_resource.integer)
self.assertEqual('another string', self.test_resource.nested.string)
self.assertEqual(0, self.test_resource.nested.integer)
self.assertEqual('field value', self.test_resource.nested.nested_field)
self.assertEqual('real', self.test_resource.nested.mapped)
self.assertEqual(3.14, self.test_resource.nested.non_existing)
self.assertEqual('a third string',
self.test_resource.field_list[0].string)
self.assertEqual(2, self.test_resource.field_list[1].integer)
self.assertIsNone(self.test_resource.non_existing_nested)
self.assertIsNone(self.test_resource.non_existing_mapped)