Add metric report resource for RSD2.2

Change-Id: I4a67a9a1081f9c98e32fede1c52c6143f22e6db5
This commit is contained in:
monokai 2019-01-24 17:41:57 +08:00 committed by Lin Yang
parent 6a1041db3b
commit ae9d5d78d1
8 changed files with 500 additions and 4 deletions

View File

@ -0,0 +1,102 @@
# 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 utils as rsd_lib_utils
class MetricValuesField(base.ListField):
metric_id = base.Field('MetricId')
"""The value shall be the MetricId of the source metric within the
associated MetricDefinition
"""
metric_value = base.Field('MetricValue')
"""The value of the metric represented as a string. Its data type is
specified in including MetricResult.MetricDefinition.
"""
time_stamp = base.Field('TimeStamp')
"""The value shall be an ISO 8601 date time for when the metric value was
computed. Note that this may be different from the time when this
instance is created. If Volatile is true for a given metric value
instance, the TimeStamp changes whenever a new measurement snapshot
is taken. A management application may establish a time series of metric
data by retrieving the instances of metric value and sorting them
according to their TimeStamp.
"""
metric_property = base.Field('MetricProperty')
"""The value shall be a URI of a property contained in the scope of the
MetricScope
"""
metric_definition = base.Field(
'MetricDefinition', adapter=rsd_lib_utils.get_resource_identity)
"""The value shall be a URI to the metric definition of the property"""
class MetricReport(base.ResourceBase):
identity = base.Field("Id")
"""The metric report identity"""
name = base.Field("Name")
"""The metric report name"""
description = base.Field("Description")
"""The metric report description"""
metric_values = MetricValuesField("MetricValues")
"""The metric report definition"""
def _get_metric_report_definition_path(self):
"""Helper function to find the metric report definition path"""
return utils.get_sub_resource_path_by(self, 'MetricReportDefinition')
@property
@utils.cache_it
def metric_report_definition(self):
"""Property to provide reference to `MetricReportDefinition`
It is calculated once the first time it is queried. On refresh,
this property is reset.
"""
# Avoid metric_report and metric_report_definition module mutually
# import each other, move import to this function
from rsd_lib.resources.v2_2.telemetry import metric_report_definition
return metric_report_definition.MetricReportDefinition(
self._conn, self._get_metric_report_definition_path(),
redfish_version=self.redfish_version)
class MetricReportCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return MetricReport
def __init__(self, connector, path, redfish_version=None):
"""A class representing a MetricReportCollection
:param connector: A Connector instance
:param path: The canonical path to the Metric Report collection
resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of the given version.
"""
super(MetricReportCollection, self).__init__(connector, path,
redfish_version)

View File

@ -21,6 +21,7 @@ from sushy.resources import base
from sushy import utils
from rsd_lib.resources.v2_2.telemetry import metric
from rsd_lib.resources.v2_2.telemetry import metric_report
from rsd_lib.resources.v2_2.telemetry import metric_report_definition_schemas
@ -137,6 +138,22 @@ class MetricReportDefinition(base.ResourceBase):
self._conn, path, redfish_version=self.redfish_version)
for path in self._get_metrics_path()]
def _get_metric_report_path(self):
"""Helper function to find the metric report path"""
return utils.get_sub_resource_path_by(self, 'MetricReport')
@property
@utils.cache_it
def metric_report(self):
"""Property to provide reference to `MetricReport` instance
It is calculated once the first time it is queried. On refresh,
this property is reset.
"""
return metric_report.MetricReport(
self._conn, self._get_metric_report_path(),
redfish_version=self.redfish_version)
def delete(self):
"""Delete report definition"""
self._conn.delete(self.path)

View File

@ -17,6 +17,7 @@ from sushy.resources import base
from sushy import utils
from rsd_lib.resources.v2_2.telemetry import metric_definition
from rsd_lib.resources.v2_2.telemetry import metric_report
from rsd_lib.resources.v2_2.telemetry import metric_report_definition
from rsd_lib import utils as rsd_lib_utils
@ -78,3 +79,19 @@ class Telemetry(base.ResourceBase):
return metric_report_definition.MetricReportDefinitionCollection(
self._conn, self._get_metric_definitions_path(),
redfish_version=self.redfish_version)
def _get_metric_reports_path(self):
"""Helper function to find the metric reports path"""
return utils.get_sub_resource_path_by(self, 'MetricReports')
@property
@utils.cache_it
def metric_reports(self):
"""Property to provide reference to `MetricReportCollection`
It is calculated once the first time it is queried. On refresh,
this property is reset.
"""
return metric_report.MetricReportCollection(
self._conn, self._get_metric_reports_path(),
redfish_version=self.redfish_version)

