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:
parent
74b8111aff
commit
9d9dae7d01
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Adds initial support for the TaskService resource to the library.
|
||||
`TaskService` is responsible for managing tasks.
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue