Implement storage service discovery

Add the discovery of pod manager storage services
to the rsd-lib suhsy extension.

Change-Id: Ie824e18e6121f8822254a218ca94b07645b3ff60
Implements-Blueprint: storage-services
This commit is contained in:
Nate Potter 2017-08-08 13:07:37 -07:00
parent 834e77e6d6
commit 3252ab39fb
17 changed files with 836 additions and 0 deletions

View File

@ -17,6 +17,7 @@ import sushy
from sushy.resources import base
from rsd_lib.resources.node import node
from rsd_lib.resources.storage_service import storage_service
class RSDLib(sushy.Sushy):
@ -24,6 +25,10 @@ class RSDLib(sushy.Sushy):
_nodes_path = base.Field(['Nodes', '@odata.id'], required=True)
"""NodeCollection path"""
_storage_service_path = base.Field(['Services',
'@odata.id'], required=True)
"""StorageServiceCollection path"""
def get_node_collection(self):
"""Get the NodeCollection object
@ -42,3 +47,24 @@ class RSDLib(sushy.Sushy):
"""
return node.Node(self._conn, identity,
redfish_version=self.redfish_version)
def get_storage_service_collection(self):
"""Get the StorageServiceCollection object
:raises: MissingAttributeError, if the collection attribute is
not found
:returns: a StorageServiceCollection object
"""
return storage_service.StorageServiceCollection(
self._conn, self._storage_service_path,
redfish_version=self.redfish_version)
def get_storage_service(self, identity):
"""Given the identity return a StorageService object
:param identity: The identity of the StorageService resource
:returns: The StorageService object
"""
return storage_service.StorageService(
self._conn, identity,
redfish_version=self.redfish_version)

View File

@ -0,0 +1,76 @@
# 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 logging
from sushy.resources import base
LOG = logging.getLogger(__name__)
class LogicalDrive(base.ResourceBase):
identity = base.Field('Id', required=True)
"""The logical drive identity string"""
drive_type = base.Field('Type')
"""Type of logical drive"""
mode = base.Field('Mode')
"""Drive mode - for Type=='LVM' only supported mode is 'LV'"""
protected = base.Field('Protected')
"""If logical drive is protected"""
capacity_gib = base.Field('CapacityGiB')
"""Logical drive capacity in GiB"""
image = base.Field('Image')
"""Any name that identifies the content of image copied to this LV"""
bootable = base.Field('Bootable')
"""If the LV contains a bootable operating system"""
snapshot = base.Field('Snapshot')
"""Type of drive replication. Yes - copy on write, No - disc clone"""
def __init__(self, connector, identity, redfish_version=None):
"""A class representing a LogicalDrive
:param connector: A Connector instance
:param identity: The identity of the LogicalDrive resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(LogicalDrive, self).__init__(connector, identity,
redfish_version)
class LogicalDriveCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return LogicalDrive
def __init__(self, connector, path, redfish_version=None):
"""A class representing a LogicalDriveCollection
:param connector: A Connector instance
:param path: The canonical path to the LogicalDrive collection resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(LogicalDriveCollection, self).__init__(connector, path,
redfish_version)

View File

@ -0,0 +1,77 @@
# 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 logging
from sushy.resources import base
LOG = logging.getLogger(__name__)
class PhysicalDrive(base.ResourceBase):
identity = base.Field('Id', required=True)
"""The physical drive identity string"""
interface = base.Field('Interface')
"""The interface for the physical drive"""
capacity_gib = base.Field('CapacityGiB')
"""Physical drive capacity in GiB"""
drive_type = base.Field('Type')
"""Type of physical drive"""
rpm = base.Field('RPM')
"""The RPM of this physical drive"""
manufacturer = base.Field('Manufacture')
"""The manufacturer of the physical drive"""
model = base.Field('Model')
"""The model of this physical drive"""
serial_number = base.Field('SerialNumber')
"""The serial number for the physical drive"""
def __init__(self, connector, identity, redfish_version=None):
"""A class representing a PhysicalDrive
:param connector: A Connector instance
:param identity: The identity of the PhysicalDrive resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(PhysicalDrive, self).__init__(connector, identity,
redfish_version)
class PhysicalDriveCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return PhysicalDrive
def __init__(self, connector, path, redfish_version=None):
"""A class representing a PhysicalDriveCollection
:param connector: A Connector instance
:param path: The canonical path to the PhysicalDrive collection
resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(PhysicalDriveCollection, self).__init__(connector, path,
redfish_version)