View File

@ -0,0 +1,37 @@
{
"@odata.context":"/redfish/v1/$metadata#MetricReport.MetricReport",
"@odata.type":"#MetricReport.v1_0_0.MetricReport",
"@odata.id":"/redfish/v1/TelemetryService/MetricReports/TransmitCPU1Metrics",
"Id":"TransmitCPU1Metrics",
"Name":"CPU1 Metric Report",
"Description":"description-as-string",
"MetricReportDefinition":{
"@odata.id":"/redfish/v1/TelemetryService/MetricReportDefinitions/CPU1Metrics"
},
"MetricValues":[
{
"MetricValue":"29",
"TimeStamp":"2016-07-25T11:27:59.895513984+02:00",
"MetricProperty":"/redfish/v1/Systems/System1/Processors/CPU1/Metrics#/BandwidthPercent",
"MetricDefinition":{
"@odata.id":"/redfish/v1/TelemetryService/MetricDefinitions/CPUBandwidth"
}
},
{
"MetricValue":"FRB1 BIST Failure",
"TimeStamp":"2016-07-25T11:27:59.795513984+02:00",
"MetricProperty":"/redfish/v1/Systems/System1/Processors/CPU1/Metrics#/CPUHealth",
"MetricDefinition":{
"@odata.id":"/redfish/v1/TelemetryService/MetricDefinitions/CPUHealth"
}
},
{
"MetricValue":"43",
"TimeStamp":"2016-07-25T11:27:59.595513984+02:00",
"MetricProperty":"/redfish/v1/Systems/System1/Processors/CPU1/Metrics#/TemperatureCelsius",
"MetricDefinition":{
"@odata.id":"/redfish/v1/TelemetryService/MetricDefinitions/CPUTemperature"
}
}
]
}

View File

@ -0,0 +1,12 @@
{
"@odata.context":"/redfish/v1/$metadata#TelemetryService/MetricReports",
"@odata.id":"/redfish/v1/TelemetryService/MetricReports",
"@odata.type":"#MetricReportCollection.MetricReportCollection",
"Name":"MetricReports",
"Members@odata.count":1,
"Members":[
{
"@odata.id":"/redfish/v1/TelemetryService/MetricReports/TransmitCPU1Metrics"
}
]
}

View File

