foreman_expire_hosts 3.0.0 → 4.0.0
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/.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 }
|