Adds basic support for TaskService to retrieve task information.

Change-Id: I3e266902da9346119a0ea1b94c49a71fb2da0f7c
Signed-off-by: Manuel Schönlaub <manuel.schoenlaub@gmail.com>
This commit is contained in:
Manuel Schönlaub 2020-10-08 18:48:30 +02:00
parent 74b8111aff
commit 9d9dae7d01
12 changed files with 341 additions and 0 deletions

View File

@ -0,0 +1,5 @@
---
features:
- |
Adds initial support for the TaskService resource to the library.
`TaskService` is responsible for managing tasks.

View File

@ -24,6 +24,7 @@ from sushy.resources.manager.constants import * # noqa
from sushy.resources.system.constants import * # noqa
from sushy.resources.system.storage.constants import * # noqa
from sushy.resources.updateservice.constants import * # noqa
from sushy.resources.taskservice.constants import * # noqa
__all__ = ('Sushy',)
__version__ = pbr.version.VersionInfo(

View File

@ -31,6 +31,7 @@ from sushy.resources.registry import message_registry_file
from sushy.resources.sessionservice import session
from sushy.resources.sessionservice import sessionservice
from sushy.resources.system import system
from sushy.resources.taskservice import taskservice
from sushy.resources.updateservice import updateservice
from sushy import utils
@ -400,6 +401,16 @@ class Sushy(base.ResourceBase):
redfish_version=self.redfish_version,
registries=self.lazy_registries)
def get_task_service(self):
"""Get the TaskService object
:returns: The TaskService object
"""
return taskservice.TaskService(
self._conn, utils.get_sub_resource_path_by(self, 'Tasks'),
redfish_version=self.redfish_version,
registries=self.lazy_registries)
def _get_registry_collection(self):
"""Get MessageRegistryFileCollection object

View File

@ -0,0 +1,19 @@
# 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.
# Values come from the Redfish UpdateService json-schema.
# https://redfish.dmtf.org/schemas/v1/TaskService.v1_1_5.json#/definitions/OverWritePolicy
# Overwrite Policy constants
OVERWRITE_POLICY_OLDEST = 'oldest completed'
OVERWRITE_POLICY_MANUAL = 'manual only'

View File

@ -14,6 +14,8 @@
# limitations under the License.
from sushy.resources import constants as res_cons
from sushy.resources.taskservice import constants as ts_cons
from sushy import utils
TASK_STATE_VALUE_MAP = {
@ -31,3 +33,11 @@ TASK_STATE_VALUE_MAP = {
'Cancelling': res_cons.TASK_STATE_CANCELLING,
'Cancelled': res_cons.TASK_STATE_CANCELLED
}
OVERWRITE_POLICY_VALUE_MAP = {
'Oldest': ts_cons.OVERWRITE_POLICY_OLDEST,
'Manual': ts_cons.OVERWRITE_POLICY_MANUAL,
}
OVERWRITE_POLICY_VALUE_MAP_REV = (
utils.revert_dictionary(OVERWRITE_POLICY_VALUE_MAP))

View File

@ -87,3 +87,24 @@ class Task(base.ResourceBase):
"""Parses the messages"""
for m in self.messages:
message_registry.parse_message(self._registries, m)
class TaskCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return Task
@property
@utils.cache_it
def summary(self):
"""Summary of task ids and corresponding state
:returns: dictionary in the format
{'jid_123456789': sushy.TASK_STATE_NEW,
'jid_123454321': sushy.TASK_STATE_RUNNING}
"""
task_dict = {}
for task in self.get_members():
task_dict[task.identity] = task.task_state
return task_dict

View File

@ -0,0 +1,76 @@
# 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.
# This is referred from Redfish standard schema.
# https://redfish.dmtf.org/schemas/v1/TaskService.v1_1_5.json
import logging
from sushy.resources import base
from sushy.resources import common
from sushy.resources.taskservice import mappings as ts_maps
from sushy.resources.taskservice import task
from sushy import utils
LOG = logging.getLogger(__name__)
class TaskService(base.ResourceBase):
identity = base.Field('Id', required=True)
"""The task service identity"""
name = base.Field('Name', required=True)
"""The task service name"""
service_enabled = base.Field('ServiceEnabled')
"""The status of whether this service is enabled"""
status = common.StatusField('Status')
"""The status of the task service"""
overwrite_policy = base.MappedField(
'CompletedTaskOverWritePolicy', ts_maps.OVERWRITE_POLICY_VALUE_MAP)
"""The overwrite policy for completed tasks"""
event_on_task_state_change = base.Field(
'LifeCycleEventOnTaskStateChange', adapter=bool)
"""Whether a task state change sends an event"""
def __init__(self, connector, identity, redfish_version=None,
registries=None):
"""A class representing a TaskService
:param connector: A Connector instance
:param identity: The identity of the TaskService resource
:param redfish_version: The version of RedFish. Used to construct
the object according to schema of given version
:param registries: Dict of Redfish Message Registry objects to be
used in any resource that needs registries to parse messages
"""
super(TaskService, self).__init__(
connector, identity, redfish_version, registries)
@property
@utils.cache_it
def tasks(self):
"""Property to reference `TaskCollection` instance
It is set once when the first time it is queried. On refresh,
this property is marked as stale (greedy-refresh not done).
Here the actual refresh of the sub-resource happens, if stale.
"""
return task.TaskCollection(
self._conn, utils.get_sub_resource_path_by(self, 'Tasks'),
redfish_version=self.redfish_version,
registries=self.registries)

View File

@ -0,0 +1,11 @@
{
"@odata.type":"#Task.v1_4_3.Task",
"Id":"546",
"Name":"Task 546",
"Description": "Task description",
"TaskMonitor":"/taskmon/546",
"TaskState":"Pending",
"TaskStatus":"OK",
"PercentComplete": 55,
"@odata.id":"/redfish/v1/TaskService/Tasks/546"
}

View File

@ -0,0 +1,16 @@
{
"@odata.context": "/redfish/v1/$metadata#TaskCollection.TaskCollection",
"@odata.id": "/redfish/v1/TaskService/Tasks",
"@odata.type": "#TaskCollection.TaskCollection",
"Description": "Collection of Tasks",
"Members": [
{
"@odata.id": "/redfish/v1/TaskService/Tasks/545"
},
{
"@odata.id": "/redfish/v1/TaskService/Tasks/546"
}
],
"Members@odata.count": 2,
"Name": "Task Collection"
}

View File

@ -0,0 +1,19 @@
{
"@odata.type": "#TaskService.v1_1_2.TaskService",
"Id": "TaskService",
"Name": "Tasks Service",
"DateTime": "2015-03-13T04:14:33+06:00",
"CompletedTaskOverWritePolicy": "Manual",
"LifeCycleEventOnTaskStateChange": true,
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ServiceEnabled": true,
"Tasks": {
"@odata.id": "/redfish/v1/TaskService/Tasks"
},
"Oem": {},
"@odata.context": "/redfish/v1/$metadata#TaskService.TaskService",
"@odata.id": "/redfish/v1/TaskService"
}

View File

@ -72,3 +72,101 @@ class TaskTestCase(base.TestCase):
self.task.parse_messages()
self.assertEqual('Property SKU is read only.',
self.task.messages[0].message)
class TaskCollectionTestCase(base.TestCase):
def setUp(self):
super(TaskCollectionTestCase, self).setUp()
self.conn = mock.Mock()
with open('sushy/tests/unit/json_samples/'
'task_collection.json') as f:
self.json_doc = json.load(f)
self.conn.get.return_value.json.return_value = self.json_doc
self.task_col = task.TaskCollection(
self.conn, '/redfish/v1/TaskService/Tasks',
redfish_version='1.0.2')
def test__parse_attributes(self):
self.task_col._parse_attributes(self.json_doc)
self.assertEqual('1.0.2', self.task_col.redfish_version)
self.assertEqual('Task Collection', self.task_col.name)
self.assertEqual(('/redfish/v1/TaskService/Tasks/545',
'/redfish/v1/TaskService/Tasks/546'),
self.task_col.members_identities)
@mock.patch.object(task, 'Task', autospec=True)
def test_get_member(self, mock_task):
self.task_col.get_member(
'/redfish/v1/TaskService/Tasks/545')
mock_task.assert_called_once_with(
self.task_col._conn,
'/redfish/v1/TaskService/Tasks/545',
self.task_col.redfish_version, None)
@mock.patch.object(task, 'Task', autospec=True)
def test_get_members(self, mock_task):
members = self.task_col.get_members()
calls = [
mock.call(self.task_col._conn,
'/redfish/v1/TaskService/Tasks/545',
self.task_col.redfish_version, None),
mock.call(self.task_col._conn,
'/redfish/v1/TaskService/Tasks/546',
self.task_col.redfish_version, None),
]
mock_task.assert_has_calls(calls)
self.assertIsInstance(members, list)
self.assertEqual(2, len(members))
def _setUp_task_summary(self):
self.conn.get.return_value.json.reset_mock()
successive_return_values = []
file_names = ['sushy/tests/unit/json_samples/task.json',
'sushy/tests/unit/json_samples/task2.json']
for file_name in file_names:
with open(file_name) as f:
successive_return_values.append(json.load(f))
self.conn.get.return_value.json.side_effect = successive_return_values
def test_summary(self):
# | GIVEN |
self._setUp_task_summary()
# | WHEN |
actual_summary = self.task_col.summary
# | THEN |
self.assertEqual({'545': 'completed', '546': 'pending'},
actual_summary)
# reset mock
self.conn.get.return_value.json.reset_mock()
# | WHEN & THEN |
# tests for same object on invoking subsequently
self.assertIs(actual_summary,
self.task_col.summary)
self.conn.get.return_value.json.assert_not_called()
def test_summary_on_refresh(self):
# | GIVEN |
self._setUp_task_summary()
# | WHEN & THEN |
self.assertEqual({'545': 'completed', '546': 'pending'},
self.task_col.summary)
self.conn.get.return_value.json.side_effect = None
# On refreshing the task_col instance...
with open('sushy/tests/unit/json_samples/'
'task_collection.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
self.task_col.invalidate()
self.task_col.refresh(force=False)
# | GIVEN |
self._setUp_task_summary()
# | WHEN & THEN |
self.assertEqual({'545': 'completed', '546': 'pending'},
self.task_col.summary)

View File

@ -0,0 +1,54 @@
# 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
from unittest import mock
from sushy.resources import constants as res_cons
from sushy.resources.taskservice import constants as ts_cons
from sushy.resources.taskservice import task
from sushy.resources.taskservice import taskservice
from sushy.tests.unit import base
class TaskServiceTestCase(base.TestCase):
def setUp(self):
super(TaskServiceTestCase, self).setUp()
self.conn = mock.Mock()
with open('sushy/tests/unit/json_samples/taskservice.json') as f:
self.json_doc = json.load(f)
self.conn.get.return_value.json.return_value = self.json_doc
self.tsk_serv = taskservice.TaskService(
self.conn, '/redfish/v1/TaskService/TaskService',
redfish_version='1.3.0')
def test__parse_attributes(self):
self.tsk_serv._parse_attributes(self.json_doc)
self.assertEqual('TaskService', self.tsk_serv.identity)
self.assertTrue(self.tsk_serv.service_enabled)
self.assertTrue(self.tsk_serv.event_on_task_state_change)
self.assertEqual(res_cons.STATE_ENABLED, self.tsk_serv.status.state)
self.assertEqual(res_cons.HEALTH_OK, self.tsk_serv.status.health)
self.assertEqual(self.tsk_serv.overwrite_policy,
ts_cons.OVERWRITE_POLICY_MANUAL)
@mock.patch.object(task, 'TaskCollection', autospec=True)
def test_tasks(self, task_collection_mock):
self.tsk_serv.tasks
task_collection_mock.assert_called_once_with(
self.conn, '/redfish/v1/TaskService/Tasks',
self.tsk_serv.redfish_version,
self.tsk_serv._registries)