foreman_expire_hosts 3.0.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +28 -2
- data/.rubocop_todo.yml +10 -47
- data/README.md +1 -0
- data/app/controllers/concerns/foreman_expire_hosts/host_controller_extensions.rb +11 -13
- data/app/helpers/concerns/foreman_expire_hosts/audits_helper_extensions.rb +2 -2
- data/app/helpers/concerns/foreman_expire_hosts/hosts_helper_extensions.rb +8 -11
- data/app/mailers/expire_hosts_mailer.rb +72 -20
- data/app/models/concerns/foreman_expire_hosts/host_ext.rb +3 -3
- data/app/models/host_status/expiration_status.rb +1 -1
- data/app/models/setting/expire_hosts.rb +2 -2
- data/app/services/foreman_expire_hosts/action/base.rb +57 -0
- data/app/services/foreman_expire_hosts/action/delete_expired_hosts.rb +23 -0
- data/app/services/foreman_expire_hosts/action/stop_expired_hosts.rb +37 -0
- data/app/services/foreman_expire_hosts/expiry_edit_authorizer.rb +21 -0
- data/app/services/foreman_expire_hosts/notification/base.rb +71 -0
- data/app/services/foreman_expire_hosts/notification/deleted_hosts.rb +15 -0
- data/app/services/foreman_expire_hosts/notification/expiry_warning.rb +26 -0
- data/app/services/foreman_expire_hosts/notification/failed_deleted_hosts.rb +15 -0
- data/app/services/foreman_expire_hosts/notification/failed_stopped_hosts.rb +15 -0
- data/app/services/foreman_expire_hosts/notification/stopped_hosts.rb +26 -0
- data/app/services/foreman_expire_hosts/safe_destroy.rb +23 -0
- data/app/services/foreman_expire_hosts/ui_notifications/hosts/base.rb +68 -0
- data/app/services/foreman_expire_hosts/ui_notifications/hosts/expiry_warning.rb +28 -0
- data/app/services/foreman_expire_hosts/ui_notifications/hosts/stopped_host.rb +11 -0
- data/app/views/expire_hosts_mailer/expiry_warning_notification.html.erb +4 -1
- data/app/views/expire_hosts_mailer/stopped_hosts_notification.html.erb +4 -1
- data/db/seeds.d/80_expire_hosts_ui_notification.rb +30 -0
- data/foreman_expire_hosts.gemspec +2 -2
- data/lib/expire_hosts_notifications.rb +7 -104
- data/lib/foreman_expire_hosts/engine.rb +14 -8
- data/lib/foreman_expire_hosts/version.rb +1 -1
- data/lib/tasks/expired_hosts.rake +7 -5
- data/test/factories/foreman_expire_hosts_factories.rb +1 -1
- data/test/functional/concerns/hosts_controller_extensions_test.rb +8 -6
- data/test/lib/expire_hosts_notifications_test.rb +128 -32
- data/test/test_plugin_helper.rb +8 -3
- data/test/unit/concerns/host_extensions_test.rb +17 -17
- data/test/unit/expire_hosts_mailer_test.rb +47 -27
- data/test/unit/expiry_edit_authorizer_test.rb +55 -0
- data/test/unit/host_status/expiration_status_test.rb +1 -1
- data/test/unit/safe_destroy_test.rb +26 -0
- metadata +30 -11
@@ -0,0 +1,23 @@
|
|
1
|
+
module ForemanExpireHosts
|
2
|
+
module Action
|
3
|
+
class DeleteExpiredHosts < Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def selector
|
7
|
+
Host.expired_past_grace_period
|
8
|
+
end
|
9
|
+
|
10
|
+
def action(host)
|
11
|
+
SafeDestroy.new(host).destroy!
|
12
|
+
end
|
13
|
+
|
14
|
+
def success_notification
|
15
|
+
ForemanExpireHosts::Notification::DeletedHosts
|
16
|
+
end
|
17
|
+
|
18
|
+
def failure_notification
|
19
|
+
ForemanExpireHosts::Notification::FailedDeletedHosts
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ForemanExpireHosts
|
2
|
+
module Action
|
3
|
+
class StopExpiredHosts < Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def selector
|
7
|
+
Host.expired
|
8
|
+
end
|
9
|
+
|
10
|
+
def action(host)
|
11
|
+
return true unless host.supports_power_and_running?
|
12
|
+
logger.info "Powering down expired host in grace period #{host}"
|
13
|
+
host.power.stop
|
14
|
+
rescue StandardError
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def success_notification
|
19
|
+
ForemanExpireHosts::Notification::StoppedHosts
|
20
|
+
end
|
21
|
+
|
22
|
+
def failure_notification
|
23
|
+
ForemanExpireHosts::Notification::FailedStoppedHosts
|
24
|
+
end
|
25
|
+
|
26
|
+
def success_notification_options
|
27
|
+
super.merge(
|
28
|
+
:delete_date => (Date.today + days_to_delete_after_expired)
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def days_to_delete_after_expired
|
33
|
+
Setting[:days_to_delete_after_host_expiration].to_i
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ForemanExpireHosts
|
2
|
+
class ExpiryEditAuthorizer
|
3
|
+
attr_accessor :user, :hosts
|
4
|
+
|
5
|
+
def initialize(opts = {})
|
6
|
+
self.user = opts.fetch(:user)
|
7
|
+
self.hosts = opts.fetch(:hosts)
|
8
|
+
end
|
9
|
+
|
10
|
+
def authorized?
|
11
|
+
hosts.each do |host|
|
12
|
+
next unless user.can?(:edit_hosts, host)
|
13
|
+
return true if user.can?(:edit_host_expiry, host)
|
14
|
+
return true if Setting[:can_owner_modify_host_expiry_date] &&
|
15
|
+
((host.owner_type == 'User' && host.owner == user) ||
|
16
|
+
(host.owner_type == 'Usergroup' && host.owner.all_users.include?(user)))
|
17
|
+
end
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module ForemanExpireHosts
|
2
|
+
module Notification
|
3
|
+
class Base
|
4
|
+
attr_accessor :all_hosts, :global_recipients
|
5
|
+
|
6
|
+
def initialize(opts)
|
7
|
+
@all_hosts = opts.fetch(:hosts)
|
8
|
+
@global_recipients = [opts[:to]].flatten.compact
|
9
|
+
end
|
10
|
+
|
11
|
+
def deliver
|
12
|
+
deliver_mail_notifications if respond_to?(:build_mail_notification, true)
|
13
|
+
deliver_ui_notifications if respond_to?(:build_ui_notification, true)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def deliver_mail_notifications
|
19
|
+
hosts_by_recipient(all_hosts).each do |recipient, hosts|
|
20
|
+
deliver_mail_notification(recipient, hosts)
|
21
|
+
end
|
22
|
+
deliver_mail_notification(additional_recipients, all_hosts) if additional_recipients.present?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def deliver_mail_notification(recipient, hosts)
|
27
|
+
build_mail_notification(recipient, hosts).deliver_now
|
28
|
+
rescue SocketError, Net::SMTPError => error
|
29
|
+
message = _('Failed to deliver %{notification_name} for Hosts %{hosts}') % {
|
30
|
+
:notification_name => humanized_name,
|
31
|
+
:hosts => hosts.map(&:name).to_sentence
|
32
|
+
}
|
33
|
+
Foreman::Logging.exception(message, error)
|
34
|
+
end
|
35
|
+
|
36
|
+
def deliver_ui_notifications
|
37
|
+
all_hosts.each do |host|
|
38
|
+
build_ui_notification(host).deliver!
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
delegate :logger, :to => :Rails
|
43
|
+
|
44
|
+
def humanized_name
|
45
|
+
_('Notification')
|
46
|
+
end
|
47
|
+
|
48
|
+
def hosts_by_recipient(hosts)
|
49
|
+
hosts.each_with_object({}) do |host, hash|
|
50
|
+
recipients = recipients_for_host(host)
|
51
|
+
recipients.each do |recipient|
|
52
|
+
hash[recipient] ||= []
|
53
|
+
hash[recipient] << host
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def recipients_for_host(host)
|
59
|
+
return global_recipients if global_recipients.present?
|
60
|
+
return [User.anonymous_admin] if host.owner.blank?
|
61
|
+
return [host.owner] if host.owner_type == 'User'
|
62
|
+
host.owner.all_users
|
63
|
+
end
|
64
|
+
|
65
|
+
def additional_recipients
|
66
|
+
return [] if Setting[:host_expiry_email_recipients].nil?
|
67
|
+
Setting[:host_expiry_email_recipients].split(',').compact.map(&:strip)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ForemanExpireHosts
|
2
|
+
module Notification
|
3
|
+
class DeletedHosts < Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def humanized_name
|
7
|
+
_('Deleted Hosts Notification')
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_mail_notification(recipient, hosts)
|
11
|
+
ExpireHostsMailer.deleted_hosts_notification(recipient, hosts)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ForemanExpireHosts
|
2
|
+
module Notification
|
3
|
+
class ExpiryWarning < Base
|
4
|
+
attr_accessor :expiry_date
|
5
|
+
|
6
|
+
def initialize(opts)
|
7
|
+
super
|
8
|
+
@expiry_date = opts.fetch(:expiry_date)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def humanized_name
|
14
|
+
_('Host Expiry Warning Notification')
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_ui_notification(host)
|
18
|
+
ForemanExpireHosts::UINotifications::Hosts::ExpiryWarning.new(host)
|
19
|
+
end
|
20
|
+
|
21
|
+
def build_mail_notification(recipient, hosts)
|
22
|
+
ExpireHostsMailer.expiry_warning_notification(recipient, expiry_date, hosts)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ForemanExpireHosts
|
2
|
+
module Notification
|
3
|
+
class FailedDeletedHosts < Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def humanized_name
|
7
|
+
_('Failed Deleted Hosts Notification')
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_mail_notification(recipient, hosts)
|
11
|
+
ExpireHostsMailer.failed_to_delete_hosts_notification(recipient, hosts)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ForemanExpireHosts
|
2
|
+
module Notification
|
3
|
+
class FailedStoppedHosts < Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def humanized_name
|
7
|
+
_('Failed Stopped Hosts Notification')
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_mail_notification(recipient, hosts)
|
11
|
+
ExpireHostsMailer.failed_to_stop_hosts_notification(recipient, hosts)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ForemanExpireHosts
|
2
|
+
module Notification
|
3
|
+
class StoppedHosts < Base
|
4
|
+
attr_accessor :delete_date
|
5
|
+
|
6
|
+
def initialize(opts)
|
7
|
+
super
|
8
|
+
@delete_date = opts.fetch(:delete_date)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def humanized_name
|
14
|
+
_('Stopped Hosts Notification')
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_ui_notification(host)
|
18
|
+
ForemanExpireHosts::UINotifications::Hosts::StoppedHost.new(host)
|
19
|
+
end
|
20
|
+
|
21
|
+
def build_mail_notification(recipient, hosts)
|
22
|
+
ExpireHostsMailer.stopped_hosts_notification(recipient, delete_date, hosts)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ForemanExpireHosts
|
2
|
+
class SafeDestroy
|
3
|
+
# See http://projects.theforeman.org/issues/14702 for reasoning.
|
4
|
+
attr_accessor :subject
|
5
|
+
|
6
|
+
def initialize(subject)
|
7
|
+
self.subject = subject
|
8
|
+
end
|
9
|
+
|
10
|
+
def destroy!
|
11
|
+
subject.destroy!
|
12
|
+
rescue ActiveRecord::RecordNotDestroyed => invalid
|
13
|
+
message = _('Failed to delete %{class_name} %{subject}: %{message} - Errors: %{errors}') % {
|
14
|
+
:class_name => subject.class.name,
|
15
|
+
:subject => subject,
|
16
|
+
:message => invalid.message,
|
17
|
+
:errors => invalid.record.errors.full_messages.to_sentence
|
18
|
+
}
|
19
|
+
Foreman::Logging.exception(message, invalid)
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module ForemanExpireHosts
|
2
|
+
module UINotifications
|
3
|
+
module Hosts
|
4
|
+
class Base < ::UINotifications::Hosts::Base
|
5
|
+
private
|
6
|
+
|
7
|
+
def create
|
8
|
+
return add_notification unless find_notification
|
9
|
+
redeliver! if redeliver?
|
10
|
+
find_notification
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_notification
|
14
|
+
::Notification.create!(
|
15
|
+
initiator: initiator,
|
16
|
+
subject: subject,
|
17
|
+
message: parsed_message,
|
18
|
+
audience: audience,
|
19
|
+
notification_blueprint: blueprint
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def message
|
24
|
+
blueprint.message
|
25
|
+
end
|
26
|
+
|
27
|
+
def parsed_message
|
28
|
+
::UINotifications::StringParser.new(
|
29
|
+
message,
|
30
|
+
message_variables
|
31
|
+
).to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def message_variables
|
35
|
+
{
|
36
|
+
subject: subject,
|
37
|
+
initator: initiator
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def update_notification
|
42
|
+
find_notification
|
43
|
+
.update_attributes(expired_at: blueprint.expired_at, :message => parsed_message)
|
44
|
+
end
|
45
|
+
|
46
|
+
def redeliver!
|
47
|
+
recipients = find_notification.notification_recipients
|
48
|
+
recipients.update_all(seen: false) # rubocop:disable Rails/SkipsModelValidations
|
49
|
+
recipients.pluck(:user_id).each do |user_id|
|
50
|
+
::UINotifications::CacheHandler.new(user_id).clear
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def redeliver?
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_notification
|
59
|
+
blueprint.notifications.find_by(subject: subject)
|
60
|
+
end
|
61
|
+
|
62
|
+
def blueprint
|
63
|
+
@blueprint ||= NotificationBlueprint.find_by(name: blueprint_name)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ForemanExpireHosts
|
2
|
+
module UINotifications
|
3
|
+
module Hosts
|
4
|
+
class ExpiryWarning < Base
|
5
|
+
include ActionView::Helpers::DateHelper
|
6
|
+
|
7
|
+
def blueprint_name
|
8
|
+
'expire_hosts_expiry_warning'
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# Nag the user about expiring hosts
|
14
|
+
def redeliver?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def message
|
19
|
+
N_('%{subject} will expire in %{relative_expiry_time}.')
|
20
|
+
end
|
21
|
+
|
22
|
+
def message_variables
|
23
|
+
super.merge(:relative_expiry_time => time_ago_in_words(subject.expired_on))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,4 +1,7 @@
|
|
1
|
-
<%= _('The following hosts will be expired on %s.
|
1
|
+
<%= _('The following hosts will be expired on %s.') % @expiry_date %>
|
2
|
+
<% if @authorized_for_expiry_date_change %>
|
3
|
+
<%= _('Please change their expiry date if you want to keep these hosts alive.') %>
|
4
|
+
<% end %>
|
2
5
|
<br/><br/>
|
3
6
|
|
4
7
|
<%= render 'hosts_table', :hosts => @hosts, :link_to_hosts => true %>
|
@@ -1,4 +1,7 @@
|
|
1
|
-
<%= _('The following hosts have been expired in Foreman and will be stopped for now. These hosts will be destroyed on %s.
|
1
|
+
<%= _('The following hosts have been expired in Foreman and will be stopped for now. These hosts will be destroyed on %s.') % l(@delete_date) %>
|
2
|
+
<% if @authorized_for_expiry_date_change %>
|
3
|
+
<%= _('Please change their expiry date and power them on if you want to keep the hosts.') %>
|
4
|
+
<% end %>
|
2
5
|
<br/><br/>
|
3
6
|
|
4
7
|
<%= render 'hosts_table', :hosts => @hosts, :link_to_hosts => true %>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
group: _('Hosts'),
|
4
|
+
name: 'expire_hosts_expiry_warning',
|
5
|
+
message: _('%{subject} will expire soon.'),
|
6
|
+
level: 'info',
|
7
|
+
actions:
|
8
|
+
{
|
9
|
+
links:
|
10
|
+
[
|
11
|
+
path_method: :host_path,
|
12
|
+
title: _('Details')
|
13
|
+
]
|
14
|
+
}
|
15
|
+
},
|
16
|
+
{
|
17
|
+
group: _('Hosts'),
|
18
|
+
name: 'expire_hosts_stopped_host',
|
19
|
+
message: _('%{subject} was stopped because it expired.'),
|
20
|
+
level: 'info',
|
21
|
+
actions:
|
22
|
+
{
|
23
|
+
links:
|
24
|
+
[
|
25
|
+
path_method: :host_path,
|
26
|
+
title: _('Details')
|
27
|
+
]
|
28
|
+
}
|
29
|
+
}
|
30
|
+
].each { |blueprint| UINotifications::Seed.new(blueprint).configure }
|