foreman-tasks 0.15.1 → 0.15.2

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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/.babelrc +9 -1
  3. data/.eslintrc +6 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +6 -0
  6. data/.storybook/webpack.config.js +51 -54
  7. data/app/controllers/foreman_tasks/api/tasks_controller.rb +0 -2
  8. data/app/controllers/foreman_tasks/tasks_controller.rb +6 -1
  9. data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +8 -0
  10. data/app/lib/actions/base.rb +7 -0
  11. data/app/lib/actions/helpers/lifecycle_logging.rb +21 -0
  12. data/app/lib/actions/proxy_action.rb +4 -2
  13. data/app/models/foreman_tasks/task.rb +23 -0
  14. data/app/models/foreman_tasks/task/summarizer.rb +96 -6
  15. data/app/models/foreman_tasks/triggering.rb +2 -2
  16. data/app/models/setting/foreman_tasks.rb +8 -1
  17. data/app/services/foreman_tasks/dashboard_table_filter.rb +47 -0
  18. data/app/services/foreman_tasks/troubleshooting_help_generator.rb +92 -0
  19. data/app/services/ui_notifications/tasks.rb +20 -0
  20. data/app/services/ui_notifications/tasks/task_paused_admin.rb +43 -0
  21. data/app/services/ui_notifications/tasks/task_paused_owner.rb +30 -0
  22. data/app/views/foreman_tasks/tasks/_details.html.erb +5 -3
  23. data/app/views/foreman_tasks/tasks/index.html.erb +13 -0
  24. data/config/routes.rb +1 -0
  25. data/db/migrate/20190318153925_add_task_state_updated_at.foreman_tasks.rb +5 -0
  26. data/db/migrate/20190404132157_add_implicit_varchar_uuid_cast.rb +25 -0
  27. data/db/seeds.d/30-notification_blueprints.rb +33 -0
  28. data/foreman-tasks.gemspec +1 -1
  29. data/lib/foreman_tasks/cleaner.rb +5 -4
  30. data/lib/foreman_tasks/engine.rb +1 -1
  31. data/lib/foreman_tasks/test_helpers.rb +10 -0
  32. data/lib/foreman_tasks/version.rb +1 -1
  33. data/package.json +14 -11
  34. data/test/controllers/tasks_controller_test.rb +10 -0
  35. data/test/foreman_tasks_test_helper.rb +4 -0
  36. data/test/support/dummy_dynflow_action.rb +29 -0
  37. data/test/support/history_tasks_builder.rb +42 -0
  38. data/test/unit/actions/action_with_sub_plans_test.rb +4 -1
  39. data/test/unit/actions/bulk_action_test.rb +2 -0
  40. data/test/unit/cleaner_test.rb +15 -0
  41. data/test/unit/dashboard_table_filter_test.rb +65 -0
  42. data/test/unit/summarizer_test.rb +39 -0
  43. data/test/unit/task_test.rb +14 -0
  44. data/test/unit/troubleshooting_help_generator_test.rb +71 -0
  45. data/test/unit/ui_notifications_test.rb +86 -0
  46. data/webpack/ForemanTasks/Components/Chart/Chart.js +128 -0
  47. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.js +20 -0
  48. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.stories.js +51 -0
  49. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.test.js +11 -0
  50. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/__snapshots__/PausedTasksCard.test.js.snap +36 -0
  51. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.js +20 -0
  52. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.stories.js +51 -0
  53. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.test.js +11 -0
  54. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/__snapshots__/RunningTasksCard.test.js.snap +36 -0
  55. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.js +64 -0
  56. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.scss +25 -0
  57. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.stories.js +28 -0
  58. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.test.js +18 -0
  59. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/__snapshots__/ScheduledTasksCard.test.js.snap +94 -0
  60. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.js +89 -0
  61. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.scss +46 -0
  62. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.stories.js +72 -0
  63. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.test.js +48 -0
  64. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCardHelper.js +63 -0
  65. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/__snapshots__/StoppedTasksCard.test.js.snap +973 -0
  66. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.js +96 -0
  67. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.scss +17 -0
  68. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.stories.js +46 -0
  69. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.test.js +43 -0
  70. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/__snapshots__/TasksDonutCard.test.js.snap +183 -0
  71. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.js +166 -0
  72. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.scss +24 -0
  73. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.stories.js +25 -0
  74. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.test.js +40 -0
  75. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartConstants.js +13 -0
  76. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartHelper.js +94 -0
  77. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartHelper.test.js +152 -0
  78. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/__snapshots__/TasksDonutChart.test.js.snap +302 -0
  79. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/__snapshots__/TasksDonutChartHelper.test.js.snap +21 -0
  80. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.fixtures.js +25 -0
  81. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.js +72 -0
  82. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.stories.js +52 -0
  83. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.test.js +21 -0
  84. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/__snapshots__/TasksCardsGrid.test.js.snap +223 -0
  85. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.js +57 -0
  86. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.scss +26 -0
  87. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.stories.js +22 -0
  88. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.test.js +57 -0
  89. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/__snapshots__/TasksLabelsRow.test.js.snap +47 -0
  90. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.js +51 -0
  91. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.stories.js +23 -0
  92. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.test.js +19 -0
  93. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/__snapshots__/TimeDropDown.test.js.snap +85 -0
  94. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.js +33 -0
  95. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.scss +11 -0
  96. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.stories.js +22 -0
  97. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.test.js +15 -0
  98. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/__snapshots__/TasksTimeRow.test.js.snap +41 -0
  99. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.js +77 -0
  100. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.scss +6 -0
  101. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardActions.js +62 -0
  102. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardConstants.js +94 -0
  103. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardHelper.js +78 -0
  104. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardPropTypes.js +13 -0
  105. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardReducer.js +50 -0
  106. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardSelectors.js +44 -0
  107. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboard.test.js +13 -0
  108. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardActions.test.js +37 -0
  109. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardHelper.test.js +36 -0
  110. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardReducer.test.js +58 -0
  111. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardSelectors.test..js +59 -0
  112. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboard.test.js.snap +51 -0
  113. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardActions.test.js.snap +61 -0
  114. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardReducer.test.js.snap +280 -0
  115. data/webpack/ForemanTasks/Components/TasksDashboard/index.js +25 -0
  116. data/webpack/ForemanTasks/ForemanTasksReducers.js +10 -0
  117. data/webpack/ForemanTasks/ForemanTasksSelectors.js +1 -0
  118. data/webpack/__mocks__/foremanReact/API.js +7 -0
  119. data/webpack/__mocks__/foremanReact/common/I18n.js +5 -0
  120. data/webpack/__mocks__/foremanReact/common/helpers.js +3 -0
  121. data/webpack/index.js +13 -1
  122. data/webpack/stories/decorators/index.js +1 -0
  123. data/webpack/stories/decorators/withCardsDecorator.js +14 -0
  124. data/webpack/stories/index.js +1 -3
  125. data/webpack/stories/index.scss +6 -0
  126. metadata +101 -8
  127. data/webpack/ForemanTasks/components/Hello/Hello.stories.js +0 -5
  128. data/webpack/ForemanTasks/components/Hello/__tests__/Hello.test.js +0 -11
  129. data/webpack/ForemanTasks/components/Hello/__tests__/__snapshots__/Hello.test.js.snap +0 -7
  130. data/webpack/ForemanTasks/components/Hello/index.js +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9be0418b3db898832481c46ba3f3eae300e2562d7fa240d71cb96876ee825de
