foreman_host_reports 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +619 -0
- data/README.md +545 -0
- data/app/controllers/api/v2/host_reports_controller.rb +98 -0
- data/app/controllers/concerns/foreman_host_reports/controller/parameters/host_report.rb +25 -0
- data/app/controllers/host_reports_controller.rb +46 -0
- data/app/models/concerns/foreman_host_reports/host_extensions.rb +9 -0
- data/app/models/concerns/foreman_host_reports/smart_proxy_extensions.rb +9 -0
- data/app/models/host_report.rb +85 -0
- data/app/models/report_keyword.rb +11 -0
- data/app/views/api/v2/host_reports/base.json.rabl +5 -0
- data/app/views/api/v2/host_reports/create.json.rabl +5 -0
- data/app/views/api/v2/host_reports/index.json.rabl +5 -0
- data/app/views/api/v2/host_reports/main.json.rabl +15 -0
- data/app/views/api/v2/host_reports/show.json.rabl +11 -0
- data/config/routes.rb +33 -0
- data/db/migrate/20210112183526_add_host_reports.rb +36 -0
- data/db/migrate/20210616133601_create_report_keywords.rb +12 -0
- data/lib/foreman_host_reports/engine.rb +68 -0
- data/lib/foreman_host_reports/version.rb +3 -0
- data/lib/foreman_host_reports.rb +4 -0
- data/lib/tasks/foreman_host_reports_tasks.rake +50 -0
- data/locale/Makefile +60 -0
- data/locale/en/foreman_host_reports.po +19 -0
- data/locale/foreman_host_reports.pot +19 -0
- data/locale/gemspec.rb +2 -0
- data/package.json +45 -0
- data/test/controllers/api/v2/host_reports_controller_test.rb +278 -0
- data/test/factories/foreman_host_reports_factories.rb +21 -0
- data/test/snapshots/foreman-web.json +918 -0
- data/test/test_plugin_helper.rb +11 -0
- data/test/unit/foreman_host_reports_test.rb +9 -0
- data/webpack/global_index.js +4 -0
- data/webpack/global_test_setup.js +11 -0
- data/webpack/index.js +0 -0
- data/webpack/src/Router/HostReports/Components/HostReportDeleteModal.js +50 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/FormatCell.js +53 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/FormatCell.scss +4 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/formatCellFormatter.js +6 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/hostReportsToShowFormatter.js +13 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/index.js +4 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/reportToShowFormatter.js +13 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/statusFormatter.js +9 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/HostReportsToShowCell.js +32 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/ReportToShowCell.js +27 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/StatusCell.js +77 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/StatusCell.scss +9 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/formatImages.js +7 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/images/ansible.png +0 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/images/puppet.png +0 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/HostReportsTable.js +85 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/HostReportsTableSchema.js +65 -0
- data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/index.js +28 -0
- data/webpack/src/Router/HostReports/IndexPage/IndexPage.js +88 -0
- data/webpack/src/Router/HostReports/IndexPage/IndexPageActions.js +55 -0
- data/webpack/src/Router/HostReports/IndexPage/IndexPageHelpers.js +50 -0
- data/webpack/src/Router/HostReports/IndexPage/IndexPageSelectors.js +86 -0
- data/webpack/src/Router/HostReports/IndexPage/constants.js +14 -0
- data/webpack/src/Router/HostReports/IndexPage/index.js +98 -0
- data/webpack/src/Router/HostReports/ShowPage/Components/HostReportMetrics/HostReportMetrics.scss +3 -0
- data/webpack/src/Router/HostReports/ShowPage/Components/HostReportMetrics/index.js +100 -0
- data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/Ansible.js +77 -0
- data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/Puppet.js +79 -0
- data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/helpers.js +17 -0
- data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/index.js +29 -0
- data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogsFilter/index.js +109 -0
- data/webpack/src/Router/HostReports/ShowPage/ShowPage.js +160 -0
- data/webpack/src/Router/HostReports/ShowPage/ShowPageSelectors.js +51 -0
- data/webpack/src/Router/HostReports/ShowPage/index.js +75 -0
- data/webpack/src/Router/HostReports/constants.js +15 -0
- data/webpack/src/Router/routes.js +23 -0
- data/webpack/test_setup.js +17 -0
- metadata +133 -0
data/README.md
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
# ForemanHostReports (foreman_host_reports)
|
|
2
|
+
|
|
3
|
+
Foreman plugin providing reporting feature optimized for high-performance. The
|
|
4
|
+
ultimate goal is to replace Foreman core reporting functionality.
|
|
5
|
+
|
|
6
|
+
## Installation and usage
|
|
7
|
+
|
|
8
|
+
See
|
|
9
|
+
[How_to_Install_a_Plugin](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Plugin)
|
|
10
|
+
for how to install Foreman plugins.
|
|
11
|
+
|
|
12
|
+
More instructions later.
|
|
13
|
+
|
|
14
|
+
## Motivation
|
|
15
|
+
|
|
16
|
+
The motivation was poor performance of the Foreman core reporting
|
|
17
|
+
implementation. If reports are processed and stored efficiently, import process
|
|
18
|
+
can be 20x (times) faster with less storage requirements. To achieve that,
|
|
19
|
+
processing is offloaded to smart proxy and reports are stripped down to minimum
|
|
20
|
+
relevant information during the import phase, report is stored as-is
|
|
21
|
+
in a database blob (text) field and only parsed when report show page is
|
|
22
|
+
opened.
|
|
23
|
+
|
|
24
|
+
For more info and discussion about the implementation, read [a thread on
|
|
25
|
+
discourse](https://community.theforeman.org/t/rfc-optimized-reports-storage/15573).
|
|
26
|
+
The design is roughly the following:
|
|
27
|
+
|
|
28
|
+
* Instead of modyfing current core report functionality which is customized by various plugins, new plugin is created.
|
|
29
|
+
* New model class is created: `HostReport`, the reason for that is that this will be better upgrade experience, new tables can be migrated, data can be transformed and legacy tables `Report` and `ConfigReport` can be dropped afterwards. API will be completely different anyway, plugins will no longer handle directly with the model anymore.
|
|
30
|
+
* New API endpoint is created on smart proxy instead of foreman, this will require modifications of ENC script, Ansible and OpenSCAP.
|
|
31
|
+
* All input processing is done on smart proxy, report is then forwarded to Foreman and directly stored in the database.
|
|
32
|
+
* The API remains very same, tho with some differences. Report is sent in JSON, required HTTP arguments "format" and "hostname" are required to allow storing reports without any input parsing (copy directly into database).
|
|
33
|
+
* Report content is stored in new field `body` as plain text (JSON) which is compressed by default by Postgres server.
|
|
34
|
+
* Foreman Puppet report format is modified for better performance - instead of individual log lines, whole report body is stored in one JSON string called "body". Parsing is done during report view/download from Foreman.
|
|
35
|
+
* Report origin is kept in a new `format` field (Foreman Puppet, Ansible, OpenSCAP, Plaintext). There is report format Plaintext which would be simply store report as-is without further processing.
|
|
36
|
+
* Status column converted from integer to 64bit big int.
|
|
37
|
+
* StatusCalculator is kept and extended to use all 64bits.
|
|
38
|
+
* Plugin decides themselves how to store data in the `body` field in such a way that it’s presentable and searchable. Plugin authors should be dis-encouraged from complex (and slow) transformations tho - transformation during view should be encouraged.
|
|
39
|
+
* New model `ReportKeyword(id: int, report_id: int, name: varchar)` is created so plugins can create arbitrary number of keywords which are associated with Report model (M:N).
|
|
40
|
+
* Example keywords:
|
|
41
|
+
* `PuppetHasFailedResource`
|
|
42
|
+
* `PuppetHasFailedRestartResource`
|
|
43
|
+
* `PuppetHasChangedResource`
|
|
44
|
+
* `AnsibleHasUnreachableHost`
|
|
45
|
+
* `AnsibleHasFailedTask`
|
|
46
|
+
* `AnsibleHasChangedTask`
|
|
47
|
+
* `ScapHasFailedRule`
|
|
48
|
+
* `ScapHasOtheredRule`
|
|
49
|
+
* `ScapHasHighSeverityFailure`
|
|
50
|
+
* `ScapHasMediumSeverityFailure`
|
|
51
|
+
* `ScapHasLowSeverityFailure`
|
|
52
|
+
* `ScapFailure:xccdf_org.ssgproject.content_rule_ensure_redhat_gpgkey_installed`
|
|
53
|
+
* `ScapFailure:xccdf_org.ssgproject.content_rule_security_patches_up_to_date`
|
|
54
|
+
* It is completely up to plugin authors which set of keywords they will generate.
|
|
55
|
+
* Keyword generation can be configurable, for example OpenSCAP plugin can have a list of allowed rules to report (the most important ones).
|
|
56
|
+
* The key is to keep amount of keywords at a reasonable level, for example OpenSCAP should not be creating `ScapPassed:xyz` keywords because there will be too many of them.
|
|
57
|
+
* Searching is supported via:
|
|
58
|
+
* Indexed keywords (e.g. `origin = scap and keyword = ScapHasHighSeverityFailure` or simply just the keyword which will be the default scoped_search field)
|
|
59
|
+
* Full text in body (slow but this will work for searching for particular line)
|
|
60
|
+
* Index page (search result) shows also number of failures, changes etc (using StatusCalculator).
|
|
61
|
+
* All searching should be by default scoped to a reasonable time frame (last week) so SQL server can quickly use index on “reported_at” column and do a quick table scan for the rest
|
|
62
|
+
|
|
63
|
+
## Report types
|
|
64
|
+
|
|
65
|
+
There are several report types implemented by this plugin.
|
|
66
|
+
|
|
67
|
+
The API REST endpoint expects the format to be also passed via HTTP argument so
|
|
68
|
+
no input parsing is actually needed to detect format.
|
|
69
|
+
|
|
70
|
+
### Plain
|
|
71
|
+
|
|
72
|
+
The most simple format, whole contents of HTTP payload is stored in body
|
|
73
|
+
database field without any processing. When such format is displayed, it is
|
|
74
|
+
presented as plain/text without any transformations or formatting.
|
|
75
|
+
|
|
76
|
+
For backward compatibility, the plugin will override the core reports API and
|
|
77
|
+
store incoming reports as plaintext so nothing gets lost, reports will be still
|
|
78
|
+
visible (but formatting will be human-unreadable) so users can take actions to
|
|
79
|
+
reconfigure reporting via the new import API.
|
|
80
|
+
|
|
81
|
+
### Standard
|
|
82
|
+
|
|
83
|
+
Standard format that is optimized for fast processing and effective storage.
|
|
84
|
+
Format, host and reported_at fields are all mandatory.
|
|
85
|
+
|
|
86
|
+
Optional field id should be set to unique number or string that represetnts the
|
|
87
|
+
report. Optional proxy field represents FQDN of foreman proxy which processed
|
|
88
|
+
the report.
|
|
89
|
+
|
|
90
|
+
Optional status field represent counts of levels for the report, this is stored
|
|
91
|
+
in 64 bit array. Standard report recognizes the following levels: debug,
|
|
92
|
+
normal, warning and error. If count exceeds the limit if unsigned 16 bits per
|
|
93
|
+
number, report returns "65536+" for this status.
|
|
94
|
+
|
|
95
|
+
Optional field errors may contain an array of strings with errors from initial
|
|
96
|
+
processing and transformation.
|
|
97
|
+
|
|
98
|
+
Field named all_lines is a simple string to minimize memory allocations,
|
|
99
|
+
usually a multi-line string. The standard implementation does not perform any
|
|
100
|
+
formatting or transformations, when implementing a new formatter it is
|
|
101
|
+
recommended to keep all_lines field as a multi-line string and prefix lines
|
|
102
|
+
with additional info like level or timestamp:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
INFO:1611047686:All log lines are represented as a single multi-line string.
|
|
106
|
+
DEBUG:1611047687:This is a second line.
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The standard format is good enough for standard output and error of UNIX
|
|
110
|
+
terminal or syslog output. If multiple lines are expected (e.g. output of files
|
|
111
|
+
for diff), JSON array should be considered instead. See Puppet format below for
|
|
112
|
+
more details.
|
|
113
|
+
|
|
114
|
+
It is recommended to avoid performing transformations during storing of reports
|
|
115
|
+
into database as reporting must be optimized for fast uploads. All
|
|
116
|
+
transformations (e.g. turning all_lines into a HTML table with three columns
|
|
117
|
+
for the example above) should be done when a report is fetched and displayed.
|
|
118
|
+
|
|
119
|
+
Whole JSON is stored in "body" database field, so additional fields can be
|
|
120
|
+
added by implementations based on the standard report. An example standard
|
|
121
|
+
report:
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
{
|
|
125
|
+
"format": "standard",
|
|
126
|
+
"id": "06b77b5d-5df5-4937-9c14-d00a2e7b927f",
|
|
127
|
+
"host": "hostname.example.com",
|
|
128
|
+
"proxy": "foreman-proxy.example.com",
|
|
129
|
+
"reported_at": "2013-05-13 19:49:00 UTC",
|
|
130
|
+
"errors": [],
|
|
131
|
+
"status": {
|
|
132
|
+
"debug": 0,
|
|
133
|
+
"normal": 41,
|
|
134
|
+
"warning": 1,
|
|
135
|
+
"error": 2
|
|
136
|
+
},
|
|
137
|
+
"all_lines": "All log lines are represented as a single multi-line string.\nThis is a second line."
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Puppet
|
|
142
|
+
|
|
143
|
+
Report designed to fullfill needs of the legacy Foreman Puppet report based on
|
|
144
|
+
the standard report. It shares common fields with the standard report (see
|
|
145
|
+
above) but it has the following statuses: applied, restarted, failed,
|
|
146
|
+
failed_restarts, skipped, pending. That's 10 bits per status with maximum value
|
|
147
|
+
of 1024.
|
|
148
|
+
|
|
149
|
+
Instead of all_lines, contents is stored in logs array with every log being
|
|
150
|
+
array of three elements:
|
|
151
|
+
|
|
152
|
+
* level: one of debug, info, notice, warning, err, alert, emerg, crit
|
|
153
|
+
* source: the puppet resource
|
|
154
|
+
* message: the message itself
|
|
155
|
+
|
|
156
|
+
Field resource_statuses only contains list of resources, but not more details.
|
|
157
|
+
|
|
158
|
+
Field evaluation_times contains top 30 resource names and its evaluation times
|
|
159
|
+
plus total sum under name of "Others" for the rest.
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
{
|
|
163
|
+
"format": "puppet",
|
|
164
|
+
"id": "06b77b5d-5df5-4937-9c14-d00a2e7b927f",
|
|
165
|
+
"host": "deb.example.com",
|
|
166
|
+
"proxy": "localhost",
|
|
167
|
+
"reported_at": "2021-01-19T12:40:02.831013816Z",
|
|
168
|
+
"report_format": 10,
|
|
169
|
+
"puppet_version": "6.16.0",
|
|
170
|
+
"environment": "production",
|
|
171
|
+
"metrics": {
|
|
172
|
+
"resources": {
|
|
173
|
+
"name": "resources",
|
|
174
|
+
"label": "Resources",
|
|
175
|
+
"values": [
|
|
176
|
+
[
|
|
177
|
+
"total",
|
|
178
|
+
"Total",
|
|
179
|
+
405
|
|
180
|
+
],
|
|
181
|
+
[
|
|
182
|
+
"skipped",
|
|
183
|
+
"Skipped",
|
|
184
|
+
0
|
|
185
|
+
],
|
|
186
|
+
[
|
|
187
|
+
"failed",
|
|
188
|
+
"Failed",
|
|
189
|
+
1
|
|
190
|
+
],
|
|
191
|
+
[
|
|
192
|
+
"failed_to_restart",
|
|
193
|
+
"Failed to restart",
|
|
194
|
+
0
|
|
195
|
+
],
|
|
196
|
+
[
|
|
197
|
+
"restarted",
|
|
198
|
+
"Restarted",
|
|
199
|
+
0
|
|
200
|
+
],
|
|
201
|
+
[
|
|
202
|
+
"changed",
|
|
203
|
+
"Changed",
|
|
204
|
+
0
|
|
205
|
+
],
|
|
206
|
+
[
|
|
207
|
+
"out_of_sync",
|
|
208
|
+
"Out of sync",
|
|
209
|
+
0
|
|
210
|
+
],
|
|
211
|
+
[
|
|
212
|
+
"scheduled",
|
|
213
|
+
"Scheduled",
|
|
214
|
+
0
|
|
215
|
+
],
|
|
216
|
+
[
|
|
217
|
+
"corrective_change",
|
|
218
|
+
"Corrective change",
|
|
219
|
+
0
|
|
220
|
+
]
|
|
221
|
+
]
|
|
222
|
+
},
|
|
223
|
+
"time": {
|
|
224
|
+
"name": "time",
|
|
225
|
+
"label": "Time",
|
|
226
|
+
"values": [
|
|
227
|
+
[
|
|
228
|
+
"package",
|
|
229
|
+
"Package",
|
|
230
|
+
0.10441856899999999
|
|
231
|
+
],
|
|
232
|
+
[
|
|
233
|
+
"file",
|
|
234
|
+
"File",
|
|
235
|
+
0.6205165560000004
|
|
236
|
+
],
|
|
237
|
+
[
|
|
238
|
+
"anchor",
|
|
239
|
+
"Anchor",
|
|
240
|
+
0.0007514900000000001
|
|
241
|
+
],
|
|
242
|
+
[
|
|
243
|
+
"exec",
|
|
244
|
+
"Exec",
|
|
245
|
+
2.5235192609999997
|
|
246
|
+
],
|
|
247
|
+
[
|
|
248
|
+
"file_line",
|
|
249
|
+
"File line",
|
|
250
|
+
0.000395901
|
|
251
|
+
],
|
|
252
|
+
[
|
|
253
|
+
"mysql_datadir",
|
|
254
|
+
"Mysql datadir",
|
|
255
|
+
0.000380248
|
|
256
|
+
],
|
|
257
|
+
[
|
|
258
|
+
"service",
|
|
259
|
+
"Service",
|
|
260
|
+
0.187978321
|
|
261
|
+
],
|
|
262
|
+
[
|
|
263
|
+
"mysql_user",
|
|
264
|
+
"Mysql user",
|
|
265
|
+
0.000324085
|
|
266
|
+
],
|
|
267
|
+
[
|
|
268
|
+
"mysql_grant",
|
|
269
|
+
"Mysql grant",
|
|
270
|
+
0.0005333
|
|
271
|
+
],
|
|
272
|
+
[
|
|
273
|
+
"group",
|
|
274
|
+
"Group",
|
|
275
|
+
0.0016234639999999998
|
|
276
|
+
],
|
|
277
|
+
[
|
|
278
|
+
"shellvar",
|
|
279
|
+
"Shellvar",
|
|
280
|
+
0.095874028
|
|
281
|
+
],
|
|
282
|
+
[
|
|
283
|
+
"mounttab",
|
|
284
|
+
"Mounttab",
|
|
285
|
+
0.008913022
|
|
286
|
+
],
|
|
287
|
+
[
|
|
288
|
+
"user",
|
|
289
|
+
"User",
|
|
290
|
+
0.014042565000000002
|
|
291
|
+
],
|
|
292
|
+
[
|
|
293
|
+
"ssh_authorized_key",
|
|
294
|
+
"Ssh authorized key",
|
|
295
|
+
0.000289458
|
|
296
|
+
],
|
|
297
|
+
[
|
|
298
|
+
"augeas",
|
|
299
|
+
"Augeas",
|
|
300
|
+
0.075545717
|
|
301
|
+
],
|
|
302
|
+
[
|
|
303
|
+
"gnupg_key",
|
|
304
|
+
"Gnupg key",
|
|
305
|
+
0.021904382
|
|
306
|
+
],
|
|
307
|
+
[
|
|
308
|
+
"mailalias",
|
|
309
|
+
"Mailalias",
|
|
310
|
+
0.000313646
|
|
311
|
+
],
|
|
312
|
+
[
|
|
313
|
+
"sshd_config",
|
|
314
|
+
"Sshd config",
|
|
315
|
+
0.034943459
|
|
316
|
+
],
|
|
317
|
+
[
|
|
318
|
+
"postgresql_conf",
|
|
319
|
+
"Postgresql conf",
|
|
320
|
+
0.001839983
|
|
321
|
+
],
|
|
322
|
+
[
|
|
323
|
+
"concat_file",
|
|
324
|
+
"Concat file",
|
|
325
|
+
0.000588799
|
|
326
|
+
],
|
|
327
|
+
[
|
|
328
|
+
"concat_fragment",
|
|
329
|
+
"Concat fragment",
|
|
330
|
+
0.0027495480000000005
|
|
331
|
+
],
|
|
332
|
+
[
|
|
333
|
+
"rvm_system_ruby",
|
|
334
|
+
"Rvm system ruby",
|
|
335
|
+
1.384307727
|
|
336
|
+
],
|
|
337
|
+
[
|
|
338
|
+
"rvm_alias",
|
|
339
|
+
"Rvm alias",
|
|
340
|
+
1.48978911
|
|
341
|
+
],
|
|
342
|
+
[
|
|
343
|
+
"postgresql_conn_validator",
|
|
344
|
+
"Postgresql conn validator",
|
|
345
|
+
0.066834735
|
|
346
|
+
],
|
|
347
|
+
[
|
|
348
|
+
"postgresql_psql",
|
|
349
|
+
"Postgresql psql",
|
|
350
|
+
0.549115666
|
|
351
|
+
],
|
|
352
|
+
[
|
|
353
|
+
"cron",
|
|
354
|
+
"Cron",
|
|
355
|
+
0.006601505
|
|
356
|
+
],
|
|
357
|
+
[
|
|
358
|
+
"filebucket",
|
|
359
|
+
"Filebucket",
|
|
360
|
+
0.000183308
|
|
361
|
+
],
|
|
362
|
+
[
|
|
363
|
+
"startup_time",
|
|
364
|
+
"Startup time",
|
|
365
|
+
0.733938909
|
|
366
|
+
],
|
|
367
|
+
[
|
|
368
|
+
"node_retrieval",
|
|
369
|
+
"Node retrieval",
|
|
370
|
+
1.081311710178852
|
|
371
|
+
],
|
|
372
|
+
[
|
|
373
|
+
"plugin_sync",
|
|
374
|
+
"Plugin sync",
|
|
375
|
+
1.6488488875329494
|
|
376
|
+
],
|
|
377
|
+
[
|
|
378
|
+
"fact_generation",
|
|
379
|
+
"Fact generation",
|
|
380
|
+
3.781282566487789
|
|
381
|
+
],
|
|
382
|
+
[
|
|
383
|
+
"convert_catalog",
|
|
384
|
+
"Convert catalog",
|
|
385
|
+
0.9406693913042545
|
|
386
|
+
],
|
|
387
|
+
[
|
|
388
|
+
"config_retrieval",
|
|
389
|
+
"Config retrieval",
|
|
390
|
+
6.882053259760141
|
|
391
|
+
],
|
|
392
|
+
[
|
|
393
|
+
"transaction_evaluation",
|
|
394
|
+
"Transaction evaluation",
|
|
395
|
+
7.9571788385510445
|
|
396
|
+
],
|
|
397
|
+
[
|
|
398
|
+
"catalog_application",
|
|
399
|
+
"Catalog application",
|
|
400
|
+
8.027862664312124
|
|
401
|
+
],
|
|
402
|
+
[
|
|
403
|
+
"total",
|
|
404
|
+
"Total",
|
|
405
|
+
23.256689724
|
|
406
|
+
]
|
|
407
|
+
]
|
|
408
|
+
},
|
|
409
|
+
"changes": {
|
|
410
|
+
"name": "changes",
|
|
411
|
+
"label": "Changes",
|
|
412
|
+
"values": [
|
|
413
|
+
[
|
|
414
|
+
"total",
|
|
415
|
+
"Total",
|
|
416
|
+
0
|
|
417
|
+
]
|
|
418
|
+
]
|
|
419
|
+
},
|
|
420
|
+
"events": {
|
|
421
|
+
"name": "events",
|
|
422
|
+
"label": "Events",
|
|
423
|
+
"values": [
|
|
424
|
+
[
|
|
425
|
+
"total",
|
|
426
|
+
"Total",
|
|
427
|
+
0
|
|
428
|
+
],
|
|
429
|
+
[
|
|
430
|
+
"failure",
|
|
431
|
+
"Failure",
|
|
432
|
+
0
|
|
433
|
+
],
|
|
434
|
+
[
|
|
435
|
+
"success",
|
|
436
|
+
"Success",
|
|
437
|
+
0
|
|
438
|
+
]
|
|
439
|
+
]
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
"logs": [
|
|
443
|
+
[
|
|
444
|
+
"notice",
|
|
445
|
+
"//deb.example.com/Puppet",
|
|
446
|
+
"Applied catalog in 8.03 seconds"
|
|
447
|
+
]
|
|
448
|
+
],
|
|
449
|
+
"resource_statuses": [
|
|
450
|
+
"Package[git]",
|
|
451
|
+
"Package[libxml2-dev]",
|
|
452
|
+
"Package[libxslt1-dev]",
|
|
453
|
+
"Package[libkrb5-dev]",
|
|
454
|
+
"Package[libsystemd-dev]",
|
|
455
|
+
"Package[freeipmi]",
|
|
456
|
+
"Package[ipmitool]",
|
|
457
|
+
"Package[firefox-esr]",
|
|
458
|
+
"Package[libvirt-dev]",
|
|
459
|
+
"Package[asciidoc]",
|
|
460
|
+
"Package[bzip2]",
|
|
461
|
+
"Package[unzip]",
|
|
462
|
+
"Package[ansible]",
|
|
463
|
+
"Package[python-virtualenv]",
|
|
464
|
+
"Package[libcurl4-openssl-dev]",
|
|
465
|
+
"Package[libsqlite3-dev]",
|
|
466
|
+
"Package[transifex-client]",
|
|
467
|
+
"Package[java]",
|
|
468
|
+
"Exec[update-java-alternatives]",
|
|
469
|
+
"File_line[java-home-environment]",
|
|
470
|
+
"Cron[puppet]",
|
|
471
|
+
"Filebucket[puppet]"
|
|
472
|
+
],
|
|
473
|
+
"keywords": [
|
|
474
|
+
"PuppetResourceFailed:Package[bzip2]"
|
|
475
|
+
],
|
|
476
|
+
"evaluation_times": [
|
|
477
|
+
[
|
|
478
|
+
"Exec[ruby-2.6.3/update_rubygems]",
|
|
479
|
+
0.662541335
|
|
480
|
+
],
|
|
481
|
+
[
|
|
482
|
+
"Exec[ruby-2.5.1/update_rubygems]",
|
|
483
|
+
0.607295328
|
|
484
|
+
],
|
|
485
|
+
[
|
|
486
|
+
"Exec[ruby-2.7.0/update_rubygems]",
|
|
487
|
+
0.600359435
|
|
488
|
+
],
|
|
489
|
+
[
|
|
490
|
+
"Others",
|
|
491
|
+
0.447703594
|
|
492
|
+
]
|
|
493
|
+
]
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
## Keywords
|
|
498
|
+
|
|
499
|
+
Each report can contain field named keyword, an array of strings. These are
|
|
500
|
+
strings, or tags, stored in a separate table with an index and associated with
|
|
501
|
+
the report. Keyword should be in CamelCase, prefixed with the report type (e.g.
|
|
502
|
+
`Puppet`). Reports must avoid creating too many keywords! Typically only
|
|
503
|
+
failures should be reported.
|
|
504
|
+
|
|
505
|
+
Example keywords:
|
|
506
|
+
|
|
507
|
+
* PuppetHasChange
|
|
508
|
+
* PuppetIsOutOfSync
|
|
509
|
+
* PuppetHasFailure
|
|
510
|
+
* PuppetResourceFailed:Package[git]
|
|
511
|
+
|
|
512
|
+
## Other ideas
|
|
513
|
+
|
|
514
|
+
Reports can have "archived" flag and during import into Foreman, reports could
|
|
515
|
+
be stored into a directories alongside database for archival purposes. Deleting
|
|
516
|
+
old reports was always huge pain in Foreman, but in this scenario it would be
|
|
517
|
+
as easy as "set archived flag and delete body for all records older than X
|
|
518
|
+
days". Those reports could remain in the database for longer period of time
|
|
519
|
+
without any body, they could be side-loaded from the directory when needed.
|
|
520
|
+
|
|
521
|
+
## Contributing
|
|
522
|
+
|
|
523
|
+
Fork and send a Pull Request. Thanks!
|
|
524
|
+
|
|
525
|
+
## License
|
|
526
|
+
|
|
527
|
+
GNU GPLv3, see LICENSE file for more information.
|
|
528
|
+
|
|
529
|
+
## Copyright
|
|
530
|
+
|
|
531
|
+
Copyright (c) 2021 Red Hat, Inc.
|
|
532
|
+
|
|
533
|
+
This program is free software: you can redistribute it and/or modify
|
|
534
|
+
it under the terms of the GNU General Public License as published by
|
|
535
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
536
|
+
(at your option) any later version.
|
|
537
|
+
|
|
538
|
+
This program is distributed in the hope that it will be useful,
|
|
539
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
540
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
541
|
+
GNU General Public License for more details.
|
|
542
|
+
|
|
543
|
+
You should have received a copy of the GNU General Public License
|
|
544
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
545
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Api
|
|
4
|
+
module V2
|
|
5
|
+
class HostReportsController < V2::BaseController
|
|
6
|
+
include Api::Version2
|
|
7
|
+
include Foreman::Controller::CsvResponder
|
|
8
|
+
include Foreman::Controller::SmartProxyAuth
|
|
9
|
+
include ForemanHostReports::Controller::Parameters::HostReport
|
|
10
|
+
|
|
11
|
+
before_action :find_resource, only: %i[destroy]
|
|
12
|
+
before_action :resolve_ids, only: %i[create]
|
|
13
|
+
|
|
14
|
+
add_smart_proxy_filters :create, features: proc { HostReport.authorized_smart_proxy_features }
|
|
15
|
+
|
|
16
|
+
api :GET, '/host_reports/', N_('List host reports')
|
|
17
|
+
param_group :search_and_pagination, ::Api::V2::BaseController
|
|
18
|
+
param :host_id, :identifier, required: false, desc: N_('If provided, filters reports by the host')
|
|
19
|
+
add_scoped_search_description_for(HostReport)
|
|
20
|
+
def index
|
|
21
|
+
options = {}
|
|
22
|
+
options.update(host_id: params[:host_id]) if params[:host_id]
|
|
23
|
+
@host_reports = resource_scope_for_index(options)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
api :GET, '/host_report/:id', N_('Show host report details')
|
|
27
|
+
param :id, :identifier, required: true
|
|
28
|
+
def show
|
|
29
|
+
@host_report = resource_scope.find(params[:id])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def_param_group :host_report do
|
|
33
|
+
param :host_report, Hash, action_aware: true, required: true do
|
|
34
|
+
param :host, String, required: true, desc: N_("Hostname of the report's host origin")
|
|
35
|
+
param :format, HostReport.formats.keys, required: false, desc: N_('Format of the report, e.g. Ansible')
|
|
36
|
+
param :reported_at, String, required: true, desc: N_('UTC time of the report')
|
|
37
|
+
param :status, Integer, required: true, desc: N_('Bitfield with arbitrary amount of status counters')
|
|
38
|
+
param :body, String, required: true, desc: N_('String with JSON formatted body of the report')
|
|
39
|
+
param :proxy, String, required: false, desc: N_('Hostname of the proxy processed the report')
|
|
40
|
+
param :keywords, Array, of: String, required: false, desc: N_('A list of keywords to associate with the report for better searching')
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
api :POST, '/host_report/', N_('Create a host report')
|
|
45
|
+
param_group :host_report, as: :create
|
|
46
|
+
|
|
47
|
+
def create
|
|
48
|
+
params[:host_report].delete(:version)
|
|
49
|
+
the_body = params[:host_report].delete(:body)
|
|
50
|
+
if the_body && !the_body.is_a?(String)
|
|
51
|
+
logger.warn "Report body not as a string, serializing JSON"
|
|
52
|
+
the_body = JSON.pretty_generate(the_body)
|
|
53
|
+
end
|
|
54
|
+
keywords = params[:host_report].delete(:keywords)
|
|
55
|
+
@host_report = HostReport.new(host_report_params.merge(body: the_body))
|
|
56
|
+
if keywords.present?
|
|
57
|
+
keywords_to_insert = keywords.each_with_object([]) do |n, ks|
|
|
58
|
+
ks << { name: n }
|
|
59
|
+
end
|
|
60
|
+
ReportKeyword.upsert_all(keywords_to_insert, unique_by: :name)
|
|
61
|
+
@host_report.report_keyword_ids = ReportKeyword.where(name: keywords).distinct.pluck(:id)
|
|
62
|
+
end
|
|
63
|
+
result = @host_report.save
|
|
64
|
+
@host_report.body = nil
|
|
65
|
+
process_response result
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
api :DELETE, '/host_reports/:id', N_('Delete a host report')
|
|
69
|
+
param :id, :identifier, required: true
|
|
70
|
+
def destroy
|
|
71
|
+
process_response @host_report.destroy
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
api :GET, '/host_reports/export', N_('Export host reports in a CSV file')
|
|
75
|
+
param_group :search_and_pagination, ::Api::V2::BaseController
|
|
76
|
+
add_scoped_search_description_for(HostReport)
|
|
77
|
+
def export
|
|
78
|
+
params[:per_page] = 'all'
|
|
79
|
+
@host_reports = resource_scope_for_index.preload(:host, :proxy)
|
|
80
|
+
csv_response(@host_reports)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def resolve_ids
|
|
86
|
+
hostname = params[:host_report].delete(:host)
|
|
87
|
+
proxyname = params[:host_report].delete(:proxy)
|
|
88
|
+
params[:host_report][:host_id] ||= Host.find_by(name: hostname)&.id
|
|
89
|
+
params[:host_report][:proxy_id] ||= SmartProxy.unscoped.find_by(name: proxyname)&.id
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def resource_scope(options = {})
|
|
93
|
+
options[:permission] = :view_host_reports
|
|
94
|
+
super(options).includes(:host, :proxy).my_reports
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ForemanHostReports
|
|
4
|
+
module Controller
|
|
5
|
+
module Parameters
|
|
6
|
+
module HostReport
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
class_methods do
|
|
10
|
+
def host_report_params_filter
|
|
11
|
+
Foreman::ParameterFilter.new(::HostReport).tap do |filter|
|
|
12
|
+
# body is permitted in controller
|
|
13
|
+
filter.permit :format, :version, :host, :proxy, :reported_at, :status, :proxy_id, :host_id
|
|
14
|
+
filter.permit :keywords => []
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def host_report_params
|
|
20
|
+
self.class.host_report_params_filter.filter_params(params, parameter_filter_context)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|