View File

@ -0,0 +1,62 @@
# 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 logging
from sushy.resources import base
LOG = logging.getLogger(__name__)
class RemoteTarget(base.ResourceBase):
identity = base.Field('Id', required=True)
"""The target identity string"""
target_type = base.Field('Type')
"""Type of target"""
addresses = base.Field('Addresses')
initiator = base.Field('Initiator')
def __init__(self, connector, identity, redfish_version=None):
"""A class representing a RemoteTarget
:param connector: A Connector instance
:param identity: The identity of the RemoteTarget resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(RemoteTarget, self).__init__(connector, identity,
redfish_version)
class RemoteTargetCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return RemoteTarget
def __init__(self, connector, path, redfish_version=None):
"""A class representing a RemoteTargetCollection
:param connector: A Connector instance
:param path: The canonical path to the RemoteTarget collection resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(RemoteTargetCollection, self).__init__(connector, path,
redfish_version)

View File

@ -0,0 +1,145 @@
# 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 logging
from sushy import exceptions
from sushy.resources import base
from rsd_lib.resources.storage_service import logical_drive
from rsd_lib.resources.storage_service import physical_drive
from rsd_lib.resources.storage_service import remote_target
LOG = logging.getLogger(__name__)
class StorageService(base.ResourceBase):
description = base.Field('Description')
"""The storage service description"""
identity = base.Field('Id', required=True)
"""The storage service identity string"""
name = base.Field('Name')
"""The storage service name"""
_logical_drives = None # ref to LogicalDriveCollection instance
_physical_drives = None # ref to PhysicalDrivesCollection instance
_remote_targets = None # ref to RemoteTargetCollection instance
def __init__(self, connector, identity, redfish_version=None):
"""A class representing a StorageService
:param connector: A Connector instance
:param identity: The identity of the StorageService resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(StorageService, self).__init__(connector, identity,
redfish_version)
def _get_logical_drive_collection_path(self):
"""Helper function to find the LogicalDriveCollection path"""
logical_drive_col = self.json.get('LogicalDrives')
if not logical_drive_col:
raise exceptions.MissingAttributeError(attribute='LogicalDrives',
resource=self._path)
return logical_drive_col.get('@odata.id')
@property
def logical_drives(self):
"""Property to provide reference to `LogicalDriveCollection` instance
It is calculated once when it is queried for the first time. On
refresh, this property is reset.
"""
if self._logical_drives is None:
self._logical_drives = logical_drive.LogicalDriveCollection(
self._conn, self._get_logical_drive_collection_path(),
redfish_version=self.redfish_version)
return self._logical_drives
def _get_physical_drive_collection_path(self):
"""Helper function to find the PhysicalDriveCollection path"""
physical_drive_col = self.json.get('Drives')
if not physical_drive_col:
raise exceptions.MissingAttributeError(attribute='PhysicalDrives',
resource=self._path)
return physical_drive_col.get('@odata.id')
@property
def physical_drives(self):
"""Property to provide reference to `PhysicalDriveCollection` instance
It is calculated once when it is queried for the first time. On
refresh, this property is reset.
"""
if self._physical_drives is None:
self._physical_drives = physical_drive.PhysicalDriveCollection(
self._conn, self._get_physical_drive_collection_path(),
redfish_version=self.redfish_version)
return self._physical_drives
def _get_remote_target_collection_path(self):
"""Helper function to find the RemoteTargetCollection path"""
remote_target_col = self.json.get('RemoteTargets')
if not remote_target_col:
raise exceptions.MissingAttributeError(attribute='RemoteTargets',
resource=self._path)
return remote_target_col.get('@odata.id')
@property
def remote_targets(self):
"""Property to provide reference to `RemoteTargetCollection` instance
It is calculated once when it is queried for the first time. On
refresh, this property is reset.
"""
if self._remote_targets is None:
self._remote_targets = remote_target.RemoteTargetCollection(
self._conn, self._get_remote_target_collection_path(),
redfish_version=self.redfish_version)
return self._remote_targets
def refresh(self):
super(StorageService, self).refresh()
self._logical_drives = None
self._physical_drives = None
self._remote_targets = None
class StorageServiceCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return StorageService
def __init__(self, connector, path, redfish_version=None):
"""A class representing a StorageServiceCollection
:param connector: A Connector instance
:param path: The canonical path to the StorageService collection
resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(StorageServiceCollection, self).__init__(connector, path,
redfish_version)

