foreman_dlm 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -1
- data/app/controllers/api/v2/dlmlock_events_controller.rb +41 -0
- data/app/controllers/api/v2/dlmlocks_controller.rb +10 -4
- data/app/controllers/concerns/foreman_dlm/update_checkin_time.rb +21 -0
- data/app/controllers/foreman_dlm/application_controller.rb +19 -0
- data/app/controllers/foreman_dlm/dlmlocks_controller.rb +73 -0
- data/app/helpers/foreman_dlm/dlmlock_helper.rb +31 -0
- data/app/jobs/foreman_dlm/refresh_dlmlock_status.rb +17 -0
- data/app/models/concerns/foreman_dlm/dlm_facet_host_extensions.rb +13 -0
- data/app/models/concerns/foreman_dlm/expirable.rb +35 -0
- data/app/models/concerns/foreman_dlm/host_extensions.rb +11 -0
- data/app/models/concerns/foreman_dlm/user_extensions.rb +13 -0
- data/app/models/foreman_dlm/dlm_facet.rb +7 -0
- data/app/models/foreman_dlm/dlmlock.rb +136 -0
- data/app/models/foreman_dlm/dlmlock/update.rb +9 -0
- data/app/models/foreman_dlm/dlmlock_event.rb +23 -0
- data/app/models/host_status/dlmlock_status.rb +44 -0
- data/app/models/settings/dlm.rb +20 -0
- data/app/views/api/v2/dlmlock_events/index.json.rabl +2 -0
- data/app/views/foreman_dlm/api/v2/dlm_facets/base.json.rabl +1 -0
- data/app/views/foreman_dlm/api/v2/dlm_facets/base_with_root.json.rabl +3 -0
- data/app/views/foreman_dlm/api/v2/dlm_facets/show.json.rabl +3 -0
- data/app/views/foreman_dlm/dlmlocks/_details.html.erb +42 -0
- data/app/views/{dlmlocks → foreman_dlm/dlmlocks}/_list.html.erb +3 -1
- data/app/views/{dlmlocks → foreman_dlm/dlmlocks}/index.html.erb +0 -0
- data/app/views/foreman_dlm/dlmlocks/show.html.erb +10 -0
- data/app/views/{dlmlocks → foreman_dlm/dlmlocks}/welcome.html.erb +0 -0
- data/app/views/hosts/_dlmlocks_tab.html.erb +39 -0
- data/config/routes.rb +10 -3
- data/contrib/systemd/foreman-dlm-expire-events.service +10 -0
- data/contrib/systemd/foreman-dlm-expire-events.timer +8 -0
- data/db/migrate/20180627150003_rename_dlmlock_sti_models.rb +9 -0
- data/db/migrate/20180704162345_add_dlmlock_events.rb +11 -0
- data/db/migrate/20180711090022_add_hosts_fk_to_dlmlocks.rb +5 -0
- data/db/migrate/20180711111903_create_dlm_facets.foreman_dlm.rb +10 -0
- data/db/migrate/20180713113208_update_permissions_for_scoped_models.rb +13 -0
- data/lib/foreman_dlm/engine.rb +43 -7
- data/lib/foreman_dlm/version.rb +1 -1
- data/lib/tasks/dlmlock_events.rake +19 -0
- data/test/controllers/api/v2/dlmlocks_controller_test.rb +32 -11
- data/test/controllers/api/v2/dlmlocks_dlmlock_events_controller_test.rb +81 -0
- data/test/controllers/api/v2/hosts_controller_test.rb +28 -0
- data/test/controllers/foreman_dlm/dlmlocks_test.rb +55 -0
- data/test/controllers/hosts_controller_test.rb +12 -0
- data/test/factories/dlm_facets.rb +6 -0
- data/test/factories/dlmlock.rb +6 -2
- data/test/factories/dlmlock_events.rb +13 -0
- data/test/factories/host.rb +7 -0
- data/test/jobs/refresh_dlmlock_status_test.rb +10 -0
- data/test/models/foreman_dlm/dlm_facet_test.rb +13 -0
- data/test/models/foreman_dlm/dlmlock_event_test.rb +19 -0
- data/test/models/foreman_dlm/dlmlock_test.rb +299 -0
- data/test/models/host_managed_test.rb +23 -0
- data/test/models/host_status/dlmlock_status_test.rb +49 -0
- data/test/models/user_test.rb +5 -0
- metadata +63 -15
- data/app/controllers/dlmlocks_controller.rb +0 -12
- data/app/models/dlmlock.rb +0 -79
- data/app/models/dlmlock/update.rb +0 -7
- data/app/views/dlmlocks/_details.html.erb +0 -35
- data/app/views/dlmlocks/show.html.erb +0 -7
- data/test/controllers/dlmlocks_test.rb +0 -24
- data/test/models/dlmlock_test.rb +0 -201
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5be1f946127eae4cab43fdd6ec4337fc1b3980ed40de368aa895c89f0714c459
|
4
|
+
data.tar.gz: 3f9df625f01d5c1bc64f36628388a5de2e1a38f84aac3f00be0a36c9b33a75f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 752c99cf8b373c6aec85803c3bd8c16104bdb50cee490138da55dcf1e924174f27121308f3a319b9632c97843b2e8f69277fc6900f881a8159b2679d6bdacbbf
|
7
|
+
data.tar.gz: 9b4d6b7903914234db467d42d44e33dd3d78126a3219a15b221e5a9080e84881e249b64f6fd506440fa3eb509d00e12eb3d5526445ab8bf455a0e7a5d57042e6
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Foreman Distributed Lock Manager
|
2
2
|
|
3
|
+
[<img src="https://opensourcelogos.aws.dmtech.cloud/dmTECH_opensource_logo%401x.svg" height="21" width="130">](https://www.dmtech.de/)
|
4
|
+
|
3
5
|
This is a plugin for Foreman that allows Foreman to act as a distributed lock manager.
|
4
6
|
Updates are key to security, but updates of an operating system are hard to apply and existing tools are hard to manage at scale. This might lead to a large drift between important security updates becoming available and all your hosts being successfully patched. Security experts recommend to install updates as soon as they come available. The ability to easily update software is the most effective way to improve server security. Automation is key to ensure this goal is reached.
|
5
7
|
This plug-in aims to provide painless updates of the operating system. This keeps your company more secure and frees up resources of your operations team for more important tasks.
|
@@ -36,6 +38,12 @@ To process the HTTP status code in a bash script, you can do something like this
|
|
36
38
|
curl --write-out %{http_code} -H 'Content-Type: application/json' -sS -o /dev/null -X PUT --key $(puppet config print hostprivkey) --cert $(puppet config print hostcert) https://foreman.example.com/api/dlmlocks/test/lock
|
37
39
|
```
|
38
40
|
|
41
|
+
## Client setup
|
42
|
+
|
43
|
+
The Foreman plugin itself just provides a central lock manager. To setup automatic updates, you need to run a script on your clients that tries to acquire a lock in Foreman and takes care of the actual patching process.
|
44
|
+
The `contrib/client` directory in this repo contains a basic script and systemd units that should allow you to get started.
|
45
|
+
A [client counterpart](https://github.com/schlitzered/foreman_dlm_updater) written in Python has been developed by the community to make it easier to use this Foreman plug-in.
|
46
|
+
|
39
47
|
## Note about curl on macOS
|
40
48
|
|
41
49
|
macOS uses curl with a different ssl library which gets problematic testing the cert-signed requests.
|
@@ -68,7 +76,7 @@ Fork and send a Pull Request. Thanks!
|
|
68
76
|
|
69
77
|
## Copyright
|
70
78
|
|
71
|
-
Copyright (c) 2018
|
79
|
+
Copyright (c) 2018 dmTECH GmbH, [dmtech.de](https://www.dmtech.de/)
|
72
80
|
|
73
81
|
This program is free software: you can redistribute it and/or modify
|
74
82
|
it under the terms of the GNU General Public License as published by
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Api
|
2
|
+
module V2
|
3
|
+
class DlmlockEventsController < V2::BaseController
|
4
|
+
include Api::Version2
|
5
|
+
|
6
|
+
before_action :find_required_nested_object
|
7
|
+
|
8
|
+
api :GET, '/dlmlocks/:dlmlock_id/dlmlock_events', N_('List all events for a given DLM lock')
|
9
|
+
param :dlmlock_id, String, :desc => N_('ID of DLM lock')
|
10
|
+
|
11
|
+
def index
|
12
|
+
@events = resource_scope_for_index
|
13
|
+
end
|
14
|
+
|
15
|
+
def resource_class
|
16
|
+
ForemanDlm::DlmlockEvent
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def action_permission
|
22
|
+
case params[:action]
|
23
|
+
when 'index'
|
24
|
+
:view
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def allowed_nested_id
|
31
|
+
['dlmlock_id']
|
32
|
+
end
|
33
|
+
|
34
|
+
def resource_class_for(resource)
|
35
|
+
return ForemanDlm::Dlmlock if resource == 'dlmlock'
|
36
|
+
return ForemanDlm::DlmlockEvent if resource == 'dlmlock_event'
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -4,10 +4,12 @@ module Api
|
|
4
4
|
include Api::Version2
|
5
5
|
include Foreman::Controller::Parameters::Dlmlocks
|
6
6
|
include ::ForemanDlm::FindHostByClientCert
|
7
|
+
include ::ForemanDlm::UpdateCheckinTime
|
7
8
|
|
8
|
-
wrap_parameters Dlmlock, :include => dlmlocks_params_filter.accessible_attributes(parameter_filter_context)
|
9
|
+
wrap_parameters ForemanDlm::Dlmlock, :include => dlmlocks_params_filter.accessible_attributes(parameter_filter_context)
|
9
10
|
|
10
11
|
authorize_host_by_client_cert [:show, :release, :acquire]
|
12
|
+
update_host_checkin_time [:show, :release, :acquire]
|
11
13
|
|
12
14
|
before_action :find_resource, :only => [:show, :update, :destroy]
|
13
15
|
before_action :find_resource_or_create, :only => [:release, :acquire]
|
@@ -17,7 +19,7 @@ module Api
|
|
17
19
|
def_param_group :dlmlock do
|
18
20
|
param :dlmlock, Hash, :required => true, :action_aware => true do
|
19
21
|
param :name, String, :required => true, :desc => N_('Name')
|
20
|
-
param :type, ['Dlmlock:Update'], :required => true, :desc => N_('Type, e.g. Dlmlock:Update')
|
22
|
+
param :type, ['ForemanDlm::Dlmlock:Update'], :required => true, :desc => N_('Type, e.g. ForemanDlm::Dlmlock:Update')
|
21
23
|
param :enabled, :bool, :desc => N_('Enable the lock')
|
22
24
|
end
|
23
25
|
end
|
@@ -41,7 +43,7 @@ module Api
|
|
41
43
|
param_group :dlmlock, :as => :create
|
42
44
|
|
43
45
|
def create
|
44
|
-
@dlmlock = Dlmlock.new(dlmlocks_params)
|
46
|
+
@dlmlock = ForemanDlm::Dlmlock.new(dlmlocks_params)
|
45
47
|
process_response @dlmlock.save
|
46
48
|
end
|
47
49
|
|
@@ -95,6 +97,10 @@ module Api
|
|
95
97
|
process_lock_response @dlmlock.release!(@host)
|
96
98
|
end
|
97
99
|
|
100
|
+
def resource_class
|
101
|
+
ForemanDlm::Dlmlock
|
102
|
+
end
|
103
|
+
|
98
104
|
private
|
99
105
|
|
100
106
|
def resource_finder(scope, id)
|
@@ -108,7 +114,7 @@ module Api
|
|
108
114
|
def find_resource_or_create
|
109
115
|
find_resource
|
110
116
|
rescue ActiveRecord::RecordNotFound
|
111
|
-
@dlmlock = Dlmlock.create(:name => params[:id], :type => 'Dlmlock::Update')
|
117
|
+
@dlmlock = ForemanDlm::Dlmlock.create(:name => params[:id], :type => 'ForemanDlm::Dlmlock::Update')
|
112
118
|
end
|
113
119
|
|
114
120
|
def action_permission
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ForemanDlm
|
2
|
+
module UpdateCheckinTime
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def update_host_checkin_time(actions)
|
7
|
+
before_action(:only => actions) { update_detected_host_checkin_time }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# Updates the last_checkin timestamp of a user
|
14
|
+
def update_detected_host_checkin_time
|
15
|
+
return unless @detected_host
|
16
|
+
facet = @detected_host.dlm_facet || @detected_host.build_dlm_facet
|
17
|
+
facet.save unless facet.persisted?
|
18
|
+
facet.touch(:last_checkin_at)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ForemanDlm
|
2
|
+
class ApplicationController < ::ApplicationController
|
3
|
+
def resource_class
|
4
|
+
self.class.to_s.sub(/Controller$/, '').singularize.constantize
|
5
|
+
end
|
6
|
+
|
7
|
+
def resource_name(resource = resource_class)
|
8
|
+
resource.name.split('::').last.downcase.singularize
|
9
|
+
end
|
10
|
+
|
11
|
+
def controller_name
|
12
|
+
"foreman_dlm_#{super}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def controller_permission
|
16
|
+
super.sub(/^foreman_dlm_/, '')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module ForemanDlm
|
2
|
+
class DlmlocksController < ::ForemanDlm::ApplicationController
|
3
|
+
include ::Foreman::Controller::AutoCompleteSearch
|
4
|
+
|
5
|
+
before_action :setup_search_options, :only => :index
|
6
|
+
before_action :find_resource, :only => [:show, :destroy, :release, :disable, :enable]
|
7
|
+
|
8
|
+
def index
|
9
|
+
@dlmlocks = resource_base_search_and_page(:host)
|
10
|
+
end
|
11
|
+
|
12
|
+
def show; end
|
13
|
+
|
14
|
+
def destroy
|
15
|
+
if @dlmlock.destroy
|
16
|
+
process_success(
|
17
|
+
:success_msg => _('Successfully deleted lock.'),
|
18
|
+
:success_redirect => foreman_dlm_dlmlocks_path
|
19
|
+
)
|
20
|
+
else
|
21
|
+
process_error
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def release
|
26
|
+
if @dlmlock.update(host: nil)
|
27
|
+
process_success(
|
28
|
+
:success_msg => _('Successfully released lock.'),
|
29
|
+
:success_redirect => foreman_dlm_dlmlocks_path
|
30
|
+
)
|
31
|
+
else
|
32
|
+
process_error
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def disable
|
37
|
+
if @dlmlock.disable!
|
38
|
+
process_success(
|
39
|
+
:success_msg => _('Successfully disabled lock.'),
|
40
|
+
:success_redirect => foreman_dlm_dlmlocks_path
|
41
|
+
)
|
42
|
+
else
|
43
|
+
process_error
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def enable
|
48
|
+
if @dlmlock.enable!
|
49
|
+
process_success(
|
50
|
+
:success_msg => _('Successfully enabled lock.'),
|
51
|
+
:success_redirect => foreman_dlm_dlmlocks_path
|
52
|
+
)
|
53
|
+
else
|
54
|
+
process_error
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def model_of_controller
|
59
|
+
ForemanDlm::Dlmlock
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def action_permission
|
65
|
+
case params[:action]
|
66
|
+
when 'release', 'disable', 'enable'
|
67
|
+
:edit
|
68
|
+
else
|
69
|
+
super
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -11,5 +11,36 @@ module ForemanDlm
|
|
11
11
|
return 'text-success' if lock.taken?
|
12
12
|
'text-info'
|
13
13
|
end
|
14
|
+
|
15
|
+
def dlmlock_actions(lock, authorizer)
|
16
|
+
actions = []
|
17
|
+
|
18
|
+
if lock.enabled?
|
19
|
+
actions << display_link_if_authorized(
|
20
|
+
_('Disable'),
|
21
|
+
hash_for_disable_foreman_dlm_dlmlock_path(:id => lock.to_param).merge(auth_object: lock, authorizer: authorizer),
|
22
|
+
method: :put
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
if lock.disabled?
|
27
|
+
actions << display_link_if_authorized(
|
28
|
+
_('Enable'),
|
29
|
+
hash_for_enable_foreman_dlm_dlmlock_path(:id => lock.to_param).merge(auth_object: lock, authorizer: authorizer),
|
30
|
+
method: :put
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
if lock.taken?
|
35
|
+
actions << display_link_if_authorized(
|
36
|
+
_('Release'),
|
37
|
+
hash_for_release_foreman_dlm_dlmlock_path(:id => lock.to_param).merge(auth_object: lock, authorizer: authorizer),
|
38
|
+
method: :put
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
actions << display_delete_if_authorized(hash_for_foreman_dlm_dlmlock_path(:id => lock.to_param).merge(auth_object: lock, authorizer: authorizer), class: 'delete')
|
43
|
+
actions
|
44
|
+
end
|
14
45
|
end
|
15
46
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ForemanDlm
|
2
|
+
class RefreshDlmlockStatus < ApplicationJob
|
3
|
+
queue_as :refresh_dlmlock_status_queue
|
4
|
+
|
5
|
+
def perform(host_ids)
|
6
|
+
Host::Managed.where(id: host_ids).each(&:refresh_dlmlock_status)
|
7
|
+
end
|
8
|
+
|
9
|
+
rescue_from(StandardError) do |error|
|
10
|
+
Foreman::Logging.exception('Failed to refresh Distributed Lock status', error, logger: 'background')
|
11
|
+
end
|
12
|
+
|
13
|
+
def humanized_name
|
14
|
+
_('Refresh Distributed Lock status')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ForemanDlm
|
2
|
+
module DlmFacetHostExtensions
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
has_one :dlm_facet, class_name: '::ForemanDlm::DlmFacet', foreign_key: :host_id, inverse_of: :host, dependent: :destroy
|
7
|
+
|
8
|
+
accepts_nested_attributes_for :dlm_facet, update_only: true, reject_if: ->(attrs) { attrs.values.compact.empty? }
|
9
|
+
|
10
|
+
scoped_search on: :last_checkin_at, relation: :dlm_facet, rename: :last_dlm_checkin_at, complete_value: true, only_explicit: true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ForemanDlm
|
2
|
+
module Expirable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def expire(created_before:, batch_size:, sleep_time:)
|
7
|
+
created_before ||= 1.week
|
8
|
+
batch_size ||= 1000
|
9
|
+
sleep_time ||= 0.2
|
10
|
+
|
11
|
+
total_count = 0
|
12
|
+
event_ids = []
|
13
|
+
|
14
|
+
logger.info "Starting #{to_s.underscore.humanize.pluralize} expiration before #{created_before.ago} in batches of #{batch_size}"
|
15
|
+
|
16
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
17
|
+
loop do
|
18
|
+
event_ids = where(arel_table[:created_at].lt(created_before.ago)).reorder('').limit(batch_size).pluck(:id)
|
19
|
+
|
20
|
+
count = where(id: event_ids).reorder('').delete_all
|
21
|
+
|
22
|
+
total_count += count
|
23
|
+
|
24
|
+
break if event_ids.blank?
|
25
|
+
sleep sleep_time
|
26
|
+
end
|
27
|
+
duration = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) / 60).to_i
|
28
|
+
|
29
|
+
logger.info "Total #{to_s.underscore.humanize.pluralize} expired: #{total_count}, duration: #{duration} min(s)"
|
30
|
+
|
31
|
+
total_count
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -4,12 +4,23 @@ module ForemanDlm
|
|
4
4
|
|
5
5
|
included do
|
6
6
|
has_many :dlmlocks,
|
7
|
+
class_name: 'ForemanDlm::Dlmlock',
|
7
8
|
foreign_key: 'host_id',
|
8
9
|
dependent: :nullify,
|
9
10
|
inverse_of: :host
|
10
11
|
|
12
|
+
has_many :dlmlock_events,
|
13
|
+
class_name: 'ForemanDlm::DlmlockEvent',
|
14
|
+
foreign_key: 'host_id',
|
15
|
+
dependent: :destroy,
|
16
|
+
inverse_of: :host
|
17
|
+
|
11
18
|
define_model_callbacks :lock, :only => :after
|
12
19
|
define_model_callbacks :unlock, :only => :after
|
13
20
|
end
|
21
|
+
|
22
|
+
def refresh_dlmlock_status
|
23
|
+
refresh_statuses([HostStatus::DlmlockStatus])
|
24
|
+
end
|
14
25
|
end
|
15
26
|
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module ForemanDlm
|
2
|
+
class Dlmlock < ApplicationRecord
|
3
|
+
include Authorizable
|
4
|
+
|
5
|
+
def self.humanize_class_name
|
6
|
+
N_('Distributed Lock')
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.dlm_stale_time
|
10
|
+
(Setting::General[:dlm_stale_time] || 4).hours
|
11
|
+
end
|
12
|
+
|
13
|
+
belongs_to_host
|
14
|
+
|
15
|
+
has_many :dlmlock_events,
|
16
|
+
class_name: '::ForemanDlm::DlmlockEvent',
|
17
|
+
foreign_key: 'dlmlock_id',
|
18
|
+
dependent: :destroy,
|
19
|
+
inverse_of: :dlmlock
|
20
|
+
|
21
|
+
validates :name, presence: true, uniqueness: true
|
22
|
+
|
23
|
+
after_save :log_enable_or_disable_event, if: -> { saved_change_to_enabled? }
|
24
|
+
after_save :log_release_and_acquire_events, if: -> { saved_change_to_host_id? }
|
25
|
+
|
26
|
+
def log_enable_or_disable_event
|
27
|
+
event_type = enabled ? :enable : :disable
|
28
|
+
log_event(host, event_type)
|
29
|
+
end
|
30
|
+
|
31
|
+
def log_release_and_acquire_events
|
32
|
+
old_host_id = saved_changes[:host_id].first
|
33
|
+
old_host = Host.find_by(id: old_host_id) if old_host_id
|
34
|
+
log_event(old_host, :release) if old_host
|
35
|
+
log_event(host, :acquire) if host
|
36
|
+
end
|
37
|
+
|
38
|
+
scope :locked, -> { where.not(host_id: nil) }
|
39
|
+
scope :stale, -> { locked.where('updated_at < ?', Time.now.utc - dlm_stale_time) }
|
40
|
+
|
41
|
+
scoped_search :on => :name, :complete_value => true, :default_order => true
|
42
|
+
scoped_search :relation => :host, :on => :name, :complete_value => true, :rename => :host
|
43
|
+
scoped_search :on => :type, :complete_value => true, :default_order => true
|
44
|
+
scoped_search :on => :enabled, :complete_value => { :true => true, :false => false }, :only_explicit => true
|
45
|
+
|
46
|
+
attr_accessor :old
|
47
|
+
|
48
|
+
def acquire!(host)
|
49
|
+
result = atomic_update(nil, host)
|
50
|
+
ForemanDlm::RefreshDlmlockStatus.set(wait: self.class.dlm_stale_time).perform_later([host.id]) if result
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
def release!(host)
|
55
|
+
atomic_update(host, nil)
|
56
|
+
end
|
57
|
+
|
58
|
+
def enable!
|
59
|
+
update(enabled: true)
|
60
|
+
end
|
61
|
+
|
62
|
+
def disable!
|
63
|
+
update(enabled: false)
|
64
|
+
end
|
65
|
+
|
66
|
+
def locked_by?(host)
|
67
|
+
self.host == host
|
68
|
+
end
|
69
|
+
alias acquired_by? locked_by?
|
70
|
+
|
71
|
+
def disabled?
|
72
|
+
!enabled?
|
73
|
+
end
|
74
|
+
|
75
|
+
def locked?
|
76
|
+
host.present?
|
77
|
+
end
|
78
|
+
alias taken? locked?
|
79
|
+
|
80
|
+
def humanized_type
|
81
|
+
_('Generic Lock')
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def atomic_update(old_host, new_host)
|
87
|
+
changes = { host_id: new_host.try(:id) }
|
88
|
+
self.old = dup
|
89
|
+
|
90
|
+
query = {
|
91
|
+
id: id,
|
92
|
+
host_id: [new_host.try(:id), old_host.try(:id)],
|
93
|
+
enabled: true
|
94
|
+
}
|
95
|
+
|
96
|
+
updated = self.class.where(query).update(changes.merge(updated_at: Time.now.utc))
|
97
|
+
|
98
|
+
unless updated.count.zero?
|
99
|
+
reload
|
100
|
+
process_host_change(old_host, new_host)
|
101
|
+
[old_host, new_host].compact.each(&:refresh_dlmlock_status)
|
102
|
+
return true
|
103
|
+
end
|
104
|
+
|
105
|
+
log_event(host, :fail)
|
106
|
+
|
107
|
+
false
|
108
|
+
end
|
109
|
+
|
110
|
+
def process_host_change(old_host, new_host)
|
111
|
+
return if host.try(:id) == old.host.try(:id)
|
112
|
+
|
113
|
+
run_callback(old_host, :unlock) if old.host
|
114
|
+
|
115
|
+
return unless host
|
116
|
+
|
117
|
+
run_callback(new_host, :lock)
|
118
|
+
end
|
119
|
+
|
120
|
+
def log_event(host, event_type)
|
121
|
+
DlmlockEvent.create!(
|
122
|
+
dlmlock: self,
|
123
|
+
event_type: event_type,
|
124
|
+
host: host,
|
125
|
+
user: User.current
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
def run_callback(h, callback)
|
130
|
+
h.run_callbacks callback do
|
131
|
+
logger.debug { "custom hook after_#{callback} on #{h} will be executed if defined." }
|
132
|
+
true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|