1
|
|
|
# Licensed to the StackStorm, Inc ('StackStorm') under one or more |
2
|
|
|
# contributor license agreements. See the NOTICE file distributed with |
3
|
|
|
# this work for additional information regarding copyright ownership. |
4
|
|
|
# The ASF licenses this file to You under the Apache License, Version 2.0 |
5
|
|
|
# (the "License"); you may not use this file except in compliance with |
6
|
|
|
# the License. You may obtain a copy of the License at |
7
|
|
|
# |
8
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0 |
9
|
|
|
# |
10
|
|
|
# Unless required by applicable law or agreed to in writing, software |
11
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS, |
12
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13
|
|
|
# See the License for the specific language governing permissions and |
14
|
|
|
# limitations under the License. |
15
|
|
|
|
16
|
|
|
import copy |
17
|
|
|
|
18
|
|
|
import six |
19
|
|
|
|
20
|
|
|
from st2common.constants.action import LIVEACTION_STATUSES |
21
|
|
|
from st2common.util import isotime |
22
|
|
|
from st2common.models.api.base import BaseAPI |
23
|
|
|
from st2common.models.db.execution import ActionExecutionDB |
24
|
|
|
from st2common.models.api.trigger import TriggerTypeAPI, TriggerAPI, TriggerInstanceAPI |
25
|
|
|
from st2common.models.api.rule import RuleAPI |
26
|
|
|
from st2common.models.api.action import RunnerTypeAPI, ActionAPI, LiveActionAPI |
27
|
|
|
from st2common import log as logging |
28
|
|
|
|
29
|
|
|
|
30
|
|
|
LOG = logging.getLogger(__name__) |
31
|
|
|
|
32
|
|
|
REQUIRED_ATTR_SCHEMAS = { |
33
|
|
|
"action": copy.deepcopy(ActionAPI.schema), |
34
|
|
|
"runner": copy.deepcopy(RunnerTypeAPI.schema), |
35
|
|
|
"liveaction": copy.deepcopy(LiveActionAPI.schema), |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
for k, v in six.iteritems(REQUIRED_ATTR_SCHEMAS): |
39
|
|
|
v.update({"required": True}) |
40
|
|
|
|
41
|
|
|
|
42
|
|
|
class ActionExecutionAPI(BaseAPI): |
43
|
|
|
model = ActionExecutionDB |
44
|
|
|
SKIP = ['start_timestamp', 'end_timestamp'] |
45
|
|
|
schema = { |
46
|
|
|
"title": "ActionExecution", |
47
|
|
|
"description": "Record of the execution of an action.", |
48
|
|
|
"type": "object", |
49
|
|
|
"properties": { |
50
|
|
|
"id": { |
51
|
|
|
"type": "string", |
52
|
|
|
"required": True |
53
|
|
|
}, |
54
|
|
|
"trigger": TriggerAPI.schema, |
55
|
|
|
"trigger_type": TriggerTypeAPI.schema, |
56
|
|
|
"trigger_instance": TriggerInstanceAPI.schema, |
57
|
|
|
"rule": RuleAPI.schema, |
58
|
|
|
"action": REQUIRED_ATTR_SCHEMAS['action'], |
59
|
|
|
"runner": REQUIRED_ATTR_SCHEMAS['runner'], |
60
|
|
|
"liveaction": REQUIRED_ATTR_SCHEMAS['liveaction'], |
61
|
|
|
"status": { |
62
|
|
|
"description": "The current status of the action execution.", |
63
|
|
|
"type": "string", |
64
|
|
|
"enum": LIVEACTION_STATUSES |
65
|
|
|
}, |
66
|
|
|
"start_timestamp": { |
67
|
|
|
"description": "The start time when the action is executed.", |
68
|
|
|
"type": "string", |
69
|
|
|
"pattern": isotime.ISO8601_UTC_REGEX |
70
|
|
|
}, |
71
|
|
|
"end_timestamp": { |
72
|
|
|
"description": "The timestamp when the action has finished.", |
73
|
|
|
"type": "string", |
74
|
|
|
"pattern": isotime.ISO8601_UTC_REGEX |
75
|
|
|
}, |
76
|
|
|
"elapsed_seconds": { |
77
|
|
|
"description": "Time duration in seconds taken for completion of this execution.", |
78
|
|
|
"type": "number", |
79
|
|
|
"required": False |
80
|
|
|
}, |
81
|
|
|
"web_url": { |
82
|
|
|
"description": "History URL for this execution if you want to view in UI.", |
83
|
|
|
"type": "string", |
84
|
|
|
"required": False |
85
|
|
|
}, |
86
|
|
|
"parameters": { |
87
|
|
|
"description": "Input parameters for the action.", |
88
|
|
|
"type": "object", |
89
|
|
|
"patternProperties": { |
90
|
|
|
"^\w+$": { |
|
|
|
|
91
|
|
|
"anyOf": [ |
92
|
|
|
{"type": "array"}, |
93
|
|
|
{"type": "boolean"}, |
94
|
|
|
{"type": "integer"}, |
95
|
|
|
{"type": "number"}, |
96
|
|
|
{"type": "object"}, |
97
|
|
|
{"type": "string"} |
98
|
|
|
] |
99
|
|
|
} |
100
|
|
|
} |
101
|
|
|
}, |
102
|
|
|
"context": { |
103
|
|
|
"type": "object" |
104
|
|
|
}, |
105
|
|
|
"result": { |
106
|
|
|
"anyOf": [{"type": "array"}, |
107
|
|
|
{"type": "boolean"}, |
108
|
|
|
{"type": "integer"}, |
109
|
|
|
{"type": "number"}, |
110
|
|
|
{"type": "object"}, |
111
|
|
|
{"type": "string"}] |
112
|
|
|
}, |
113
|
|
|
"parent": {"type": "string"}, |
114
|
|
|
"children": { |
115
|
|
|
"type": "array", |
116
|
|
|
"items": {"type": "string"}, |
117
|
|
|
"uniqueItems": True |
118
|
|
|
}, |
119
|
|
|
"log": { |
120
|
|
|
"description": "Contains information about execution state transitions.", |
121
|
|
|
"type": "array", |
122
|
|
|
"items": { |
123
|
|
|
"type": "object", |
124
|
|
|
"properties": { |
125
|
|
|
"timestamp": { |
126
|
|
|
"type": "string", |
127
|
|
|
"pattern": isotime.ISO8601_UTC_REGEX |
128
|
|
|
}, |
129
|
|
|
"status": { |
130
|
|
|
"type": "string", |
131
|
|
|
"enum": LIVEACTION_STATUSES |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
} |
136
|
|
|
}, |
137
|
|
|
"additionalProperties": False |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
@classmethod |
141
|
|
|
def from_model(cls, model, mask_secrets=False): |
142
|
|
|
doc = cls._from_model(model, mask_secrets=mask_secrets) |
143
|
|
|
start_timestamp = model.start_timestamp |
144
|
|
|
start_timestamp_iso = isotime.format(start_timestamp, offset=False) |
145
|
|
|
doc['start_timestamp'] = start_timestamp_iso |
146
|
|
|
|
147
|
|
|
end_timestamp = model.end_timestamp |
148
|
|
|
if end_timestamp: |
149
|
|
|
end_timestamp_iso = isotime.format(end_timestamp, offset=False) |
150
|
|
|
doc['end_timestamp'] = end_timestamp_iso |
151
|
|
|
doc['elapsed_seconds'] = (end_timestamp - start_timestamp).total_seconds() |
152
|
|
|
|
153
|
|
|
for entry in doc.get('log', []): |
154
|
|
|
entry['timestamp'] = isotime.format(entry['timestamp'], offset=False) |
155
|
|
|
|
156
|
|
|
attrs = {attr: value for attr, value in six.iteritems(doc) if value} |
157
|
|
|
return cls(**attrs) |
158
|
|
|
|
159
|
|
|
@classmethod |
160
|
|
|
def to_model(cls, instance): |
161
|
|
|
values = {} |
162
|
|
|
for attr, meta in six.iteritems(cls.schema.get('properties', dict())): |
163
|
|
|
if not getattr(instance, attr, None): |
164
|
|
|
continue |
165
|
|
|
|
166
|
|
|
default = copy.deepcopy(meta.get('default', None)) |
167
|
|
|
value = getattr(instance, attr, default) |
168
|
|
|
|
169
|
|
|
# pylint: disable=no-member |
170
|
|
|
# TODO: Add plugin which lets pylint know each MongoEngine document has _fields |
171
|
|
|
# attribute |
172
|
|
|
attr_schema = cls.model._fields.get(attr, None) |
173
|
|
|
if not value and (attr_schema and not attr_schema.required): |
174
|
|
|
continue |
175
|
|
|
if attr not in ActionExecutionAPI.SKIP: |
176
|
|
|
values[attr] = value |
177
|
|
|
|
178
|
|
|
values['start_timestamp'] = isotime.parse(instance.start_timestamp) |
179
|
|
|
values['end_timestamp'] = isotime.parse(instance.end_timestamp) |
180
|
|
|
|
181
|
|
|
model = cls.model(**values) |
182
|
|
|
return model |
183
|
|
|
|
Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with
r
orR
are they interpreted as regular expressions.The escape sequence that was used indicates that you might have intended to write a regular expression.
Learn more about the available escape sequences. in the Python documentation.