@ -0,0 +1,191 @@
# 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 sushy import exceptions
from rsd_lib.resources.v2_2.telemetry import metric_report
from rsd_lib.resources.v2_2.telemetry import metric_report_definition
class MetricReportTestCase(testtools.TestCase):
def setUp(self):
super(MetricReportTestCase, self).setUp()
self.conn = mock.Mock()
with open(
'rsd_lib/tests/unit/json_samples/v2_2/metric_report.json',
'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.metric_report_inst = metric_report.MetricReport(
self.conn,
'/redfish/v1/TelemetryService/MetricReports/TransmitCPU1Metrics',
redfish_version='1.1.0')
def test__parse_attributes(self):
self.metric_report_inst._parse_attributes()
self.assertEqual("TransmitCPU1Metrics",
self.metric_report_inst.identity)
self.assertEqual("CPU1 Metric Report", self.metric_report_inst.name)
self.assertEqual("description-as-string",
self.metric_report_inst.description)
# metric_values section
self.assertEqual(
None, self.metric_report_inst.metric_values[0].metric_id)
self.assertEqual(
"29", self.metric_report_inst.metric_values[0].metric_value)
self.assertEqual(
"2016-07-25T11:27:59.895513984+02:00",
self.metric_report_inst.metric_values[0].time_stamp)
self.assertEqual(
"/redfish/v1/Systems/System1/Processors/CPU1/Metrics#/"
"BandwidthPercent",
self.metric_report_inst.metric_values[0].metric_property)
self.assertEqual(
"/redfish/v1/TelemetryService/MetricDefinitions/CPUBandwidth",
self.metric_report_inst.metric_values[0].metric_definition)
self.assertEqual(
None, self.metric_report_inst.metric_values[1].metric_id)
self.assertEqual(
"FRB1 BIST Failure",
self.metric_report_inst.metric_values[1].metric_value)
self.assertEqual(
"2016-07-25T11:27:59.795513984+02:00",
self.metric_report_inst.metric_values[1].time_stamp)
self.assertEqual(
"/redfish/v1/Systems/System1/Processors/CPU1/Metrics#/CPUHealth",
self.metric_report_inst.metric_values[1].metric_property)
self.assertEqual(
"/redfish/v1/TelemetryService/MetricDefinitions/CPUHealth",
self.metric_report_inst.metric_values[1].metric_definition)
self.assertEqual(
None, self.metric_report_inst.metric_values[2].metric_id)
self.assertEqual(
"43", self.metric_report_inst.metric_values[2].metric_value)
self.assertEqual(
"2016-07-25T11:27:59.595513984+02:00",
self.metric_report_inst.metric_values[2].time_stamp)
self.assertEqual(
"/redfish/v1/Systems/System1/Processors/CPU1/Metrics#/"
"TemperatureCelsius",
self.metric_report_inst.metric_values[2].metric_property)
self.assertEqual(
"/redfish/v1/TelemetryService/MetricDefinitions/CPUTemperature",
self.metric_report_inst.metric_values[2].metric_definition)
def test__get_metric_report_definition_path_path(self):
self.assertEqual(
'/redfish/v1/TelemetryService/MetricReportDefinitions/CPU1Metrics',
self.metric_report_inst._get_metric_report_definition_path())
def test__get_metric_report_definition_path_missing_systems_attr(self):
self.metric_report_inst._json.pop('MetricReportDefinition')
with self.assertRaisesRegex(
exceptions.MissingAttributeError,
'attribute MetricReportDefinition'):
self.metric_report_inst._get_metric_report_definition_path()
def test_metric_report_definition(self):
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_report_definition.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_report_definition = \
self.metric_report_inst.metric_report_definition
# | THEN |
self.assertIsInstance(
actual_report_definition,
metric_report_definition.MetricReportDefinition)
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_report_definition,
self.metric_report_inst.metric_report_definition)
self.conn.get.return_value.json.assert_not_called()
def test_metric_report_definitions_on_refresh(self):
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_report_definition.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(
self.metric_report_inst.metric_report_definition,
metric_report_definition.MetricReportDefinition)
# On refreshing the telemetry service instance...
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_report.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.metric_report_inst.invalidate()
self.metric_report_inst.refresh(force=False)
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_report_definition.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(
self.metric_report_inst.metric_report_definition,
metric_report_definition.MetricReportDefinition)
class MetricReportCollectionTestCase(testtools.TestCase):
def setUp(self):
super(MetricReportCollectionTestCase, self).setUp()
self.conn = mock.Mock()
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_report_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.metric_report_col = metric_report. MetricReportCollection(
self.conn, '/redfish/v1/TelemetryService/MetricReports',
redfish_version='1.1.0')
def test_parse_attributes(self):
self.metric_report_col._parse_attributes()
self.assertEqual("MetricReports", self.metric_report_col.name)
@mock.patch.object(metric_report, 'MetricReport', autospec=True)
def test_get_member(self, mock_metric_report):
self.metric_report_col.get_member(
'/redfish/v1/TelemetryService/MetricReports/TransmitCPU1Metrics')
mock_metric_report.assert_called_once_with(
self.metric_report_col._conn,
'/redfish/v1/TelemetryService/MetricReports/TransmitCPU1Metrics',
redfish_version=self.metric_report_col.redfish_version
)
@mock.patch.object(metric_report, 'MetricReport', autospec=True)
def test_get_members(self, mock_metric_report):
members = self.metric_report_col.get_members()
self.assertEqual(mock_metric_report.call_count, 1)
self.assertIsInstance(members, list)
self.assertEqual(1, len(members))

View File