4
- data.tar.gz: 61db2a1487d48b7312478c3ad65adffa2230c2fbe9f5a452d200cc4b56428962
3
+ metadata.gz: 9daa93c0112f4c796a124787624ad95f01e241bcc2f80912560eb548cabc080d
4
+ data.tar.gz: 0f2577e225a712c6e5041cb9c5006ac5707bf6f36cde9d9525f5118557037249
5
5
  SHA512:
6
- metadata.gz: d303c00a15e97cbf2c5fe443559a5bd2260298e8b54e52e552040c93fb8885caba6130f7957ce097bfbddafee4a4b3b1bd1a8d41786471b88cd306876496f2f9
7
- data.tar.gz: f4a036829b034d94577d0affce68fdb899bbcdeaf0bef74f8b2358d0cb165ac9e42b7afc8111060210d6eb90bc5289d3af5d49ca6fa88b4f05bd7464a5c94480
6
+ metadata.gz: 5aafc0509dedd3a27aeadbca202772220916f0e3ac20b35f28a8f25ece3190273b7c845f054d0be09ff1298a9cd244430cadf9294566a1cbfa570054a1a0de99
7
+ data.tar.gz: 25705ff172efabb0cbba2725c61571f113a017cc85c4a386825d151dac30799d3c7257b38640c7a5ee7f460bcd1baa3a6e2d7b221397c57debba3e62c2949a0f
data/.babelrc CHANGED
@@ -18,7 +18,15 @@
18
18
  ],
