1#  Copyright (C) 2018 Codethink Limited
2#
3#  This program is free software; you can redistribute it and/or
4#  modify it under the terms of the GNU Lesser General Public
5#  License as published by the Free Software Foundation; either
6#  version 2 of the License, or (at your option) any later version.
7#
8#  This library is distributed in the hope that it will be useful,
9#  but WITHOUT ANY WARRANTY; without even the implied warranty of
10#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
11#  Lesser General Public License for more details.
12#
13#  You should have received a copy of the GNU Lesser General Public
14#  License along with this library. If not, see <http://www.gnu.org/licenses/>.
15#
16#  Author:
17#        Tristan Daniël Maat <tristan.maat@codethink.co.uk>
18#
19from ruamel import yaml
20
21from ..._message import Message, MessageType
22
23from .job import Job
24
25
26# ElementJob()
27#
28# A job to run an element's commands. When this job is spawned
29# `action_cb` will be called, and when it completes `complete_cb` will
30# be called.
31#
32# Args:
33#    scheduler (Scheduler): The scheduler
34#    action_name (str): The queue action name
35#    max_retries (int): The maximum number of retries
36#    action_cb (callable): The function to execute on the child
37#    complete_cb (callable): The function to execute when the job completes
38#    element (Element): The element to work on
39#    kwargs: Remaining Job() constructor arguments
40#
41# Here is the calling signature of the action_cb:
42#
43#     action_cb():
44#
45#     This function will be called in the child task
46#
47#     Args:
48#        element (Element): The element passed to the Job() constructor
49#
50#     Returns:
51#        (object): Any abstract simple python object, including a string, int,
52#                  bool, list or dict, this must be a simple serializable object.
53#
54# Here is the calling signature of the complete_cb:
55#
56#     complete_cb():
57#
58#     This function will be called when the child task completes
59#
60#     Args:
61#        job (Job): The job object which completed
62#        element (Element): The element passed to the Job() constructor
63#        status (JobStatus): The status of whether the workload raised an exception
64#        result (object): The deserialized object returned by the `action_cb`, or None
65#                         if `success` is False
66#
67class ElementJob(Job):
68    def __init__(self, *args, element, queue, action_cb, complete_cb, **kwargs):
69        super().__init__(*args, **kwargs)
70        self.queue = queue
71        self._element = element
72        self._action_cb = action_cb            # The action callable function
73        self._complete_cb = complete_cb        # The complete callable function
74
75        # Set the task wide ID for logging purposes
76        self.set_task_id(element._unique_id)
77
78    @property
79    def element(self):
80        return self._element
81
82    def child_process(self):
83
84        # Print the element's environment at the beginning of any element's log file.
85        #
86        # This should probably be omitted for non-build tasks but it's harmless here
87        elt_env = self._element.get_environment()
88        env_dump = yaml.round_trip_dump(elt_env, default_flow_style=False, allow_unicode=True)
89        self.message(MessageType.LOG,
90                     "Build environment for element {}".format(self._element.name),
91                     detail=env_dump)
92
93        # Run the action
94        return self._action_cb(self._element)
95
96    def parent_complete(self, status, result):
97        self._complete_cb(self, self._element, status, self._result)
98
99    def message(self, message_type, message, **kwargs):
100        args = dict(kwargs)
101        args['scheduler'] = True
102        self._scheduler.context.message(
103            Message(self._element._unique_id,
104                    message_type,
105                    message,
106                    **args))
107
108    def child_process_data(self):
109        data = {}
110
111        workspace = self._element._get_workspace()
112        if workspace is not None:
113            data['workspace'] = workspace.to_dict()
114
115        return data
116