foreman-tasks 0.15.1 → 0.15.2

Sign up to get free protection for your applications and to get access to all the features.
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