Add DriveMetrics resource for RSD2.3
Change-Id: I00a162b4ee6405f53abf3f5030a7cbfb5345f075
This commit is contained in:
parent
6c46d04f78
commit
caa3c06add
@ -13,15 +13,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
from sushy.resources import base
|
||||
from sushy import utils
|
||||
|
||||
from rsd_lib.resources.v2_3.storage_service import drive_metrics
|
||||
from rsd_lib import utils as rsd_lib_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LinksField(base.CompositeField):
|
||||
chassis = base.Field('Chassis',
|
||||
@ -151,6 +148,26 @@ class Drive(base.ResourceBase):
|
||||
"""
|
||||
super(Drive, self).__init__(connector, identity, redfish_version)
|
||||
|
||||
def _get_metrics_path(self):
|
||||
"""Helper function to find the Metrics path"""
|
||||
return utils.get_sub_resource_path_by(self,
|
||||
['Links',
|
||||
'Oem',
|
||||
'Intel_RackScale',
|
||||
'Metrics'])
|
||||
|
||||
@property
|
||||
@utils.cache_it
|
||||
def metrics(self):
|
||||
"""Property to provide reference to `Metrics` instance
|
||||
|
||||
It is calculated once when it is queried for the first time. On
|
||||
refresh, this property is reset.
|
||||
"""
|
||||
return drive_metrics.DriveMetrics(
|
||||
self._conn, self._get_metrics_path(),
|
||||
redfish_version=self.redfish_version)
|
||||
|
||||
|
||||
class DriveCollection(base.ResourceCollectionBase):
|
||||
|
||||
|
80
rsd_lib/resources/v2_3/storage_service/drive_metrics.py
Normal file
80
rsd_lib/resources/v2_3/storage_service/drive_metrics.py
Normal file
@ -0,0 +1,80 @@
|
||||
# Copyright (c) 2019 Intel, Corp.
|
||||
#
|
||||
# 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 rsd_lib import utils as rsd_lib_utils
|
||||
|
||||
|
||||
class LifeTimeField(base.CompositeField):
|
||||
unit_size_bytes = base.Field('UnitSizeBytes',
|
||||
adapter=rsd_lib_utils.num_or_none)
|
||||
"""The size of a unit in bytes used by UnitsRead and UnitsWritten"""
|
||||
|
||||
units_read = base.Field('UnitsRead')
|
||||
"""The number of units of a read since reset """
|
||||
|
||||
units_written = base.Field('UnitsWritten')
|
||||
"""The number of units of a written since reset"""
|
||||
|
||||
host_read_commands = base.Field('HostReadCommands')
|
||||
"""The number of read commands completed by the disk controller"""
|
||||
|
||||
host_write_commands = base.Field('HostWriteCommands')
|
||||
"""The number of write commands completed by the disk controller"""
|
||||
|
||||
power_cycles = base.Field('PowerCycles')
|
||||
"""The number of power cycels of this drive"""
|
||||
|
||||
power_on_hours = base.Field('PowerOnHours')
|
||||
"""The number of power-on hours of this drive"""
|
||||
|
||||
controller_busy_time_minutes = base.Field('ControllerBusyTimeMinutes')
|
||||
"""The amount of time in minutes the driver controller is busy"""
|
||||
|
||||
|
||||
class HealthDataField(base.CompositeField):
|
||||
available_spare_percentage = base.Field('AvailableSparePercentage')
|
||||
"""The percentage of the remaining spare capacity available"""
|
||||
|
||||
predicted_media_life_used_percent = base.Field(
|
||||
'PredictedMediaLifeUsedPercent')
|
||||
"""The percentage of life remaining in the driver's media"""
|
||||
|
||||
unsafe_shutdowns = base.Field('UnsafeShutdowns')
|
||||
"""The number of unsafe shutdowns of this drive"""
|
||||
|
||||
media_errors = base.Field('MediaErrors')
|
||||
"""The number of media and data integrity errors of this drive"""
|
||||
|
||||
|
||||
class DriveMetrics(base.ResourceBase):
|
||||
|
||||
name = base.Field('Name')
|
||||
"""Drive metrics name"""
|
||||
|
||||
identity = base.Field('Id')
|
||||
"""Drive metrics id"""
|
||||
|
||||
description = base.Field('Description')
|
||||
"""Drive metrics description"""
|
||||
|
||||
life_time = LifeTimeField('LifeTime')
|
||||
"""The life time metrics for this drive"""
|
||||
|
||||
health_data = HealthDataField('HealthData')
|
||||
"""The health data metrics for this drive"""
|
||||
|
||||
temperature_kelvin = base.Field('TemperatureKelvin')
|
||||
"""The temperature in Kelvin degrees of this drive"""
|
@ -53,7 +53,14 @@
|
||||
"Chassis": {
|
||||
"@odata.id": "/redfish/v1/Chassis/1"
|
||||
},
|
||||
"Oem": {},
|
||||
"Oem": {
|
||||
"Intel_RackScale": {
|
||||
"@odata.type": "#Intel.Oem.DriveLinks",
|
||||
"Metrics": {
|
||||
"@odata.id": "/redfish/v1/Chassis/1/Drives/1/Metrics"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Volumes": [],
|
||||
"Endpoints": []
|
||||
},
|
||||
|
25
rsd_lib/tests/unit/json_samples/v2_3/drive_metrics.json
Normal file
25
rsd_lib/tests/unit/json_samples/v2_3/drive_metrics.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"@odata.context": "/redfish/v1/$metadata#Chassis/Members/1/Drive/Metrics/$entity",
|
||||
"@odata.id": "/redfish/v1/Chassis/1/Drives/1/Metrics",
|
||||
"@odata.type": "#DriveMetrics.v1_0_0.DriveMetrics",
|
||||
"Name": "Drive Metrics for Drive",
|
||||
"Description": "Metrics for Drive 1",
|
||||
"Id": "Metrics",
|
||||
"TemperatureKelvin": 318,
|
||||
"LifeTime": {
|
||||
"UnitSizeBytes": 512000,
|
||||
"UnitsRead": 1640,
|
||||
"UnitsWritten": 2,
|
||||
"HostReadCommands": 12344,
|
||||
"HostWriteCommands": 2323,
|
||||
"PowerCycles": 244,
|
||||
"PowerOnHours": 34566566,
|
||||
"ControllerBusyTimeMinutes": 545465665656
|
||||
},
|
||||
"HealthData": {
|
||||
"AvailableSparePercentage": 67,
|
||||
"PredictedMediaLifeUsedPercent": 120,
|
||||
"UnsafeShutdowns": 23,
|
||||
"MediaErrors": 10
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ import mock
|
||||
import testtools
|
||||
|
||||
from rsd_lib.resources.v2_3.storage_service import drive
|
||||
from rsd_lib.resources.v2_3.storage_service import drive_metrics
|
||||
|
||||
|
||||
class DriveTestCase(testtools.TestCase):
|
||||
@ -77,6 +78,56 @@ class DriveTestCase(testtools.TestCase):
|
||||
self.assertEqual((), self.drive_inst.links.volumes)
|
||||
self.assertEqual((), self.drive_inst.links.endpoints)
|
||||
|
||||
def test_get_metrics_path(self):
|
||||
expected = '/redfish/v1/Chassis/1/Drives/1/Metrics'
|
||||
result = self.drive_inst._get_metrics_path()
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_drive_metrics(self):
|
||||
# | GIVEN |
|
||||
self.conn.get.return_value.json.reset_mock()
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_3/'
|
||||
'drive_metrics.json', 'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
# | WHEN |
|
||||
actual_drive_metrics = self.drive_inst.metrics
|
||||
# | THEN |
|
||||
self.assertIsInstance(actual_drive_metrics,
|
||||
drive_metrics.DriveMetrics)
|
||||
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_drive_metrics,
|
||||
self.drive_inst.metrics)
|
||||
self.conn.get.return_value.json.assert_not_called()
|
||||
|
||||
def test_drive_metrics_on_refresh(self):
|
||||
# | GIVEN |
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_3/'
|
||||
'drive_metrics.json', 'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
# | WHEN & THEN |
|
||||
self.assertIsInstance(self.drive_inst.metrics,
|
||||
drive_metrics.DriveMetrics)
|
||||
|
||||
# On refreshing the chassis instance...
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_3/'
|
||||
'drive.json', 'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
|
||||
self.drive_inst.invalidate()
|
||||
self.drive_inst.refresh(force=False)
|
||||
|
||||
# | GIVEN |
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_3/'
|
||||
'drive_metrics.json', 'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
# | WHEN & THEN |
|
||||
self.assertIsInstance(self.drive_inst.metrics,
|
||||
drive_metrics.DriveMetrics)
|
||||
|
||||
|
||||
class DriveCollectionTestCase(testtools.TestCase):
|
||||
|
||||
|
@ -0,0 +1,59 @@
|
||||
# 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.
|
||||
|
||||
import json
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
from rsd_lib.resources.v2_3.storage_service import drive_metrics
|
||||
|
||||
|
||||
class DriveMetricsTestCase(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(DriveMetricsTestCase, self).setUp()
|
||||
self.conn = mock.Mock()
|
||||
with open('rsd_lib/tests/unit/json_samples/v2_3/drive_metrics.json',
|
||||
'r') as f:
|
||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||
|
||||
self.drive_metrics_inst = drive_metrics.DriveMetrics(
|
||||
self.conn, '/redfish/v1/Chassis/1/Drives/1/Metrics',
|
||||
redfish_version='1.0.2')
|
||||
|
||||
def test__parse_attributes(self):
|
||||
self.drive_metrics_inst._parse_attributes()
|
||||
self.assertEqual('Drive Metrics for Drive',
|
||||
self.drive_metrics_inst.name)
|
||||
self.assertEqual('Metrics for Drive 1',
|
||||
self.drive_metrics_inst.description)
|
||||
self.assertEqual('Metrics', self.drive_metrics_inst.identity)
|
||||
self.assertEqual(318, self.drive_metrics_inst.temperature_kelvin)
|
||||
|
||||
life_time = self.drive_metrics_inst.life_time
|
||||
self.assertEqual(512000, life_time.unit_size_bytes)
|
||||
self.assertEqual(1640, life_time.units_read)
|
||||
self.assertEqual(2, life_time.units_written)
|
||||
self.assertEqual(12344, life_time.host_read_commands)
|
||||
self.assertEqual(2323, life_time.host_write_commands)
|
||||
self.assertEqual(244, life_time.power_cycles)
|
||||
self.assertEqual(34566566, life_time.power_on_hours)
|
||||
self.assertEqual(545465665656, life_time.controller_busy_time_minutes)
|
||||
|
||||
health_data = self.drive_metrics_inst.health_data
|
||||
self.assertEqual(67, health_data.available_spare_percentage)
|
||||
self.assertEqual(120, health_data.predicted_media_life_used_percent)
|
||||
self.assertEqual(23, health_data.unsafe_shutdowns)
|
||||
self.assertEqual(10, health_data.media_errors)
|
Loading…
x
Reference in New Issue
Block a user