View File

@ -0,0 +1,43 @@
{
"@odata.context": "/redfish/v1/$metadata#LogicalDrives/Links/Members/$entity",
"@odata.id": "/redfish/v1/Services/1/LogicalDrives/1",
"@odata.type": "#LogicalDrive.LogicalDrive",
"Id": "1",
"Name": "Logical Drive",
"Description": "Logical Drive",
"Status": {
"State": "Enabled",
"Health": "OK",
},
"Type": "LVM",
"Mode": "RAID0",
"Protected": false,
"CapacityGiB": 8096,
"Image": "Ubuntu 12.04.4LTS / Linux 3.11 / 2014.1",
"Bootable": true,
"Snapshot": false,
"Oem": {},
"Links": {
"LogicalDrives": [
],
"PhysicalDrives": [
{
"@odata.id": "/redfish/v1/Services/1/Drives/2"
}
],
"MasterDrive": {
"@odata.id": "/redfish/v1/Services/1/LogicalDrives/12"
},
"UsedBy": [
{
"@odata.id": "/redfish/v1/Services/1/LogicalDrives/14"
}
],
"Targets": [
{
"@odata.id": "/redfish/v1/Services/1/Targets/2"
}
],
"Oem": {}
}
}

View File

@ -0,0 +1,12 @@
{
"@odata.context": "/redfish/v1/$metadata#LogicalDrives",
"@odata.id": "/redfish/v1/Services/1/LogicalDrives",
"@odata.type": "#LogicalDriveCollection.LogicalDriveCollection",
"Name": "Logical Drives Collection",
"Members@odata.count": 1,
"Members": [
{
"@odata.id": "/redfish/v1/Services/1/LogicalDrives/1"
}
]
}

View File

@ -0,0 +1,28 @@
{
"@odata.context": "/redfish/v1/$metadata#Drive/Links/Members/$entity",
"@odata.id": "/redfish/v1/Services/1/Drives/1",
"@odata.type": "#PhysicalDrive.v1_0_0.PhysicalDrive",
"Id": "1","Name": "Simple drive"
"Description": "Physical drive"
"Interface": < { "PCIe", "SAS", "SATA" } >
"CapacityGiB": 500,
"Type": < { "HDD", "SSD" } >,
"RPM": 0,
"Manufacturer": "Intel",
"Model": "S3710",
"SerialNumber": "XYZ123456789",
"Status": {
"State": < { "Enabled", "Disabled", "Offline", "InTest", "Starting", "Absent" } >,
"Health": < {"OK", "Warning", "Critical" } >,
"HealthRollup": < {"OK", "Warning", "Critical" } >
},
"Oem": {},
"Links": {
"UsedBy": [
{
"@odata.id": "/redfish/v1/Services/1/LogicalDrives/1"
}
],
"Oem": {}
}
}

View File

@ -0,0 +1,12 @@
{
"@odata.context": "/redfish/v1/$metadata#Drives",
"@odata.id": "/redfish/v1/Services/1/Drives",
"@odata.type": "#PhysicalDriveCollection.PhysicalDriveCollection",
"Name": "Physical Drives Collection",
"Members@odata.count": 1,
"Members": [
{
"@odata.id": "/redfish/v1/Services/1/Drives/1"
}
]
}

View File

@ -0,0 +1,46 @@
{
"@odata.context": "/redfish/v1/$metadata#RemoteTargets/Links/Members/$entity",
"@odata.id": "/redfish/v1/Services/1/Targets/1",
"@odata.type": "#RemoteTarget.v1_1_0.RemoteTarget",
"Id": "1",
"Name": "Remote Target",
"Description": "Remote Target",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Type": "Network Storage",
"Addresses": [
{
"iSCSI":
{
"TargetLUN": [
{
"LUN" : 1,
"LogicalDrive": "/redfish/v1/Services/1/LogicalDrives/1"
}
],
"TargetIQN": "iqn.2015-01.com.example:ceph-ubuntu14",
"TargetPortalIP": "10.102.44.54",
"TargetPortalPort": 3260,
"CHAP": {
"Type": "Mutual",
"Username": "valid_user",
"Secret": null,
"MutualUsername": "user2",
"MutualSecret": null
}
}
}
],
"Initiator": [
{
"iSCSI":
{
"InitiatorIQN": "iqn.2015-01.com.example:fedora21"
}
}
],
"Oem": {},
"Links": {}
}

