Add CapacitySources class in RSD 2.4

Change-Id: I049ce331a5a2f430dca4a5e9f56e11dcbfc4fa45
This commit is contained in:
Lin Yang 2019-05-28 17:03:56 -07:00
parent 171b075a92
commit aeacfae878
6 changed files with 546 additions and 77 deletions

View File

@ -0,0 +1,132 @@
# Copyright 2019 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.
from sushy.resources import base
from sushy import utils
from rsd_lib import base as rsd_lib_base
from rsd_lib.resources.v2_3.storage_service import drive
from rsd_lib.resources.v2_3.storage_service import storage_pool
from rsd_lib import utils as rsd_lib_utils
class CapacityInfoField(base.CompositeField):
"""CapacityInfo field
The capacity of specific data type in a data store.
"""
consumed_bytes = base.Field(
"ConsumedBytes", adapter=rsd_lib_utils.num_or_none
)
"""The number of bytes consumed in this data store for this data type."""
allocated_bytes = base.Field(
"AllocatedBytes", adapter=rsd_lib_utils.num_or_none
)
"""The number of bytes currently allocated by the storage system in this
data store for this data type.
"""
guaranteed_bytes = base.Field(
"GuaranteedBytes", adapter=rsd_lib_utils.num_or_none
)
"""The number of bytes the storage system guarantees can be allocated in
this data store for this data type.
"""
provisioned_bytes = base.Field(
"ProvisionedBytes", adapter=rsd_lib_utils.num_or_none
)
"""The maximum number of bytes that can be allocated in this data store
for this data type.
"""
class CapacityField(base.CompositeField):
"""Capacity field
This is the schema definition for the Capacity of a device. It
represents the properties for capacity for any data store.
"""
data = CapacityInfoField("Data")
"""The capacity information relating to the user data."""
metadata = CapacityInfoField("Metadata")
"""The capacity information relating to metadata."""
snapshot = CapacityInfoField("Snapshot")
"""The capacity information relating to snapshot or backup data."""
is_thin_provisioned = base.Field("IsThinProvisioned", adapter=bool)
"""Marks that the capacity is not necessarily fully allocated."""
class CapacitySource(rsd_lib_base.ResourceBase):
"""CapacitySource resource class
A description of the type and source of storage.
"""
provided_capacity = CapacityField("ProvidedCapacity")
"""The amount of space that has been provided from the ProvidingDrives,
ProvidingVolumes, ProvidingMemory or ProvidingPools.
"""
# TODO(lin.yang): Add property for references in CapacitySource resource
@property
@utils.cache_it
def providing_volumes(self):
"""Property to provide reference to `VolumeCollection` instance
It is calculated once when it is queried for the first time. On
refresh, this property is reset.
"""
from rsd_lib.resources.v2_4.storage_service import volume
return volume.VolumeCollection(
self._conn,
utils.get_sub_resource_path_by(self, "ProvidingVolumes"),
redfish_version=self.redfish_version,
)
@property
@utils.cache_it
def providing_pools(self):
"""Property to provide reference to `StoragePoolCollection` instance
It is calculated once when it is queried for the first time. On
refresh, this property is reset.
"""
return storage_pool.StoragePoolCollection(
self._conn,
utils.get_sub_resource_path_by(self, "ProvidingPools"),
redfish_version=self.redfish_version,
)
@property
@utils.cache_it
def providing_drives(self):
"""Property to provide reference to `DriveCollection` instance
It is calculated once when it is queried for the first time. On
refresh, this property is reset.
"""
return drive.DriveCollection(
self._conn,
utils.get_sub_resource_path_by(self, "ProvidingDrives"),
redfish_version=self.redfish_version,
)

View File