@ -21,6 +21,7 @@ import testtools
from sushy import exceptions
from rsd_lib.resources.v2_2.telemetry import metric
from rsd_lib.resources.v2_2.telemetry import metric_report
from rsd_lib.resources.v2_2.telemetry import metric_report_definition
from rsd_lib.tests.unit.fakes import request_fakes
@ -140,6 +141,65 @@ class ReportDefinitionTestCase(testtools.TestCase):
self.assertIsInstance(
self.metric_report_definition_inst.metrics[0], metric.Metric)
def test__get_metric_report_path_path(self):
self.assertEqual(
'/redfish/v1/TelemetryService/MetricReports/TransmitCPU1Metrics',
self.metric_report_definition_inst._get_metric_report_path())
def test__get_metric_report_path_missing_systems_attr(self):
self.metric_report_definition_inst._json.pop('MetricReport')
with self.assertRaisesRegex(exceptions.MissingAttributeError,
'attribute MetricReport'):
self.metric_report_definition_inst._get_metric_report_path()
def test_metric_report(self):
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_report.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_metric_report = self.metric_report_definition_inst.metric_report
# | THEN |
self.assertIsInstance(
actual_metric_report, metric_report.MetricReport)
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_metric_report,
self.metric_report_definition_inst.metric_report)
self.conn.get.return_value.json.assert_not_called()
def test_metric_report_on_refresh(self):
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_report.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(
self.metric_report_definition_inst.metric_report,
metric_report.MetricReport)
# On refreshing the telemetry service instance...
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'telemetry_service.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.metric_report_definition_inst.invalidate()
self.metric_report_definition_inst.refresh(force=False)
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_report.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(
self.metric_report_definition_inst.metric_report,
metric_report.MetricReport)
def test_delete(self):
self.metric_report_definition_inst.delete()
self.metric_report_definition_inst._conn.delete.assert_called_once()
@ -168,8 +228,8 @@ class ReportDefinitionCollectionTestCase(testtools.TestCase):
def test_parse_attributes(self):
self.report_definition_col._parse_attributes()
self.assertEqual("MetricReportDefinition Collection", self.
report_definition_col.name)
self.assertEqual("MetricReportDefinition Collection",
self.report_definition_col.name)
@mock.patch.object(
metric_report_definition, 'MetricReportDefinition', autospec=True)

View File

@ -20,6 +20,7 @@ import testtools
from sushy import exceptions
from rsd_lib.resources.v2_2.telemetry import metric_definition
from rsd_lib.resources.v2_2.telemetry import metric_report
from rsd_lib.resources.v2_2.telemetry import metric_report_definition
from rsd_lib.resources.v2_2.telemetry import telemetry
@ -79,7 +80,7 @@ class TelemetryTestCase(testtools.TestCase):
self.telemetry_inst.metric_definitions)
self.conn.get.return_value.json.assert_not_called()
def test_metrics_definitions_on_refresh(self):
def test_metric_definitions_on_refresh(self):
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_definition_collection.json', 'r') as f:
@ -139,7 +140,7 @@ class TelemetryTestCase(testtools.TestCase):
self.telemetry_inst.metric_report_definitions)
self.conn.get.return_value.json.assert_not_called()
def test_metrics_report_definitions_on_refresh(self):
def test_metric_report_definitions_on_refresh(self):
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_report_definition_collection.json', 'r') as f:
@ -165,3 +166,62 @@ class TelemetryTestCase(testtools.TestCase):
self.assertIsInstance(
self.telemetry_inst.metric_report_definitions,
metric_report_definition.MetricReportDefinitionCollection)
def test__get_metric_reports_path_path(self):
self.assertEqual(
'/redfish/v1/TelemetryService/MetricReports',
self.telemetry_inst._get_metric_reports_path())
def test__get_metric_reports_path_missing_systems_attr(self):
self.telemetry_inst._json.pop('MetricReports')
with self.assertRaisesRegex(exceptions.MissingAttributeError,
'attribute MetricReports'):
self.telemetry_inst._get_metric_reports_path()
def test_metric_reports(self):
# | GIVEN |
self.conn.get.return_value.json.reset_mock()
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_report_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN |
actual_metric_reports = self.telemetry_inst.metric_reports
# | THEN |
self.assertIsInstance(
actual_metric_reports, metric_report.MetricReportCollection)
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_metric_reports,
self.telemetry_inst.metric_reports)
self.conn.get.return_value.json.assert_not_called()
def test_metric_reports_on_refresh(self):
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_report_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(
self.telemetry_inst.metric_reports,
metric_report.MetricReportCollection)
# On refreshing the telemetry service instance...
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'telemetry_service.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.telemetry_inst.invalidate()
self.telemetry_inst.refresh(force=False)
# | GIVEN |
with open('rsd_lib/tests/unit/json_samples/v2_2/'
'metric_report_collection.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
# | WHEN & THEN |
self.assertIsInstance(
self.telemetry_inst.metric_reports,
metric_report.MetricReportCollection)