View File

@ -0,0 +1,12 @@
{
"@odata.context": "/redfish/v1/$metadata#RemoteTargets",
"@odata.id": "/redfish/v1/Services/1/Targets",
"@odata.type": "#RemoteTargetCollection.RemoteTargetCollection",
"Name": "Remote Targets Collection",
"Members@odata.count": 1,
"Members": [
{
"@odata.id": "/redfish/v1/Services/1/Targets/1"
}
]
}

View File

@ -7,6 +7,9 @@
"Systems": {
"@odata.id": "/redfish/v1/Systems"
},
"Services": {
"@odata.id": "/redfish/v1/Services"
},
"Chassis": {
"@odata.id": "/redfish/v1/Chassis"
},

View File

@ -0,0 +1,30 @@
{
"@odata.context": "/redfish/v1/$metadata#Services/Members/1/$entity",
"@odata.id": "/redfish/v1/Services/RSS1",
"@odata.type": "#StorageService.v1 0 0.StorageService",
"Id": "RSS1",
"Name": "Storage Service",
"Description": "Storage Service",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"RemoteTargets": {
"@odata.id": "/redfish/v1/Services/RSS1/Targets"
},
"LogicalDrives": {
"@odata.id": "/redfish/v1/Services/RSS1/LogicalDrives"
},
"Drives": {
"@odata.id": "/redfish/v1/Services/RSS1/Drives"
},
"Oem": {},
"Links": {
"ManagedBy": [
{
"@odata.id": "/redfish/v1/Managers/RSS"
}
],
"Oem": {}
}
}

View File

@ -0,0 +1,13 @@
{
"@odata.context": "/redfish/v1/$metadata#StorageServices",
"@odata.id": "/redfish/v1/Services",
"@odata.type": "#StorageServiceCollection.StorageServiceCollection",
"Name": "Storage Services Collection",
"Description": "Collection of RSD Storage Services",
"Members@odata.count": 1,
"Members": [
{
"@odata.id": "/redfish/v1/Services/RSS1"
}
]
}

View File

