forked from electric-cloud/DSL-Samples
-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathJobStepReporting.dsl
More file actions
309 lines (284 loc) · 11.2 KB
/
Copy pathJobStepReporting.dsl
File metadata and controls
309 lines (284 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
/*
CloudBees CD DSL: Implement Job Step reporting
- Creates a new report object type "jobstep"
- Creates a report to aggregate unique hostnames vs. start times
- Creates a dashboard with a widget that displays the number of unique hosts vs. start times
- Creates a procedure that finds all job steps with an assigned resource in a user-provided time range and pushes these
to the DevOps Insights reporting database
- Creates a self service catalog item to wrap this procedure
Instructions
1. If you are using a recent version of CloudBees CD (v10+), set RECENTVERSION=true below
2. Apply this DSL (ectool evalDsl --dslFile jobStepReporting.dsl, or import and run in DSLIDE)
3. Run the self service catalog item "Push reporting data". Choose a small time range such as one day.
The "Dry run" flag is set by default. Press OK and note how many records were found. If this number is
less than say 10,000, rerun the job with the "Dry Run" option deselected (Use the Run... option from the run Job
pull down menu).
4. Navigate to the DevOps Insights Dashboards and select "Resource Utilization" to view the number of hosts being used
over time.
To generate TAB delimited output for the "Host in use" and "Hosts" reports, run the following DSL from the DSLIDE:
----------------------------
// "Host in use" widget
def headers = [
'startTime_count',
'total',
'startTime_label','startTime',
'distinct_count_hostName'
]
def tab = headers.join('\t') + '\n'
runReport(projectName: "Job Step Reporting", widgetName: "Hosts in use", reportName: "Host count by startTime", dashboardName: "Resource Utilization").asNode()[0].each { row ->
headers.each { field ->
tab += row[field]
tab += '\t'
}
tab += '\n'
}
tab
---------------------------
// "Hosts" widget
def headers = [
"hostName_count",
"hostName",
]
def tab = headers.join('\t') + '\n'
runReport(projectName: "Job Step Reporting", widgetName: "Hosts", reportName: "Host list", dashboardName: "Resource Utilization").asNode()[0].each { row ->
headers.each { field ->
tab += row[field]
tab += '\t'
}
tab += '\n'
}
tab
*/
def RECENTVERSION=false // Set to true if using 10.0+
reportObjectType "jobstep", displayName: "Job Steps"
/*
All job step fields
-------------------
jobStepId, stepName, alwaysRun, broadcast, combinedStatus, status, condition, createTime, duration, elapsedTime, environmentWaitTime, errorHandling, exclusive, exclusiveMode, exitCode, external, finish, jobId, jobName, lastModifiedBy, licenseReshareWaitTime, licenseWaitTime, liveProcedureStep, logFileName, modifyTime, outcome, owner, parallel, postExitCode, postLogFileName, procedureName, projectName, propertySheetId, releaseExclusive, releaseMode, resourceWaitTime, retries, runTime, runnable, start, status, stepIndex, subprocedure, subproject, timeLimit, totalWaitTime, waitTime, workspaceWaitTime
*/
def fields = [
"resourceName":"STRING",
"hostName":"STRING",
"jobName":"STRING",
"jobStepId":"STRING",
"stepName":"STRING",
"startTime":"DATETIME",
"endTime":"DATETIME",
"duration":"DURATION",
"projectName":"STRING"
]
fields.each { field, type ->
/*
description, enumerationValues, required, type:
<BOOLEAN|CONSTANT|DATE|DATETIME|DURATION|NUMBER|PERCENT|STRING>
*/
reportObjectAttribute reportObjectTypeName: "jobstep", field, type: type
}
project 'Job Step Reporting',{
report 'Host count by startTime', {
reportObjectTypeName = 'jobstep'
reportQuery = '''\
{
"searchCriteria":[],
"groupBy":[
{
"field":"startTime"
}
],
"aggregationFunctions":[
{
"field":"hostName",
"function":"DISTINCT_COUNT"
}
]
}
'''.stripIndent()
}
report 'Host list', {
reportObjectTypeName = 'jobstep'
reportQuery = '''\
{
"searchCriteria":[],
"groupBy":[
{
"field":"hostName"
}
],
"aggregationFunctions":[]
}
'''.stripIndent()
}
report 'Resource-Host', {
reportObjectTypeName = 'jobstep'
reportQuery = '''\
{
"searchCriteria":[],
"groupBy":[
{
"field":"hostName"
},{
"field":"resourceName"
}
],
"aggregationFunctions":[]
}
'''.stripIndent()
}
dashboard 'Resource Utilization', {
layout = 'FLOW'
type = 'STANDARD'
reportingFilter 'DateFilter', {
operator = 'BETWEEN'
parameterName = 'startTime'
required = '1'
type = 'DATE'
}
if (RECENTVERSION) {
reportingFilter 'Host Name', {
operator = 'IN'
parameterName = 'hostName'
reportObjectTypeName = 'jobstep'
type = 'CUSTOM'
}
reportingFilter 'Resource', {
operator = 'IN'
parameterName = 'resourceName'
reportObjectTypeName = 'jobstep'
type = 'CUSTOM'
}
}
widget 'Hosts in use', {
description = ''
attributeDataType = [
'yAxis': 'NUMBER',
'xAxis': 'DATE',
]
attributePath = [
'yAxis': 'distinct_count_hostName',
'xAxis': 'startTime_label',
]
reportName = 'Host count by startTime'
title = 'Hosts in use'
visualization = 'VERTICAL_BAR_CHART'
}
widget 'Hosts', {
attributeDataType = [
'column1': 'STRING',
]
attributePath = [
'column1': 'hostName',
'column1Label': 'Host name',
]
reportName = 'Host list'
title = 'Hosts'
visualization = 'TABLE'
}
widget 'Resource-host', {
attributeDataType = [
'column1': 'STRING',
'column2': 'STRING',
]
attributePath = [
'column1': 'resourceName',
'column2Label': 'Host name',
'column2': 'hostName',
'column1Label': 'Resource Name',
]
reportName = 'Resource-Host'
title = 'Resource-host'
visualization = 'TABLE'
}
}
procedure 'Push job step reporting data',{
formalParameter "startTime",
label: "Start time",
required: true,
description: "YYYY-MM-ddTHH:mm:ss.sssZ or YYYY-MM-dd, e.g., 2020-10-01T04:00:00.000Z or 2020-10-01",
orderIndex: 1,
type: 'date'
formalParameter "endTime",
label: "End time",
required: true,
description: "YYYY-MM-ddTHH:mm:ss.sssZ or YYYY-MM-dd, e.g., 2020-10-01T04:00:00.000Z or 2020-10-01",
orderIndex: 2,
type: 'date'
formalParameter 'dryRun',
label: "Dry run",
defaultValue: 'true',
description: 'When unchecked, data will be pushed to the DevOps Insights reporting server',
checkedValue: 'true',
orderIndex: 3,
type: 'checkbox',
uncheckedValue: 'false'
step "Gather and push", shell: "ec-groovy", command: '''\
import groovy.json.JsonOutput
import com.electriccloud.client.groovy.ElectricFlow
import com.electriccloud.client.groovy.models.Filter
ElectricFlow ef = new ElectricFlow()
def startTime = "$[startTime]"
def endTime = "$[endTime]"
if (startTime.length() == 10) startTime += "T00:00:00.000Z"
if (endTime.length() == 10) endTime += "T00:00:00.000Z"
Filter begin = new Filter(
propertyName: "start",
operator: "greaterThan",
operand1: startTime
)
Filter end = new Filter(
propertyName: "start",
operator: "lessThan",
operand1: endTime
)
Filter resource = new Filter(
propertyName: "assignedResourceName",
operator: "isNotNull",
)
def result = ef.findObjects(
objectType: 'jobStep',
filters: [begin, end, resource],
maxIds: 0
)
if ("$[dryRun]" == "true") {
ef.setProperty(propertyName: "summary", value: "Dry Run: Found ${result.object.size()} records")
} else {
ef.setProperty(propertyName: "summary", value: "Found ${result.object.size()} records")
result.object.eachWithIndex { it, index ->
def payload = [
resourceName: it.jobStep.assignedResourceName,
hostName: it.jobStep.hostName,
jobName: it.jobStep.jobName,
stepName: it.jobStep.stepName,
jobStepId: it.jobStep.jobStepId,
startTime: it.jobStep.start,
duration: it.jobStep.elapsedTime,
]
if (it.jobStep.finish) payload << [endTime: it.jobStep.finish]
ef.sendReportingData(
payload: JsonOutput.toJson(payload),
reportObjectTypeName: 'jobstep'
)
ef.setProperty(propertyName: "summary", value: "Sent ${index+1} of ${result.object.size()} records")
}
}
'''.stripIndent()
}
catalog 'Resource Utilization', {
catalogItem 'Push reporting data', {
description = '''\
<xml>
<title>This SSC item will push job step data to the reporting database. This data can be use to report on agent host utilization.</title>
<htmlData>
<![CDATA[
Run this self service catalog item to push resource utilization data to the DevOps Insights reporting server.
<p>Chose a data range and run; by default, Dry Run is enabled, so no data will be sent to the reporting server but the number of element to be sent will be displayed. If this number is less that 10000, rerun the job with Dry Run disabled (use the pull down menu next to the run button on the Job page).</p>
<p>Once there is data in the reporting server, the "Resource Utilization" dashboard should have content.</p>
]]>
</htmlData>
</xml>
'''.stripIndent()
buttonLabel = 'Execute'
iconUrl = 'icon-resource.svg'
subprocedure = 'Push job step reporting data'
}
}
}