@ -14,11 +14,29 @@
# under the License.
from sushy import exceptions
from sushy import utils
from rsd_lib.resources.v2_3.storage_service import volume
from rsd_lib.resources.v2_4.storage_service import capacity
class Volume(volume.Volume):
@property
@utils.cache_it
def capacity_sources(self):
"""Property to provide a list of `CapacitySource` instance
It is calculated once when it is queried for the first time. On
refresh, this property is reset.
"""
return [
capacity.CapacitySource(
self._conn, path, redfish_version=self.redfish_version
)
for path in utils.get_sub_resource_path_by(
self, "CapacitySources", is_collection=True
)
]
def resize(self, num_bytes):
"""Update volume properties
@ -27,20 +45,21 @@ class Volume(volume.Volume):
"""
if not isinstance(num_bytes, int):
raise exceptions.InvalidParameterValueError(
parameter='num_bytes', value=num_bytes,
valid_values='integer')
parameter="num_bytes", value=num_bytes, valid_values="integer"
)
if self.capacity_bytes and num_bytes <= self.capacity_bytes:
raise exceptions.InvalidParameterValueError(
parameter='num_bytes', value=num_bytes,
valid_values='> {0}'.format(self.capacity_bytes))
parameter="num_bytes",
value=num_bytes,
valid_values="> {0}".format(self.capacity_bytes),
)
data = {"Capacity": {"Data": {'AllocatedBytes': num_bytes}}}
data = {"Capacity": {"Data": {"AllocatedBytes": num_bytes}}}
self._conn.patch(self.path, data=data)
class VolumeCollection(volume.VolumeCollection):
@property
def _resource_type(self):
return Volume

View File

@ -0,0 +1,28 @@
{
"@odata.context": "/redfish/v1/$metadata#Capacity.CapacitySource",
"@odata.id": "/redfish/v1/StorageServices/1/Volumes/1/CapacitySources/1",
"@odata.type": "#Capacity.v1_1_0.CapacitySource",
"Description": "Volume capacity source",
"Id": "1",
"Name": "CapacitySource",
"ProvidingPools": {
"@odata.id": "/redfish/v1/StorageServices/1/Volumes/1/CapacitySources/1/ProvidingPools"
},
"ProvidingVolumes": {
"@odata.id": "/redfish/v1/StorageServices/1/Volumes/1/CapacitySources/1/ProvidingVolumes"
},
"ProvidingDrives": {
"@odata.id": "/redfish/v1/StorageServices/1/Volumes/1/CapacitySources/1/ProvidingDrives"
},
"ProvidedCapacity": {
"Data": {
"AllocatedBytes": 3071983104
}
},
"Oem": {},
"Status": {
"Health": "OK",
"HealthRollup": "OK",
"State": "Enabled"
}
}

View File

