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.
- checksums.yaml +4 -4
- data/.babelrc +9 -1
- data/.eslintrc +6 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +6 -0
- data/.storybook/webpack.config.js +51 -54
- data/app/controllers/foreman_tasks/api/tasks_controller.rb +0 -2
- data/app/controllers/foreman_tasks/tasks_controller.rb +6 -1
- data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +8 -0
- data/app/lib/actions/base.rb +7 -0
- data/app/lib/actions/helpers/lifecycle_logging.rb +21 -0
- data/app/lib/actions/proxy_action.rb +4 -2
- data/app/models/foreman_tasks/task.rb +23 -0
- data/app/models/foreman_tasks/task/summarizer.rb +96 -6
- data/app/models/foreman_tasks/triggering.rb +2 -2
- data/app/models/setting/foreman_tasks.rb +8 -1
- data/app/services/foreman_tasks/dashboard_table_filter.rb +47 -0
- data/app/services/foreman_tasks/troubleshooting_help_generator.rb +92 -0
- data/app/services/ui_notifications/tasks.rb +20 -0
- data/app/services/ui_notifications/tasks/task_paused_admin.rb +43 -0
- data/app/services/ui_notifications/tasks/task_paused_owner.rb +30 -0
- data/app/views/foreman_tasks/tasks/_details.html.erb +5 -3
- data/app/views/foreman_tasks/tasks/index.html.erb +13 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20190318153925_add_task_state_updated_at.foreman_tasks.rb +5 -0
- data/db/migrate/20190404132157_add_implicit_varchar_uuid_cast.rb +25 -0
- data/db/seeds.d/30-notification_blueprints.rb +33 -0
- data/foreman-tasks.gemspec +1 -1
- data/lib/foreman_tasks/cleaner.rb +5 -4
- data/lib/foreman_tasks/engine.rb +1 -1
- data/lib/foreman_tasks/test_helpers.rb +10 -0
- data/lib/foreman_tasks/version.rb +1 -1
- data/package.json +14 -11
- data/test/controllers/tasks_controller_test.rb +10 -0
- data/test/foreman_tasks_test_helper.rb +4 -0
- data/test/support/dummy_dynflow_action.rb +29 -0
- data/test/support/history_tasks_builder.rb +42 -0
- data/test/unit/actions/action_with_sub_plans_test.rb +4 -1
- data/test/unit/actions/bulk_action_test.rb +2 -0
- data/test/unit/cleaner_test.rb +15 -0
- data/test/unit/dashboard_table_filter_test.rb +65 -0
- data/test/unit/summarizer_test.rb +39 -0
- data/test/unit/task_test.rb +14 -0
- data/test/unit/troubleshooting_help_generator_test.rb +71 -0
- data/test/unit/ui_notifications_test.rb +86 -0
- data/webpack/ForemanTasks/Components/Chart/Chart.js +128 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.js +20 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.stories.js +51 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.test.js +11 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/__snapshots__/PausedTasksCard.test.js.snap +36 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.js +20 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.stories.js +51 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.test.js +11 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/__snapshots__/RunningTasksCard.test.js.snap +36 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.js +64 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.scss +25 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.stories.js +28 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.test.js +18 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/__snapshots__/ScheduledTasksCard.test.js.snap +94 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.js +89 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.scss +46 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.stories.js +72 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.test.js +48 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCardHelper.js +63 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/__snapshots__/StoppedTasksCard.test.js.snap +973 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.js +96 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.scss +17 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.stories.js +46 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.test.js +43 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/__snapshots__/TasksDonutCard.test.js.snap +183 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.js +166 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.scss +24 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.stories.js +25 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.test.js +40 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartConstants.js +13 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartHelper.js +94 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartHelper.test.js +152 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/__snapshots__/TasksDonutChart.test.js.snap +302 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/__snapshots__/TasksDonutChartHelper.test.js.snap +21 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.fixtures.js +25 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.js +72 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.stories.js +52 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.test.js +21 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/__snapshots__/TasksCardsGrid.test.js.snap +223 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.js +57 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.scss +26 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.stories.js +22 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.test.js +57 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/__snapshots__/TasksLabelsRow.test.js.snap +47 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.js +51 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.stories.js +23 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.test.js +19 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/__snapshots__/TimeDropDown.test.js.snap +85 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.js +33 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.scss +11 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.stories.js +22 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.test.js +15 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/__snapshots__/TasksTimeRow.test.js.snap +41 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.js +77 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.scss +6 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardActions.js +62 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardConstants.js +94 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardHelper.js +78 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardPropTypes.js +13 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardReducer.js +50 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardSelectors.js +44 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboard.test.js +13 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardActions.test.js +37 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardHelper.test.js +36 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardReducer.test.js +58 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardSelectors.test..js +59 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboard.test.js.snap +51 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardActions.test.js.snap +61 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardReducer.test.js.snap +280 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/index.js +25 -0
- data/webpack/ForemanTasks/ForemanTasksReducers.js +10 -0
- data/webpack/ForemanTasks/ForemanTasksSelectors.js +1 -0
- data/webpack/__mocks__/foremanReact/API.js +7 -0
- data/webpack/__mocks__/foremanReact/common/I18n.js +5 -0
- data/webpack/__mocks__/foremanReact/common/helpers.js +3 -0
- data/webpack/index.js +13 -1
- data/webpack/stories/decorators/index.js +1 -0
- data/webpack/stories/decorators/withCardsDecorator.js +14 -0
- data/webpack/stories/index.js +1 -3
- data/webpack/stories/index.scss +6 -0
- metadata +101 -8
- data/webpack/ForemanTasks/components/Hello/Hello.stories.js +0 -5
- data/webpack/ForemanTasks/components/Hello/__tests__/Hello.test.js +0 -11
- data/webpack/ForemanTasks/components/Hello/__tests__/__snapshots__/Hello.test.js.snap +0 -7
- data/webpack/ForemanTasks/components/Hello/index.js +0 -5
|
@@ -18,8 +18,8 @@ module ForemanTasks
|
|
|
18
18
|
ALLOWED_INPUT_TYPES = [:cronline, :monthly, :weekly, :daily, :hourly].freeze
|
|
19
19
|
|
|
20
20
|
TIME_FORMAT = '%Y-%m-%d %H:%M'.freeze
|
|
21
|
-
TIME_REGEXP = /\A\d{4}-\d{2}-\d{2} \d{2}:\d{2}\Z
|
|
22
|
-
DAYS_REGEXP = /\A(\s*\d{1,2}\s*)(,\s*\d{1,2}\s*)*\Z
|
|
21
|
+
TIME_REGEXP = /\A\d{4}-\d{2}-\d{2} \d{2}:\d{2}\Z/.freeze
|
|
22
|
+
DAYS_REGEXP = /\A(\s*\d{1,2}\s*)(,\s*\d{1,2}\s*)*\Z/.freeze
|
|
23
23
|
|
|
24
24
|
has_one :recurring_logic, :foreign_key => :triggering_id, :dependent => :nullify
|
|
25
25
|
|
|
@@ -3,6 +3,8 @@ class Setting::ForemanTasks < Setting
|
|
|
3
3
|
# Check the table exists
|
|
4
4
|
return unless super
|
|
5
5
|
|
|
6
|
+
Setting::BLANK_ATTRS.push('foreman_tasks_troubleshooting_url')
|
|
7
|
+
|
|
6
8
|
transaction do
|
|
7
9
|
[
|
|
8
10
|
set('foreman_tasks_sync_task_timeout', N_('Number of seconds to wait for synchronous task to finish.'), 120),
|
|
@@ -12,7 +14,12 @@ class Setting::ForemanTasks < Setting
|
|
|
12
14
|
set('foreman_tasks_proxy_action_retry_count', N_('Number of attempts to start a task on the smart proxy before failing'), 4),
|
|
13
15
|
set('foreman_tasks_proxy_action_retry_interval', N_('Time in seconds between retries'), 15),
|
|
14
16
|
set('foreman_tasks_proxy_batch_trigger', N_('Allow triggering tasks on the smart proxy in batches'), true),
|
|
15
|
-
set('foreman_tasks_proxy_batch_size', N_('Number of tasks which should be sent to the smart proxy in one request, if foreman_tasks_proxy_batch_trigger is enabled'),
|
|
17
|
+
set('foreman_tasks_proxy_batch_size', N_('Number of tasks which should be sent to the smart proxy in one request, if foreman_tasks_proxy_batch_trigger is enabled'), 100),
|
|
18
|
+
set('foreman_tasks_troubleshooting_url',
|
|
19
|
+
N_('Url pointing to the task troubleshooting documentation. '\
|
|
20
|
+
'It should contain %{label} placeholder, that will be replaced with normalized task label '\
|
|
21
|
+
'(restricted to only alphanumeric characters)). %{version} placeholder is also available.'),
|
|
22
|
+
nil)
|
|
16
23
|
].each { |s| create! s.update(:category => 'Setting::ForemanTasks') }
|
|
17
24
|
end
|
|
18
25
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module ForemanTasks
|
|
2
|
+
# narrows the scope for the tasks table based on params coming from tasks dashboard
|
|
3
|
+
#
|
|
4
|
+
# Supported filters:
|
|
5
|
+
#
|
|
6
|
+
# * :result
|
|
7
|
+
# * :state
|
|
8
|
+
# * :time_horizon - expected format of Hxy, where the xy is the time horizon in hours we're interested in
|
|
9
|
+
# :time_mode can be set to 'recent' to filter the recent tasks, or 'older' (default) to filter earlier ones
|
|
10
|
+
class DashboardTableFilter
|
|
11
|
+
def initialize(scope, params)
|
|
12
|
+
@scope = scope
|
|
13
|
+
@params = params
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def scope
|
|
17
|
+
@new_scope = @scope
|
|
18
|
+
scope_by(:result)
|
|
19
|
+
scope_by(:state)
|
|
20
|
+
scope_by_time
|
|
21
|
+
@new_scope
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def scope_by(field)
|
|
27
|
+
@new_scope = @new_scope.where(field => @params[field]) if @params[field].present?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def scope_by_time
|
|
31
|
+
return if @params[:time_horizon].blank?
|
|
32
|
+
hours = @params[:time_horizon][/\AH(\d{1,2})$/i, 1]
|
|
33
|
+
unless hours
|
|
34
|
+
raise Foreman::Exception, 'Unexpected format of time: should be in form of "H24"'
|
|
35
|
+
end
|
|
36
|
+
timestamp = Time.now.utc - hours.to_i.hours
|
|
37
|
+
case @params[:time_mode]
|
|
38
|
+
when 'recent'
|
|
39
|
+
operator = '>'
|
|
40
|
+
else
|
|
41
|
+
operator = '<'
|
|
42
|
+
search_suffix = 'OR state_updated_at IS NULL'
|
|
43
|
+
end
|
|
44
|
+
@new_scope = @new_scope.where("state_updated_at #{operator} ? #{search_suffix}", timestamp)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module ForemanTasks
|
|
2
|
+
class TroubleshootingHelpGenerator
|
|
3
|
+
class Link
|
|
4
|
+
attr_accessor :name, :title, :description, :href
|
|
5
|
+
|
|
6
|
+
def initialize(name:, title:, description:, href:)
|
|
7
|
+
@name = name
|
|
8
|
+
@title = title
|
|
9
|
+
@description = description
|
|
10
|
+
@href = href
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_h(capitalize_title: false)
|
|
14
|
+
title = capitalize_title ? self.title.titlecase : self.title
|
|
15
|
+
{ name: name, title: title, description: description, href: href, external: true }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Info
|
|
20
|
+
attr_reader :links, :description_lines
|
|
21
|
+
|
|
22
|
+
def initialize
|
|
23
|
+
@description_lines = []
|
|
24
|
+
@links = []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def add_line(line)
|
|
28
|
+
@description_lines << line
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def add_link(link)
|
|
32
|
+
@links << link
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def initialize(action)
|
|
37
|
+
@action = action
|
|
38
|
+
@custom_info = action.troubleshooting_info if action.respond_to?(:troubleshooting_info)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def generate_html
|
|
42
|
+
# rubocop:disable Rails/OutputSafety
|
|
43
|
+
(description + link_descriptions_html).join('<br/>').html_safe
|
|
44
|
+
# rubocop:enable Rails/OutputSafety
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def link_descriptions_html
|
|
48
|
+
links.map do |link|
|
|
49
|
+
link.description % { link: %(<a href="%{href}">%{title}</a>) % link.to_h }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def description
|
|
54
|
+
ret = generic_info.description_lines
|
|
55
|
+
ret += @custom_info.description_lines if @custom_info
|
|
56
|
+
ret
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def links
|
|
60
|
+
links = generic_info.links
|
|
61
|
+
links += @custom_info.links if @custom_info
|
|
62
|
+
links
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def generic_info
|
|
66
|
+
@generic_info ||= Info.new.tap do |i|
|
|
67
|
+
i.add_line _('A paused task represents a process that has not finished properly. '\
|
|
68
|
+
'Any task in paused state can lead to potential inconsistency '\
|
|
69
|
+
'and needs to be resolved.')
|
|
70
|
+
i.add_line _("The recommended approach is to investigate the error messages below and in 'errors' tab, "\
|
|
71
|
+
'address the primary cause of the issue and resume the task.')
|
|
72
|
+
if (link = troubleshooting_link)
|
|
73
|
+
i.add_link(link)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def troubleshooting_link(generic_only: false)
|
|
79
|
+
url_template = Setting[:foreman_tasks_troubleshooting_url]
|
|
80
|
+
return if url_template.blank?
|
|
81
|
+
url = url_template % { label: generic_only ? '' : link_anchor, version: SETTINGS[:version].short }
|
|
82
|
+
Link.new(name: :troubleshooting,
|
|
83
|
+
title: _('troubleshooting documentation'),
|
|
84
|
+
description: _('See %{link} for more details on how to resolve the issue'),
|
|
85
|
+
href: url)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def link_anchor
|
|
89
|
+
@action.label.to_s
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module UINotifications
|
|
2
|
+
module Tasks
|
|
3
|
+
class Base < ::UINotifications::Base
|
|
4
|
+
def initialize(task)
|
|
5
|
+
@subject = @task = task
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def initiator
|
|
9
|
+
User.anonymous_admin
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def troubleshooting_help_generator
|
|
13
|
+
return @troubleshooting_help_generator if defined? @troubleshooting_help_generator
|
|
14
|
+
@troubleshooting_help_generator = if @task.main_action
|
|
15
|
+
ForemanTasks::TroubleshootingHelpGenerator.new(@task.main_action)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module UINotifications
|
|
2
|
+
module Tasks
|
|
3
|
+
class TaskPausedAdmin < Tasks::Base
|
|
4
|
+
def deliver!
|
|
5
|
+
# delete previous notifications about paused tasks first
|
|
6
|
+
Notification.where(notification_blueprint_id: blueprint.id).each(&:destroy)
|
|
7
|
+
notification = ::Notification.new(
|
|
8
|
+
:audience => Notification::AUDIENCE_ADMIN,
|
|
9
|
+
:notification_blueprint => blueprint,
|
|
10
|
+
:initiator => initiator,
|
|
11
|
+
:subject => subject,
|
|
12
|
+
:message => message
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
notification.send(:set_custom_attributes) # to add links from blueprint
|
|
16
|
+
notification.actions['links'] ||= []
|
|
17
|
+
if troubleshooting_help_generator
|
|
18
|
+
troubleshooting_link = troubleshooting_help_generator.troubleshooting_link(generic_only: true)
|
|
19
|
+
notification.actions['links'] << troubleshooting_link.to_h(capitalize_title: true) if troubleshooting_link
|
|
20
|
+
end
|
|
21
|
+
notification.save!
|
|
22
|
+
notification
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def initiator
|
|
26
|
+
User.anonymous_admin
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def blueprint
|
|
30
|
+
@blueprint ||= NotificationBlueprint.unscoped.find_by(:name => 'tasks_paused_admin')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def message
|
|
34
|
+
return @message if @message
|
|
35
|
+
|
|
36
|
+
tasks_count = ForemanTasks::Task.where(state: 'paused').count
|
|
37
|
+
@message = n_('There is %{count} paused task in the system that need attention',
|
|
38
|
+
'There are %{count} paused tasks in the system that need attention',
|
|
39
|
+
tasks_count) % { count: tasks_count }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module UINotifications
|
|
2
|
+
module Tasks
|
|
3
|
+
class TaskPausedOwner < Tasks::Base
|
|
4
|
+
def deliver!
|
|
5
|
+
notification = ::Notification.new(
|
|
6
|
+
:audience => Notification::AUDIENCE_USER,
|
|
7
|
+
:notification_blueprint => blueprint,
|
|
8
|
+
:initiator => initiator,
|
|
9
|
+
:message => message,
|
|
10
|
+
:subject => subject
|
|
11
|
+
)
|
|
12
|
+
notification.send(:set_custom_attributes) # to add links from blueprint
|
|
13
|
+
notification.actions['links'] ||= []
|
|
14
|
+
if troubleshooting_help_generator
|
|
15
|
+
notification.actions['links'].concat(troubleshooting_help_generator.links.map { |l| l.to_h(capitalize_title: true) })
|
|
16
|
+
end
|
|
17
|
+
notification.save!
|
|
18
|
+
notification
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def blueprint
|
|
22
|
+
@blueprint ||= NotificationBlueprint.unscoped.find_by(:name => 'tasks_paused_owner')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def message
|
|
26
|
+
StringParser.new(blueprint.message, subject: subject.action).to_s
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -174,6 +174,8 @@
|
|
|
174
174
|
</div>
|
|
175
175
|
</div>
|
|
176
176
|
|
|
177
|
+
<%= troubleshooting_info %>
|
|
178
|
+
|
|
177
179
|
<% unless @task.humanized[:output].blank? %>
|
|
178
180
|
<div>
|
|
179
181
|
<span class="param-name list-group-item-heading"><%= _("Output") %>:</span>
|
|
@@ -184,10 +186,10 @@
|
|
|
184
186
|
<% end %>
|
|
185
187
|
|
|
186
188
|
<% unless @task.humanized[:errors].blank? %>
|
|
187
|
-
<
|
|
188
|
-
<span class="param-name"><%= _("Errors") %>:</span>
|
|
189
|
+
<p>
|
|
190
|
+
<span class="param-name"><b><%= _("Errors") %>:</b></span>
|
|
189
191
|
<span class="param-value">
|
|
190
192
|
<pre><%=Array(@task.humanized[:errors]).join("\n") %></pre>
|
|
191
193
|
</span>
|
|
192
|
-
</
|
|
194
|
+
</p>
|
|
193
195
|
<% end %>
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
<% content_for(:javascripts) do %>
|
|
2
|
+
<%= webpacked_plugins_js_for :'foreman-tasks' %>
|
|
3
|
+
<% end %>
|
|
4
|
+
<% content_for(:stylesheets) do %>
|
|
5
|
+
<%= webpacked_plugins_css_for :'foreman-tasks' %>
|
|
6
|
+
<% end %>
|
|
7
|
+
<% content_for(:before_search_bar) do %>
|
|
8
|
+
<div id="tasks-dashboard"></div>
|
|
9
|
+
<%= mount_react_component('TasksDashboard', '#tasks-dashboard', {
|
|
10
|
+
yo: 'ya'
|
|
11
|
+
}.to_json) %>
|
|
12
|
+
<% end %>
|
|
13
|
+
|
|
1
14
|
<% title _("Tasks") %>
|
|
2
15
|
<% title_actions csv_link, help_button %>
|
|
3
16
|
<% stylesheet 'foreman_tasks/tasks' %>
|
data/config/routes.rb
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class AddImplicitVarcharUuidCast < ActiveRecord::Migration[5.2]
|
|
2
|
+
def up
|
|
3
|
+
if on_postgresql?
|
|
4
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
5
|
+
CREATE CAST (varchar AS uuid)
|
|
6
|
+
WITH INOUT
|
|
7
|
+
AS IMPLICIT
|
|
8
|
+
SQL
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def down
|
|
13
|
+
if on_postgresql?
|
|
14
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
15
|
+
DROP CAST (varchar AS uuid)
|
|
16
|
+
SQL
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def on_postgresql?
|
|
23
|
+
ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
blueprints = [
|
|
2
|
+
{
|
|
3
|
+
group: N_('Tasks'),
|
|
4
|
+
name: 'tasks_paused_admin',
|
|
5
|
+
message: "DYNAMIC",
|
|
6
|
+
level: 'warning',
|
|
7
|
+
actions:
|
|
8
|
+
{
|
|
9
|
+
links:
|
|
10
|
+
[
|
|
11
|
+
href: "/foreman_tasks/tasks?search=#{CGI.escape('state=paused')}",
|
|
12
|
+
title: N_('List of tasks')
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
{
|
|
18
|
+
group: N_('Tasks'),
|
|
19
|
+
name: 'tasks_paused_owner',
|
|
20
|
+
message: "The task '%{subject}' got paused",
|
|
21
|
+
level: 'warning',
|
|
22
|
+
actions:
|
|
23
|
+
{
|
|
24
|
+
links:
|
|
25
|
+
[
|
|
26
|
+
path_method: :foreman_tasks_task_path,
|
|
27
|
+
title: N_('Task Details')
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
blueprints.each { |blueprint| UINotifications::Seed.new(blueprint).configure }
|
data/foreman-tasks.gemspec
CHANGED
|
@@ -29,7 +29,7 @@ same resource. It also optionally provides Dynflow infrastructure for using it f
|
|
|
29
29
|
s.extra_rdoc_files = Dir['README*', 'LICENSE']
|
|
30
30
|
|
|
31
31
|
s.add_dependency "foreman-tasks-core"
|
|
32
|
-
s.add_dependency "dynflow", '>= 1.2.
|
|
32
|
+
s.add_dependency "dynflow", '>= 1.2.3'
|
|
33
33
|
s.add_dependency "sinatra" # for Dynflow web console
|
|
34
34
|
s.add_dependency "parse-cron", '~> 0.1.4'
|
|
35
35
|
s.add_dependency "get_process_mem" # for memory polling
|
|
@@ -193,15 +193,16 @@ module ForemanTasks
|
|
|
193
193
|
|
|
194
194
|
def orphaned_dynflow_tasks
|
|
195
195
|
db = ForemanTasks.dynflow.world.persistence.adapter.db
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
db.fetch("select dynflow_execution_plans.uuid from dynflow_execution_plans left join "\
|
|
197
|
+
"foreman_tasks_tasks on (dynflow_execution_plans.uuid = foreman_tasks_tasks.external_id) "\
|
|
198
|
+
"where foreman_tasks_tasks.id IS NULL")
|
|
198
199
|
end
|
|
199
200
|
|
|
200
201
|
def prepare_filter
|
|
201
202
|
filter_parts = [filter]
|
|
202
203
|
filter_parts << %(started_at < "#{after.ago.to_s(:db)}") if after > 0
|
|
203
|
-
filter_parts <<
|
|
204
|
-
filter_parts.select(&:present?).join(' AND ')
|
|
204
|
+
filter_parts << "state ^ (#{states.join(',')})" if states.any?
|
|
205
|
+
filter_parts.select(&:present?).map { |segment| "(#{segment})" }.join(' AND ')
|
|
205
206
|
end
|
|
206
207
|
|
|
207
208
|
def start_tracking_progress(name, total = tasks.size)
|
data/lib/foreman_tasks/engine.rb
CHANGED
|
@@ -56,7 +56,7 @@ module ForemanTasks
|
|
|
56
56
|
:last => true
|
|
57
57
|
|
|
58
58
|
security_block :foreman_tasks do |_map|
|
|
59
|
-
permission :view_foreman_tasks, { :'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :show],
|
|
59
|
+
permission :view_foreman_tasks, { :'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :summary, :show],
|
|
60
60
|
:'foreman_tasks/react' => [:index],
|
|
61
61
|
:'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary] }, :resource_type => ForemanTasks::Task.name
|
|
62
62
|
permission :edit_foreman_tasks, { :'foreman_tasks/tasks' => [:resume, :unlock, :force_unlock, :cancel_step, :cancel, :abort],
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
require 'dynflow/testing'
|
|
2
2
|
module ForemanTasks
|
|
3
3
|
module TestHelpers
|
|
4
|
+
def self.use_in_memory_sqlite!
|
|
5
|
+
raise 'the in thread world have already been initialized' if @test_in_thread_world
|
|
6
|
+
@use_in_memory_sqlite = true
|
|
7
|
+
end
|
|
8
|
+
|
|
4
9
|
def self.test_in_thread_world
|
|
5
10
|
return @test_in_thread_world if @test_in_thread_world
|
|
6
11
|
world_config = ForemanTasks.dynflow.config.world_config
|
|
12
|
+
if @use_in_memory_sqlite
|
|
13
|
+
world_config.persistence_adapter = lambda do |*_args|
|
|
14
|
+
::ForemanTasks::Dynflow::Persistence.new('adapter' => 'sqlite', 'database' => ':memory:')
|
|
15
|
+
end
|
|
16
|
+
end
|
|
7
17
|
@test_in_thread_world = ::Dynflow::Testing::InThreadWorld.new(world_config)
|
|
8
18
|
end
|
|
9
19
|
|