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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +545 -0
  4. data/app/controllers/api/v2/host_reports_controller.rb +98 -0
  5. data/app/controllers/concerns/foreman_host_reports/controller/parameters/host_report.rb +25 -0
  6. data/app/controllers/host_reports_controller.rb +46 -0
  7. data/app/models/concerns/foreman_host_reports/host_extensions.rb +9 -0
  8. data/app/models/concerns/foreman_host_reports/smart_proxy_extensions.rb +9 -0
  9. data/app/models/host_report.rb +85 -0
  10. data/app/models/report_keyword.rb +11 -0
  11. data/app/views/api/v2/host_reports/base.json.rabl +5 -0
  12. data/app/views/api/v2/host_reports/create.json.rabl +5 -0
  13. data/app/views/api/v2/host_reports/index.json.rabl +5 -0
  14. data/app/views/api/v2/host_reports/main.json.rabl +15 -0
  15. data/app/views/api/v2/host_reports/show.json.rabl +11 -0
  16. data/config/routes.rb +33 -0
  17. data/db/migrate/20210112183526_add_host_reports.rb +36 -0
  18. data/db/migrate/20210616133601_create_report_keywords.rb +12 -0
  19. data/lib/foreman_host_reports/engine.rb +68 -0
  20. data/lib/foreman_host_reports/version.rb +3 -0
  21. data/lib/foreman_host_reports.rb +4 -0
  22. data/lib/tasks/foreman_host_reports_tasks.rake +50 -0
  23. data/locale/Makefile +60 -0
  24. data/locale/en/foreman_host_reports.po +19 -0
  25. data/locale/foreman_host_reports.pot +19 -0
  26. data/locale/gemspec.rb +2 -0
  27. data/package.json +45 -0
  28. data/test/controllers/api/v2/host_reports_controller_test.rb +278 -0
  29. data/test/factories/foreman_host_reports_factories.rb +21 -0
  30. data/test/snapshots/foreman-web.json +918 -0
  31. data/test/test_plugin_helper.rb +11 -0
  32. data/test/unit/foreman_host_reports_test.rb +9 -0
  33. data/webpack/global_index.js +4 -0
  34. data/webpack/global_test_setup.js +11 -0
  35. data/webpack/index.js +0 -0
  36. data/webpack/src/Router/HostReports/Components/HostReportDeleteModal.js +50 -0
  37. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/FormatCell.js +53 -0
  38. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/FormatCell.scss +4 -0
  39. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/formatCellFormatter.js +6 -0
  40. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/hostReportsToShowFormatter.js +13 -0
  41. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/index.js +4 -0
  42. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/reportToShowFormatter.js +13 -0
  43. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/statusFormatter.js +9 -0
  44. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/HostReportsToShowCell.js +32 -0
  45. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/ReportToShowCell.js +27 -0
  46. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/StatusCell.js +77 -0
  47. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/StatusCell.scss +9 -0
  48. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/formatImages.js +7 -0
  49. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/images/ansible.png +0 -0
  50. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/images/puppet.png +0 -0
  51. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/HostReportsTable.js +85 -0
  52. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/HostReportsTableSchema.js +65 -0
  53. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/index.js +28 -0
  54. data/webpack/src/Router/HostReports/IndexPage/IndexPage.js +88 -0
  55. data/webpack/src/Router/HostReports/IndexPage/IndexPageActions.js +55 -0
  56. data/webpack/src/Router/HostReports/IndexPage/IndexPageHelpers.js +50 -0
  57. data/webpack/src/Router/HostReports/IndexPage/IndexPageSelectors.js +86 -0
  58. data/webpack/src/Router/HostReports/IndexPage/constants.js +14 -0
  59. data/webpack/src/Router/HostReports/IndexPage/index.js +98 -0
  60. data/webpack/src/Router/HostReports/ShowPage/Components/HostReportMetrics/HostReportMetrics.scss +3 -0
  61. data/webpack/src/Router/HostReports/ShowPage/Components/HostReportMetrics/index.js +100 -0
  62. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/Ansible.js +77 -0
  63. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/Puppet.js +79 -0
  64. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/helpers.js +17 -0
  65. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/index.js +29 -0
  66. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogsFilter/index.js +109 -0
  67. data/webpack/src/Router/HostReports/ShowPage/ShowPage.js +160 -0
  68. data/webpack/src/Router/HostReports/ShowPage/ShowPageSelectors.js +51 -0
  69. data/webpack/src/Router/HostReports/ShowPage/index.js +75 -0
  70. data/webpack/src/Router/HostReports/constants.js +15 -0
  71. data/webpack/src/Router/routes.js +23 -0
  72. data/webpack/test_setup.js +17 -0
  73. 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