@ -26,19 +26,10 @@
},
"CapacitySources": [
{
"ProvidingPools": [
{
"@odata.id": "/redfish/v1/StorageServices/1/StoragePools/2"
}
],
"ProvidedCapacity": {
"Data": {
"AllocatedBytes": 3071983104
}
}
"@odata.id": "/redfish/v1/StorageServices/NVMeoE1/Volumes/1/CapacitySources/1"
}
],
"Identifiers": [
"Identifiers": [
{
"DurableName": "/dev/nvme1n1p1",
"DurableNameFormat": "SystemPath"

View File

@ -0,0 +1,224 @@
# Copyright 2018 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 json
import mock
import testtools
from rsd_lib.resources.v2_3.storage_service import drive
from rsd_lib.resources.v2_3.storage_service import storage_pool
from rsd_lib.resources.v2_4.storage_service import capacity
from rsd_lib.resources.v2_4.storage_service import volume
class CapacitySourceTestCase(testtools.TestCase):
def setUp(self):
super(CapacitySourceTestCase, self).setUp()
self.conn = mock.Mock()
with open(
"rsd_lib/tests/unit/json_samples/v2_4/capacity_sources.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.capacity_sources_inst = capacity.CapacitySource(
self.conn,
"/redfish/v1/StorageServices/1/Volumes/1/CapacitySources/1",
redfish_version="1.0.2",
)
def test__parse_attributes(self):
self.capacity_sources_inst._parse_attributes()
self.assertEqual("1.0.2", self.capacity_sources_inst.redfish_version)
self.assertEqual(
"Volume capacity source", self.capacity_sources_inst.description
)
self.assertEqual("1", self.capacity_sources_inst.identity)
self.assertEqual("CapacitySource", self.capacity_sources_inst.name)
self.assertEqual(
3071983104,
self.capacity_sources_inst.provided_capacity.data.allocated_bytes,
)
def test_providing_volumes(self):
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open(
"rsd_lib/tests/unit/json_samples/v2_4/volume_collection.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_providing_volumes = self.capacity_sources_inst.providing_volumes
# | THEN |
self.assertIsInstance(
actual_providing_volumes, volume.VolumeCollection
)
self.conn.get.return_value.json.assert_called_once_with()
# reset mock
self.conn.get.return_value.json.reset_mock()
# | WHEN & THEN |
# tests for same object on invoking subsequently
self.assertIs(
actual_providing_volumes,
self.capacity_sources_inst.providing_volumes,
)
self.conn.get.return_value.json.assert_not_called()
def test_providing_volumes_on_refresh(self):
# | GIVEN |
with open(
"rsd_lib/tests/unit/json_samples/v2_4/volume_collection.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(
self.capacity_sources_inst.providing_volumes,
volume.VolumeCollection,
)
# On refreshing the chassis instance...
with open(
"rsd_lib/tests/unit/json_samples/v2_4/capacity_sources.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.capacity_sources_inst.invalidate()
self.capacity_sources_inst.refresh(force=False)
# | GIVEN |
with open(
"rsd_lib/tests/unit/json_samples/v2_4/volume_collection.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(
self.capacity_sources_inst.providing_volumes,
volume.VolumeCollection,
)
def test_providing_pools(self):
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open(
"rsd_lib/tests/unit/json_samples/v2_3/"
"storage_pool_collection.json",
"r",
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_providing_pools = self.capacity_sources_inst.providing_pools
# | THEN |
self.assertIsInstance(
actual_providing_pools, storage_pool.StoragePoolCollection
)
self.conn.get.return_value.json.assert_called_once_with()
# reset mock
self.conn.get.return_value.json.reset_mock()
# | WHEN & THEN |
# tests for same object on invoking subsequently
self.assertIs(
actual_providing_pools, self.capacity_sources_inst.providing_pools
)
self.conn.get.return_value.json.assert_not_called()
def test_providing_pools_on_refresh(self):
# | GIVEN |
with open(
"rsd_lib/tests/unit/json_samples/v2_3/"
"storage_pool_collection.json",
"r",
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(
self.capacity_sources_inst.providing_pools,
storage_pool.StoragePoolCollection,
)
# On refreshing the chassis instance...
with open(
"rsd_lib/tests/unit/json_samples/v2_4/capacity_sources.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.capacity_sources_inst.invalidate()
self.capacity_sources_inst.refresh(force=False)
# | GIVEN |
with open(
"rsd_lib/tests/unit/json_samples/v2_3/"
"storage_pool_collection.json",
"r",
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(
self.capacity_sources_inst.providing_pools,
storage_pool.StoragePoolCollection,
)
def test_providing_drives(self):
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open(
"rsd_lib/tests/unit/json_samples/v2_3/drive_collection.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_providing_drives = self.capacity_sources_inst.providing_drives
# | THEN |
self.assertIsInstance(actual_providing_drives, drive.DriveCollection)
self.conn.get.return_value.json.assert_called_once_with()
# reset mock
self.conn.get.return_value.json.reset_mock()
# | WHEN & THEN |
# tests for same object on invoking subsequently
self.assertIs(
actual_providing_drives,
self.capacity_sources_inst.providing_drives,
)
self.conn.get.return_value.json.assert_not_called()
def test_providing_drives_on_refresh(self):
# | GIVEN |
with open(
"rsd_lib/tests/unit/json_samples/v2_3/drive_collection.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(
self.capacity_sources_inst.providing_drives, drive.DriveCollection
)
# On refreshing the chassis instance...
with open(
"rsd_lib/tests/unit/json_samples/v2_4/capacity_sources.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.capacity_sources_inst.invalidate()
self.capacity_sources_inst.refresh(force=False)
# | GIVEN |
with open(
"rsd_lib/tests/unit/json_samples/v2_3/drive_collection.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(
self.capacity_sources_inst.providing_drives, drive.DriveCollection
)

View File

@ -19,125 +19,200 @@ import testtools
from sushy import exceptions
from rsd_lib.resources.v2_4.storage_service import capacity
from rsd_lib.resources.v2_4.storage_service import volume
class StorageServiceTestCase(testtools.TestCase):
def setUp(self):
super(StorageServiceTestCase, self).setUp()
self.conn = mock.Mock()
with open('rsd_lib/tests/unit/json_samples/v2_3/volume.json',
'r') as f:
with open(
"rsd_lib/tests/unit/json_samples/v2_4/volume.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.volume_inst = volume.Volume(
self.conn, '/redfish/v1/StorageServices/NVMeoE1/Volumes/1',
redfish_version='1.0.2')
self.conn,
"/redfish/v1/StorageServices/NVMeoE1/Volumes/1",
redfish_version="1.0.2",
)
def test__parse_attributes(self):
self.volume_inst._parse_attributes()
self.assertEqual('1.0.2', self.volume_inst.redfish_version)
self.assertEqual('Volume description', self.volume_inst.description)
self.assertEqual('1', self.volume_inst.identity)
self.assertEqual('NVMe remote storage', self.volume_inst.name)
self.assertEqual('Enabled', self.volume_inst.status.state)
self.assertEqual('OK', self.volume_inst.status.health)
self.assertEqual('OK', self.volume_inst.status.health_rollup)
self.assertEqual("1.0.2", self.volume_inst.redfish_version)
self.assertEqual("Volume description", self.volume_inst.description)
self.assertEqual("1", self.volume_inst.identity)
self.assertEqual("NVMe remote storage", self.volume_inst.name)
self.assertEqual("Enabled", self.volume_inst.status.state)
self.assertEqual("OK", self.volume_inst.status.health)
self.assertEqual("OK", self.volume_inst.status.health_rollup)
self.assertIsNone(self.volume_inst.model)
self.assertIsNone(self.volume_inst.manufacturer)
self.assertEqual(['Read', 'Write'],
self.volume_inst.access_capabilities)
self.assertEqual(
["Read", "Write"], self.volume_inst.access_capabilities
)
self.assertEqual(3071983104, self.volume_inst.capacity_bytes)
self.assertEqual(3071983104, self.volume_inst.allocated_Bytes)
self.assertEqual(('/redfish/v1/StorageServices/1/StoragePools/2',),
self.volume_inst.capacity_sources[0].providing_pools)
self.assertEqual(3071983104,
self.volume_inst.capacity_sources[0].allocated_Bytes)
self.assertEqual(
'/dev/nvme1n1p1',
self.volume_inst.identifiers[0].durable_name)
"/dev/nvme1n1p1", self.volume_inst.identifiers[0].durable_name
)
self.assertEqual(
'SystemPath',
self.volume_inst.identifiers[0].durable_name_format)
"SystemPath", self.volume_inst.identifiers[0].durable_name_format
)
self.assertEqual(
'iqn.2001-04.com.example:diskarrays-sn-a8675309',
self.volume_inst.identifiers[1].durable_name)
"iqn.2001-04.com.example:diskarrays-sn-a8675309",
self.volume_inst.identifiers[1].durable_name,
)
self.assertEqual(
'iQN',
self.volume_inst.identifiers[1].durable_name_format)
self.assertEqual(('/redfish/v1/Fabrics/NVMeoE/Endpoints/1',),
self.volume_inst.links.endpoints)
"iQN", self.volume_inst.identifiers[1].durable_name_format
)
self.assertEqual(
'/redfish/v1/StorageServices/NVMeoE1/Volumes/1/Metrics',
self.volume_inst.links.metrics)
("/redfish/v1/Fabrics/NVMeoE/Endpoints/1",),
self.volume_inst.links.endpoints,
)
self.assertEqual(
'SourceElement',
self.volume_inst.replica_infos[0].replica_readonly_access)
self.assertEqual('Snapshot',
self.volume_inst.replica_infos[0].replica_type)
self.assertEqual('Target',
self.volume_inst.replica_infos[0].replica_role)
self.assertEqual('/redfish/v1/StorageServices/NVMeoE1/Volumes/2',
self.volume_inst.replica_infos[0].replica)
"/redfish/v1/StorageServices/NVMeoE1/Volumes/1/Metrics",
self.volume_inst.links.metrics,
)
self.assertEqual(
"SourceElement",
self.volume_inst.replica_infos[0].replica_readonly_access,
)
self.assertEqual(
"Snapshot", self.volume_inst.replica_infos[0].replica_type
)
self.assertEqual(
"Target", self.volume_inst.replica_infos[0].replica_role
)
self.assertEqual(
"/redfish/v1/StorageServices/NVMeoE1/Volumes/2",
self.volume_inst.replica_infos[0].replica,
)
self.assertEqual(False, self.volume_inst.bootable)
self.assertIsNone(self.volume_inst.erased)
self.assertEqual(True, self.volume_inst.erase_on_detach)
def test_capacity_sources(self):
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open(
"rsd_lib/tests/unit/json_samples/v2_4/capacity_sources.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_capacity_sources = self.volume_inst.capacity_sources
# | THEN |
self.assertIsInstance(actual_capacity_sources, list)
self.assertIsInstance(
actual_capacity_sources[0], capacity.CapacitySource
)
self.conn.get.return_value.json.assert_called_once_with()
# reset mock
self.conn.get.return_value.json.reset_mock()
# | WHEN & THEN |
# tests for same object on invoking subsequently
self.assertIs(
actual_capacity_sources, self.volume_inst.capacity_sources
)
self.conn.get.return_value.json.assert_not_called()
def test_capacity_sources_on_refresh(self):
# | GIVEN |
with open(
"rsd_lib/tests/unit/json_samples/v2_4/capacity_sources.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.volume_inst.capacity_sources, list)
self.assertIsInstance(
self.volume_inst.capacity_sources[0], capacity.CapacitySource
)
# On refreshing the telemetry service instance...
with open(
"rsd_lib/tests/unit/json_samples/v2_4/volume.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.volume_inst.invalidate()
self.volume_inst.refresh(force=False)
# | GIVEN |
with open(
"rsd_lib/tests/unit/json_samples/v2_4/capacity_sources.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.volume_inst.capacity_sources, list)
self.assertIsInstance(
self.volume_inst.capacity_sources[0], capacity.CapacitySource
)
def test_resize_volume(self):
self.volume_inst.resize(3071983105)
self.volume_inst._conn.patch.assert_called_once_with(
'/redfish/v1/StorageServices/NVMeoE1/Volumes/1',
data={"Capacity": {"Data": {'AllocatedBytes': 3071983105}}})
"/redfish/v1/StorageServices/NVMeoE1/Volumes/1",
data={"Capacity": {"Data": {"AllocatedBytes": 3071983105}}},
)
def test_update_volume_with_invalid_parameter(self):
with self.assertRaisesRegex(
exceptions.InvalidParameterValueError,
'The parameter "num_bytes" value "fake-value" is invalid'):
self.volume_inst.resize('fake-value')
'The parameter "num_bytes" value "fake-value" is invalid',
):
self.volume_inst.resize("fake-value")
with self.assertRaisesRegex(
exceptions.InvalidParameterValueError,
'The parameter "num_bytes" value "1024" is invalid'):
'The parameter "num_bytes" value "1024" is invalid',
):
self.volume_inst.resize(1024)
class VolumeCollectionTestCase(testtools.TestCase):
def setUp(self):
super(VolumeCollectionTestCase, self).setUp()
self.conn = mock.Mock()
with open('rsd_lib/tests/unit/json_samples/v2_4/'
'volume_collection.json', 'r') as f:
with open(
"rsd_lib/tests/unit/json_samples/v2_4/volume_collection.json", "r"
) as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.volume_col = volume.VolumeCollection(
self.conn, '/redfish/v1/StorageServices/NVMeoE1/Volumes',
redfish_version='1.0.2')
self.conn,
"/redfish/v1/StorageServices/NVMeoE1/Volumes",
redfish_version="1.0.2",
)
def test__parse_attributes(self):
self.volume_col._parse_attributes()
self.assertEqual('1.0.2', self.volume_col.redfish_version)
self.assertEqual('Volume Collection',
self.volume_col.name)
self.assertEqual(('/redfish/v1/StorageServices/NVMeoE1/Volumes/1',),
self.volume_col.members_identities)
self.assertEqual("1.0.2", self.volume_col.redfish_version)
self.assertEqual("Volume Collection", self.volume_col.name)
self.assertEqual(
("/redfish/v1/StorageServices/NVMeoE1/Volumes/1",),
self.volume_col.members_identities,
)
@mock.patch.object(volume, 'Volume', autospec=True)
@mock.patch.object(volume, "Volume", autospec=True)
def test_get_member(self, mock_volume):
self.volume_col.get_member(
'/redfish/v1/StorageServices/NVMeoE1/Volumes/1')
"/redfish/v1/StorageServices/NVMeoE1/Volumes/1"
)
mock_volume.assert_called_once_with(
self.volume_col._conn,
'/redfish/v1/StorageServices/NVMeoE1/Volumes/1',
redfish_version=self.volume_col.redfish_version)
"/redfish/v1/StorageServices/NVMeoE1/Volumes/1",
redfish_version=self.volume_col.redfish_version,
)
@mock.patch.object(volume, 'Volume', autospec=True)
@mock.patch.object(volume, "Volume", autospec=True)
def test_get_members(self, mock_volume):
members = self.volume_col.get_members()
mock_volume.assert_called_once_with(
self.volume_col._conn,
'/redfish/v1/StorageServices/NVMeoE1/Volumes/1',
redfish_version=self.volume_col.redfish_version)
"/redfish/v1/StorageServices/NVMeoE1/Volumes/1",
redfish_version=self.volume_col.redfish_version,
)
self.assertIsInstance(members, list)
self.assertEqual(1, len(members))