19
19
  "env": {
20
20
  "test": {
21
- "plugins": ["dynamic-import-node"]
21
+ "plugins": [
22
+ ["module-resolver", {
23
+ "alias": {
24
+ "root": ["./"],
25
+ "foremanReact": "./webpack/__mocks__/foremanReact"
26
+ }
27
+ }],
28
+ "dynamic-import-node"
29
+ ]
22
30
  }
23
31
  }
24
32
  }
data/.eslintrc CHANGED
@@ -5,6 +5,12 @@
5
5
  "prettier/prettier": ["error", {
6
6
  "singleQuote": true,
7
7
  "trailingComma": "es5"
8
+ }],
9
+ "import/no-unresolved": ["error", {
10
+ "ignore": ['foremanReact/.*']
11
+ }],
12
+ "import/extensions": ["error", {
13
+ "ignore": ['foremanReact/.*']
8
14
  }]
9
15
  },
10
16
  "settings": {
data/.gitignore CHANGED
@@ -16,3 +16,4 @@ locale/*/*.po.time_stamp
16
16
  node_modules
17
17
  package-lock.json
18
18
  coverage/
19
+ .storybook-dist
data/.rubocop.yml CHANGED
@@ -104,3 +104,9 @@ Style/FormatStringToken:
104
104
 
105
105
  Style/RegexpLiteral:
106
106
  Enabled: false
107
+
108
+ Layout/AlignHash:
109
+ Enabled: false
110
+
111
+ Metrics/ClassLength:
112
+ Enabled: false
@@ -1,66 +1,63 @@
1
1
  let path = require('path');
2
2
 
3
- // Use storybook's default configuration with our customizations
4
- module.exports = (baseConfig, env, defaultConfig) => {
5
-
6
- // overwrite storybook's default import rules
7
- defaultConfig.module.rules = [
8
- {
9
- test: /\.js$/,
10
- exclude: /node_modules/,
11
- loader: 'babel-loader',
12
- options: {
13
- presets: [
14
- path.join(__dirname, '..', 'node_modules/babel-preset-react'),
15
- path.join(__dirname, '..', 'node_modules/babel-preset-env')
16
- ],
17
- plugins: [
18
- path.join(__dirname, '..', 'node_modules/babel-plugin-transform-class-properties'),
19
- path.join(__dirname, '..', 'node_modules/babel-plugin-transform-object-rest-spread'),
20
- path.join(__dirname, '..', 'node_modules/babel-plugin-transform-object-assign'),
21
- path.join(__dirname, '..', 'node_modules/babel-plugin-syntax-dynamic-import')
22
- ]
23
- }
24
- },
25
- {
26
- test: /(\.png|\.gif)$/,
27
- loader: 'url-loader?limit=32767'
28
- },
29
- {
30
- test: /\.css$/,
31
- loaders: ['style-loader', 'css-loader']
32
- },
33
- {
34
- test: /\.scss$/,
35
- loaders: ['style-loader', 'css-loader', {
36
- loader: 'sass-loader',
3
+ module.exports = {
4
+ module: {
5
+ rules: [
6
+ {
7
+ test: /\.js$/,
8
+ exclude: /node_modules/,
9
+ loader: 'babel-loader',
37
10
  options: {
38
- includePaths: [
39
- // teach webpack to resolve patternfly dependencies
40
- path.resolve(__dirname, '..', 'node_modules', 'patternfly', 'dist', 'sass'),
41
- path.resolve(__dirname, '..', 'node_modules', 'bootstrap-sass', 'assets', 'stylesheets'),
42
- path.resolve(__dirname, '..', 'node_modules', 'font-awesome-sass', 'assets', 'stylesheets')
11
+ presets: [
12
+ path.join(__dirname, '..', 'node_modules/babel-preset-react'),
13
+ path.join(__dirname, '..', 'node_modules/babel-preset-env')
14
+ ],
15
+ plugins: [
16
+ path.join(__dirname, '..', 'node_modules/babel-plugin-transform-class-properties'),
17
+ path.join(__dirname, '..', 'node_modules/babel-plugin-transform-object-rest-spread'),
18
+ path.join(__dirname, '..', 'node_modules/babel-plugin-transform-object-assign'),
19
+ path.join(__dirname, '..', 'node_modules/babel-plugin-syntax-dynamic-import')
43
20
  ]
44
21
  }
45
- }]
46
- },
47
- {
48
- test: /\.md$/,
49
- loaders: ['raw-loader']
50
- },
51
- {
52
- test: /(\.ttf|\.woff|\.woff2|\.eot|\.svg|\.jpg)$/,
53
- loaders: ['url-loader']
54
- },
55
- ]
22
+ },
23
+ {
24
+ test: /(\.png|\.gif)$/,
25
+ loader: 'url-loader?limit=32767'
26
+ },
27
+ {
28
+ test: /\.css$/,
29
+ loaders: ['style-loader', 'css-loader'],
30
+ },
31
+ {
32
+ test: /\.scss$/,
33
+ loaders: ['style-loader', 'css-loader', {
34
+ loader: 'sass-loader',
35
+ options: {
36
+ includePaths: [
37
+ // teach webpack to resolve patternfly dependencies
38
+ path.resolve(__dirname, '..', 'node_modules', 'patternfly', 'dist', 'sass'),
39
+ path.resolve(__dirname, '..', 'node_modules', 'bootstrap-sass', 'assets', 'stylesheets'),
40
+ path.resolve(__dirname, '..', 'node_modules', 'font-awesome-sass', 'assets', 'stylesheets')
41
+ ]
42
+ }
43
+ }]
44
+ },
45
+ {
46
+ test: /\.md$/,
47
+ loaders: ['raw-loader']
48
+ },
49
+ {
50
+ test: /(\.ttf|\.woff|\.woff2|\.eot|\.svg|\.jpg)$/,
51
+ loaders: ['url-loader']
52
+ },
53
+ ],
54
+ },
56
55
 
57
- defaultConfig.resolve = {
56
+ resolve: {
58
57
  modules: [
59
58
  path.join(__dirname, '..', 'webpack'),
60
59
  path.join(__dirname, '..', 'node_modules'),
61
60
  'node_modules/',
62
61
  ],
63
- };
64
-
65
- return defaultConfig;
62
+ },
66
63
  };
@@ -1,5 +1,4 @@
1
1
  module ForemanTasks
2
- # rubocop:disable Metrics/ClassLength
3
2
  module Api
4
3
  class TasksController < ::Api::V2::BaseController
5
4
  include ::Foreman::Controller::SmartProxyAuth
@@ -285,5 +284,4 @@ module ForemanTasks
285
284
  end
286
285
  end
287
286
  end
288
- # rubocop:enable Metrics/ClassLength
289
287
  end
@@ -24,6 +24,10 @@ module ForemanTasks
24
24
  end
25
25
  end
26
26
 
27
+ def summary
28
+ render json: Task::Summarizer.new(params[:recent_timeframe].to_i).summary
29
+ end
30
+
27
31
  def sub_tasks
28
32
  task = Task.find(params[:id])
29
33
  @tasks = filter(task.sub_tasks)
@@ -109,7 +113,7 @@ module ForemanTasks
109
113
 
110
114
  def action_permission
111
115
  case params[:action]
112
- when 'sub_tasks'
116
+ when 'sub_tasks', 'summary'
113
117
  :view
114
118
  when 'resume', 'unlock', 'force_unlock', 'cancel_step', 'cancel', 'abort'
115
119
  :edit
@@ -129,6 +133,7 @@ module ForemanTasks
129
133
  def filter(scope, paginate: true)
130
134
  search = current_taxonomy_search
131
135
  search = [search, params[:search]].select(&:present?).join(' AND ')
136
+ scope = DashboardTableFilter.new(scope, params).scope
132
137
  scope = scope.search_for(search, :order => params[:order])
133
138
  scope = scope.paginate(:page => params[:page], :per_page => params[:per_page]) if paginate
134
139
  scope.distinct
@@ -17,6 +17,14 @@ module ForemanTasks
17
17
  content_tag(:i, '&nbsp'.html_safe, :class => "glyphicon #{icon}") + content_tag(:span, recurring_logic.humanized_state, :class => status)
18
18
  end
19
19
 
20
+ def troubleshooting_info
21
+ return if @task.state != 'paused' || @task.main_action.nil?
22
+ helper = TroubleshootingHelpGenerator.new(@task.main_action)
23
+ ret = '<p><b>Troubleshooting</b></p>'
24
+ ret += '<p>%{help}</p>' % { help: helper.generate_html }
25
+ ret.html_safe
26
+ end
27
+
20
28
  def task_result_icon_class(task)
21
29
  return 'task-status pficon-help' if task.state != 'stopped'
22
30
 
@@ -1,6 +1,9 @@
1
1
  module Actions
2
2
  class Base < Dynflow::Action
3
3
  middleware.use ::Actions::Middleware::RailsExecutorWrap
4
+ include Actions::Helpers::LifecycleLogging
5
+
6
+ execution_plan_hooks.use :notify_paused, :on => [:paused]
4
7
 
5
8
  def task
6
9
  @task ||= ::ForemanTasks::Task::DynflowTask.where(:external_id => execution_plan_id).first!
@@ -52,5 +55,9 @@ module Actions
52
55
  def serializer_class
53
56
  ::Actions::Serializers::ActiveRecordSerializer
54
57
  end
58
+
59
+ def notify_paused(*_args)
60
+ task.build_notifications.each(&:deliver!) if root_action?
61
+ end
55
62
  end
56
63
  end
@@ -0,0 +1,21 @@
1
+ module Actions
2
+ module Helpers
3
+ module LifecycleLogging
4
+ def self.included(base)
5
+ base.execution_plan_hooks.use :log_task_state_change
6
+ end
7
+
8
+ def log_task_state_change(execution_plan)
9
+ return unless root_action?
10
+ logger = Rails.application.dynflow.world.action_logger
11
+ task_id = ForemanTasks::Task::DynflowTask.where(external_id: execution_plan.id).pluck(:id).first
12
+
13
+ task_id_parts = []
14
+ task_id_parts << "id: #{task_id}" if task_id
15
+ task_id_parts << "execution_plan_id: #{execution_plan.id}"
16
+ result_info = " result: #{execution_plan.result}" if [:stopped, :paused].include?(execution_plan.state)
17
+ logger.info("Task {label: #{execution_plan.label}, #{task_id_parts.join(', ')}} state changed: #{execution_plan.state} #{result_info}")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -154,7 +154,9 @@ module Actions
154
154
  if output.key?(:proxy_output) || state == :error
155
155
  output.fetch(:proxy_output, {})
156
156
  elsif live && proxy_task_id
157
- proxy_data = proxy.status_of_task(proxy_task_id)['actions'].detect { |action| action['class'] == proxy_action_name }
157
+ proxy_data = proxy.status_of_task(proxy_task_id)['actions'].detect do |action|
158
+ action['class'] == proxy_action_name || action.fetch('input', {})['proxy_operation_name'] == proxy_operation_name
159
+ end
158
160
  proxy_data.fetch('output', {})
159
161
  else
160
162
  {}
@@ -193,7 +195,7 @@ module Actions
193
195
  end
194
196
 
195
197
  def with_batch_triggering?(proxy_version)
196
- (proxy_version[:major] == 1 && proxy_version[:minor] > 20) || proxy_version[:major] > 1 &&
198
+ ((proxy_version[:major] == 1 && proxy_version[:minor] > 20) || proxy_version[:major] > 1) &&
197
199
  input.fetch(:connection_options, {}).fetch(:proxy_batch_triggering, false)
198
200
  end
199
201
 
@@ -15,6 +15,7 @@ module ForemanTasks
15
15
 
16
16
  self.primary_key = :id
17
17
  before_create :generate_id
18
+ before_save :update_state_updated_at
18
19
 
19
20
  belongs_to :parent_task, :class_name => 'ForemanTasks::Task'
20
21
  has_many :sub_tasks, :class_name => 'ForemanTasks::Task', :foreign_key => :parent_task_id, :dependent => :nullify
@@ -39,6 +40,7 @@ module ForemanTasks
39
40
  scoped_search :on => :state, :complete_value => true
40
41
  scoped_search :on => :result, :complete_value => true
41
42
  scoped_search :on => :started_at, :complete_value => false
43
+ scoped_search :on => :state_updated_at, :complete_value => false
42
44
  scoped_search :on => :start_at, :complete_value => false
43
45
  scoped_search :on => :ended_at, :complete_value => false
44
46
  scoped_search :on => :parent_task_id, :complete_value => true
@@ -146,6 +148,23 @@ module ForemanTasks
146
148
  end
147
149
  end
148
150
 
151
+ # used by Foreman notifications framework
152
+ def notification_recipients_ids
153
+ owner_ids
154
+ end
155
+
156
+ def build_notifications
157
+ notifications = []
158
+ if paused?
159
+ owner = self.owner
160
+ if owner && !owner.hidden?
161
+ notifications << UINotifications::Tasks::TaskPausedOwner.new(self)
162
+ end
163
+ notifications << UINotifications::Tasks::TaskPausedAdmin.new(self)
164
+ end
165
+ notifications
166
+ end
167
+
149
168
  def self.search_by_generic_resource(key, operator, value)
150
169
  key = 'resource_type' if key.blank?
151
170
  key_name = connection.quote_column_name(key.sub(/^.*\./, ''))
@@ -269,5 +288,9 @@ module ForemanTasks
269
288
  def generate_id
270
289
  self.id ||= SecureRandom.uuid
271
290
  end
291
+
292
+ def update_state_updated_at
293
+ self.state_updated_at = Time.now.utc if changes.key?(:state)
294
+ end
272
295
  end
273
296
  end
@@ -1,15 +1,105 @@
1
1
  module ForemanTasks
2
2
  class Task::Summarizer
3
- def summarize_by_status(since = nil)
4
- result = ::ForemanTasks::Task.where("result <> 'success'")
5
- .select('count(state) AS count, state, result, max(started_at) AS started_at')
6
- .group(:state, :result).order(:state)
7
- result = result.where('started_at > ?', since) if since
8
- result
3
+ ENSURED_STATE_KEYS = %w[running paused stopped scheduled].freeze
4
+ ENSURED_RESULT_KEYS = %w[success error warning].freeze
5
+
6
+ # number of recent/total tasks for specific classification (state/result)
7
+ class Record
8
+ def initialize
9
+ @data = { recent: 0, total: 0 }
10
+ end
11
+
12
+ def to_h
13
+ @data.dup
14
+ end
15
+
16
+ # updates `field` (one of [:recent, :total]) with counts from aggregated_scope
17
+ def update(field, counts)
18
+ raise ArgumentError, "Unexpected field #{field}" unless @data.key?(field)
19
+ @data[field] = counts.sum(&:count)
20
+ end
21
+ end
22
+
23
+ # number of recent/total tasks for specific state + distribution across different results
24
+ # in `by_result` attribute
25
+ class RecordWithResult < Record
26
+ attr_reader :by_result
27
+
28
+ def initialize
29
+ super
30
+ @by_result = Hash.new { |h, k| h[k] = Record.new }
31
+ ENSURED_RESULT_KEYS.each { |result_value| @by_result[result_value] = Record.new }
32
+ end
33
+
34
+ def to_h
35
+ by_result_hash = @by_result.each.with_object({}) do |(result, record), hash|
36
+ hash[result] = record.to_h
37
+ end
38
+ super.update(by_result: by_result_hash)
39
+ end
40
+
41
+ # Updates the `field` summary + grouping by result
42
+ def update(field, counts)
43
+ super(field, counts)
44
+ counts.group_by(&:result).each do |(result, result_counts)|
45
+ @by_result[result].update(field, result_counts)
46
+ end
47
+ end
48
+ end
49
+
50
+ def initialize(recent_timeframe = 24)
51
+ @recent_timeframe = recent_timeframe.hours
52
+ end
53
+
54
+ def summarize_by_status
55
+ aggregated_scope.where("result <> 'success'")
9
56
  end
10
57
 
11
58
  def latest_tasks_in_errors_warning(limit = 5)
12
59
  ::ForemanTasks::Task.where('result in (?)', %w[error warning]).order('started_at DESC').limit(limit)
13
60
  end
61
+
62
+ # Returns summary of tasks count, grouped by `state` and `result`, if form of:
63
+ #
64
+ # { 'running' => { recent: 3, total: 6 },
65
+ # 'paused' => { recent: 1, total: 3 },
66
+ # 'stopped' => { recent: 3, total: 7,
67
+ # by_result: {
68
+ # 'success' => { recent: 2, total: 4 },
69
+ # 'warning' => { recent: 1, total: 2 },
70
+ # 'error' => { recent: 0, total: 1 },
71
+ # }}
72
+ # 'scheduled' => { recent: 0, total: 3 }}
73
+ #
74
+ def summary
75
+ @summary = Hash.new { |h, state| h[state] = Record.new }
76
+ ENSURED_STATE_KEYS.each do |state|
77
+ @summary[state] = state == 'stopped' ? RecordWithResult.new : Record.new
78
+ end
79
+
80
+ add_to_summary(aggregated_scope, :total)
81
+ add_to_summary(aggregated_recent_scope, :recent)
82
+
83
+ @summary.each.with_object({}) do |(key, record), hash|
84
+ hash[key] = record.to_h
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def add_to_summary(aggregated_scope, field)
91
+ aggregated_scope.group_by(&:state).each do |(state, state_counts)|
92
+ @summary[state].update(field, state_counts)
93
+ end
94
+ end
95
+
96
+ def aggregated_scope
97
+ ::ForemanTasks::Task.select('count(state) AS count, state, result, max(started_at) AS started_at')
98
+ .group(:state, :result).order(:state)
99
+ end
100
+
101
+ def aggregated_recent_scope
102
+ aggregated_scope.where("state_updated_at > ?", Time.now.utc - @recent_timeframe)
103
+ end
14
104
  end
15
105
  end