@ -0,0 +1,251 @@
# 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 json
import mock
from sushy import exceptions
import testtools
from rsd_lib.resources.storage_service import logical_drive
from rsd_lib.resources.storage_service import physical_drive
from rsd_lib.resources.storage_service import remote_target
from rsd_lib.resources.storage_service import storage_service
class StorageServiceTestCase(testtools.TestCase):
def setUp(self):
super(StorageServiceTestCase, self).setUp()
self.conn = mock.Mock()
with open('rsd_lib/tests/unit/json_samples/storage_service.json',
'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.storage_service_inst = storage_service.StorageService(
self.conn, '/redfish/v1/Nodes/RSS1',
redfish_version='1.0.2')
def test__parse_attributes(self):
self.storage_service_inst._parse_attributes()
self.assertEqual('1.0.2', self.storage_service_inst.redfish_version)
self.assertEqual('Storage Service',
self.storage_service_inst.description)
self.assertEqual('RSS1', self.storage_service_inst.identity)
self.assertEqual('Storage Service', self.storage_service_inst.name)
self.assertIsNone(self.storage_service_inst._logical_drives)
self.assertIsNone(self.storage_service_inst._physical_drives)
self.assertIsNone(self.storage_service_inst._remote_targets)
def test__get_logical_drive_collection_path_missing_processors_attr(self):
self.storage_service_inst._json.pop('LogicalDrives')
self.assertRaisesRegex(
exceptions.MissingAttributeError, 'attribute LogicalDrives',
self.storage_service_inst._get_logical_drive_collection_path)
def test_logical_drives(self):
# check for the underneath variable value
self.assertIsNone(self.storage_service_inst._logical_drives)
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open('rsd_lib/tests/unit/json_samples/'
'logical_drive_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_logical_drives = self.storage_service_inst.logical_drives
# | THEN |
self.assertIsInstance(actual_logical_drives,
logical_drive.LogicalDriveCollection)
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_logical_drives,
self.storage_service_inst.logical_drives)
self.conn.get.return_value.json.assert_not_called()
def test_logical_drives_on_refresh(self):
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/'
'logical_drive_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.storage_service_inst.logical_drives,
logical_drive.LogicalDriveCollection)
# On refreshing the storage service instance...
with open('rsd_lib/tests/unit/json_samples/'
'storage_service.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.storage_service_inst.refresh()
# | WHEN & THEN |
self.assertIsNone(self.storage_service_inst._logical_drives)
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/'
'logical_drive_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.storage_service_inst.logical_drives,
logical_drive.LogicalDriveCollection)
def test__get_physical_drive_collection_path_missing_processors_attr(self):
self.storage_service_inst._json.pop('Drives')
self.assertRaisesRegex(
exceptions.MissingAttributeError, 'attribute PhysicalDrives',
self.storage_service_inst._get_physical_drive_collection_path)
def test_physical_drives(self):
# check for the underneath variable value
self.assertIsNone(self.storage_service_inst._physical_drives)
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open('rsd_lib/tests/unit/json_samples/'
'physical_drive_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_physical_drives = self.storage_service_inst.physical_drives
# | THEN |
self.assertIsInstance(actual_physical_drives,
physical_drive.PhysicalDriveCollection)
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_physical_drives,
self.storage_service_inst.physical_drives)
self.conn.get.return_value.json.assert_not_called()
def test_physical_drives_on_refresh(self):
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/'
'physical_drive_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.storage_service_inst.physical_drives,
physical_drive.PhysicalDriveCollection)
# On refreshing the storage service instance...
with open('rsd_lib/tests/unit/json_samples/'
'storage_service.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.storage_service_inst.refresh()
# | WHEN & THEN |
self.assertIsNone(self.storage_service_inst._physical_drives)
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/'
'physical_drive_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.storage_service_inst.physical_drives,
physical_drive.PhysicalDriveCollection)
def test__get_remote_target_collection_path_missing_processors_attr(self):
self.storage_service_inst._json.pop('RemoteTargets')
self.assertRaisesRegex(
exceptions.MissingAttributeError, 'attribute RemoteTargets',
self.storage_service_inst._get_remote_target_collection_path)
def test_remote_targets(self):
# check for the underneath variable value
self.assertIsNone(self.storage_service_inst._remote_targets)
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open('rsd_lib/tests/unit/json_samples/'
'remote_target_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_remote_targets = self.storage_service_inst.remote_targets
# | THEN |
self.assertIsInstance(actual_remote_targets,
remote_target.RemoteTargetCollection)
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_remote_targets,
self.storage_service_inst.remote_targets)
self.conn.get.return_value.json.assert_not_called()
def test_remote_targets_on_refresh(self):
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/'
'remote_target_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.storage_service_inst.remote_targets,
remote_target.RemoteTargetCollection)
# On refreshing the storage service instance...
with open('rsd_lib/tests/unit/json_samples/'
'storage_service.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.storage_service_inst.refresh()
# | WHEN & THEN |
self.assertIsNone(self.storage_service_inst._remote_targets)
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/'
'remote_target_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(self.storage_service_inst.remote_targets,
remote_target.RemoteTargetCollection)
class StorageServiceCollectionTestCase(testtools.TestCase):
def setUp(self):
super(StorageServiceCollectionTestCase, self).setUp()
self.conn = mock.Mock()
with open('rsd_lib/tests/unit/json_samples/'
'storage_service_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.storage_service_col = storage_service.StorageServiceCollection(
self.conn, '/redfish/v1/Services', redfish_version='1.0.2')
def test__parse_attributes(self):
self.storage_service_col._parse_attributes()
self.assertEqual('1.0.2', self.storage_service_col.redfish_version)
self.assertEqual('Storage Services Collection',
self.storage_service_col.name)
self.assertEqual(('/redfish/v1/Services/RSS1',),
self.storage_service_col.members_identities)
@mock.patch.object(storage_service, 'StorageService', autospec=True)
def test_get_member(self, mock_storage_service):
self.storage_service_col.get_member('/redfish/v1/Services/RSS1')
mock_storage_service.assert_called_once_with(
self.storage_service_col._conn, '/redfish/v1/Services/RSS1',
redfish_version=self.storage_service_col.redfish_version)
@mock.patch.object(storage_service, 'StorageService', autospec=True)
def test_get_members(self, mock_storage_service):
members = self.storage_service_col.get_members()
mock_storage_service.assert_called_once_with(
self.storage_service_col._conn, '/redfish/v1/Services/RSS1',
redfish_version=self.storage_service_col.redfish_version)
self.assertIsInstance(members, list)
self.assertEqual(1, len(members))