foreman_dlm 0.1.0 → 2.0.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 +18 -6
- data/app/controllers/api/v2/dlmlock_events_controller.rb +42 -0
- data/app/controllers/api/v2/dlmlocks_controller.rb +31 -16
- data/app/controllers/concerns/foreman_dlm/find_host_by_client_cert.rb +7 -13
- data/app/controllers/concerns/foreman_dlm/find_host_by_ip.rb +1 -1
- data/app/controllers/concerns/foreman_dlm/update_checkin_time.rb +22 -0
- data/app/controllers/foreman_dlm/application_controller.rb +19 -0
- data/app/controllers/foreman_dlm/dlmlocks_controller.rb +82 -0
- data/app/helpers/foreman_dlm/dlmlock_helper.rb +33 -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 +36 -0
- data/app/models/concerns/foreman_dlm/host_extensions.rb +21 -2
- data/app/models/concerns/foreman_dlm/host_monitoring_extensions.rb +2 -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/update.rb +9 -0
- data/app/models/foreman_dlm/dlmlock.rb +137 -0
- data/app/models/foreman_dlm/dlmlock_event.rb +23 -0
- data/app/models/host_status/dlmlock_status.rb +44 -0
- data/app/views/api/v2/dlmlock_events/index.json.rabl +2 -0
- data/app/views/api/v2/dlmlocks/show.json.rabl +1 -1
- data/app/views/api/v2/errors/precondition_failed.json.rabl +1 -1
- 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/foreman_dlm/dlmlocks/index.html.erb +2 -0
- data/app/views/foreman_dlm/dlmlocks/show.html.erb +10 -0
- data/app/views/{dlmlocks → foreman_dlm/dlmlocks}/welcome.html.erb +2 -2
- data/app/views/hosts/_dlmlocks_tab.html.erb +39 -0
- data/config/routes.rb +20 -13
- data/contrib/systemd/foreman-dlm-expire-events.service +10 -0
- data/contrib/systemd/foreman-dlm-expire-events.timer +8 -0
- data/db/migrate/20170824084100_add_dlmlock.foreman_dlm.rb +1 -1
- 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 +58 -19
- data/lib/foreman_dlm/version.rb +1 -1
- data/lib/tasks/dlmlock_events.rake +19 -0
- data/lib/tasks/foreman_dlm_tasks.rake +2 -4
- data/test/controllers/api/v2/dlmlocks_controller_test.rb +73 -52
- 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/find_host_by_client_cert_test.rb +2 -2
- 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/integration/foreman_dlm/dlmlocks_test.rb +28 -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 +307 -0
- data/test/models/host_managed_test.rb +41 -0
- data/test/models/host_monitoring_test.rb +1 -1
- data/test/models/host_status/dlmlock_status_test.rb +45 -0
- data/test/models/user_test.rb +5 -0
- data/test/test_plugin_helper.rb +5 -3
- metadata +108 -30
- data/app/controllers/dlmlocks_controller.rb +0 -13
- data/app/models/dlmlock/update.rb +0 -5
- data/app/models/dlmlock.rb +0 -79
- data/app/views/dlmlocks/_details.html.erb +0 -35
- data/app/views/dlmlocks/index.html.erb +0 -2
- 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: 46b88d4ab255520440c5a8aeb4222e8ab62e167027ca0a833eaa03bf4ee32885
|
4
|
+
data.tar.gz: 9b953cde9907b2e7be6282aedbf44fad05d447f13d97e420c62ffd47a3ccf685
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f07de80a511db25bbfdc37d01726a611678b8be5c360c1020ad6ff3e69fdac0d269fe2312fbed24770d8ebec21f05148eb5d629ea69e8b6f2298597adfdea1f
|
7
|
+
data.tar.gz: 9ced134c94780300383e6096e968fdadf1035ac6323f053a0ffcffa8027c660447ce1fd49c830a9d9c103389c7412021e96243fe63e8581fbe743f314b8a605d
|
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.
|
@@ -10,7 +12,9 @@ With this plugin servers can acquire a lock in Foreman to ensure only one server
|
|
10
12
|
|
11
13
|
| Foreman Version | Plugin Version |
|
12
14
|
| --------------- | -------------- |
|
13
|
-
| >= 1.15 |
|
15
|
+
| >= 1.15 | ~> 0.1 |
|
16
|
+
| >= 1.17 | ~> 1.0 |
|
17
|
+
| >= 3.0 | ~> 2.0 |
|
14
18
|
|
15
19
|
## Installation
|
16
20
|
|
@@ -31,10 +35,17 @@ Use the HTTP method `GET` to show a lock, `PUT` to acquire a lock and `DELETE` t
|
|
31
35
|
Foreman will respond with the HTTP status code `200 OK` if the action was successful and `412 Precondition Failed` if the lock could not be acquired or release. This may happen, if the lock is taken by another host.
|
32
36
|
|
33
37
|
To process the HTTP status code in a bash script, you can do something like this:
|
38
|
+
|
34
39
|
```
|
35
40
|
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
|
36
41
|
```
|
37
42
|
|
43
|
+
## Client setup
|
44
|
+
|
45
|
+
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.
|
46
|
+
The `contrib/client` directory in this repo contains a basic script and systemd units that should allow you to get started.
|
47
|
+
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.
|
48
|
+
|
38
49
|
## Note about curl on macOS
|
39
50
|
|
40
51
|
macOS uses curl with a different ssl library which gets problematic testing the cert-signed requests.
|
@@ -50,24 +61,26 @@ $ brew link curl --force
|
|
50
61
|
```
|
51
62
|
|
52
63
|
After that `curl --version` changes from
|
64
|
+
|
53
65
|
```
|
54
66
|
$ curl --version
|
55
67
|
curl 7.54.0 (x86_64-apple-darwin16.0) libcurl/7.54.0 SecureTransport zlib/1.2.8
|
56
68
|
```
|
69
|
+
|
57
70
|
to
|
71
|
+
|
58
72
|
```
|
59
73
|
$ curl --version
|
60
74
|
curl 7.56.1 (x86_64-apple-darwin16.7.0) libcurl/7.56.1 OpenSSL/1.0.2m zlib/1.2.8
|
61
75
|
```
|
62
76
|
|
63
|
-
|
64
77
|
## Contributing
|
65
78
|
|
66
79
|
Fork and send a Pull Request. Thanks!
|
67
80
|
|
68
81
|
## Copyright
|
69
82
|
|
70
|
-
Copyright (c) 2018
|
83
|
+
Copyright (c) 2018 dmTECH GmbH, [dmtech.de](https://www.dmtech.de/)
|
71
84
|
|
72
85
|
This program is free software: you can redistribute it and/or modify
|
73
86
|
it under the terms of the GNU General Public License as published by
|
@@ -76,9 +89,8 @@ the Free Software Foundation, either version 3 of the License, or
|
|
76
89
|
|
77
90
|
This program is distributed in the hope that it will be useful,
|
78
91
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
79
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
92
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
80
93
|
GNU General Public License for more details.
|
81
94
|
|
82
95
|
You should have received a copy of the GNU General Public License
|
83
|
-
along with this program.
|
84
|
-
|
96
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
@@ -0,0 +1,42 @@
|
|
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
|
+
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
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,11 +43,11 @@ 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
|
|
48
|
-
api :PUT,
|
50
|
+
api :PUT, '/dlmlocks/:id/', N_('Update a DLM lock')
|
49
51
|
param :id, String, :required => true, :desc => N_('Id or name of the DLM lock')
|
50
52
|
param_group :dlmlock
|
51
53
|
|
@@ -64,14 +66,14 @@ module Api
|
|
64
66
|
param :id, String, :required => true, :desc => N_('Id or name of the DLM lock')
|
65
67
|
error 200, 'Lock acquired successfully.'
|
66
68
|
error 412, 'Lock could not be acquired.'
|
67
|
-
description <<-
|
69
|
+
description <<-DOCS
|
68
70
|
== Acquire a lock
|
69
71
|
This action acquires a lock.
|
70
72
|
It fails, if the lock is currently taken by another host.
|
71
73
|
|
72
74
|
== Authentication & Host Identification
|
73
75
|
The host is authenticated via a client certificate and identified via the CN of that certificate.
|
74
|
-
|
76
|
+
DOCS
|
75
77
|
|
76
78
|
def acquire
|
77
79
|
process_lock_response @dlmlock.acquire!(@host)
|
@@ -82,19 +84,23 @@ module Api
|
|
82
84
|
error 200, 'Lock released successfully.'
|
83
85
|
error 412, 'Lock could not be released.'
|
84
86
|
|
85
|
-
description <<-
|
87
|
+
description <<-DOCS
|
86
88
|
== Release a lock
|
87
89
|
This action releases a lock.
|
88
90
|
It fails, if the lock is currently taken by another host.
|
89
91
|
|
90
92
|
== Authentication & Host Identification
|
91
93
|
The host is authenticated via a client certificate and identified via the CN of that certificate.
|
92
|
-
|
94
|
+
DOCS
|
93
95
|
|
94
96
|
def release
|
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)
|
@@ -102,21 +108,22 @@ module Api
|
|
102
108
|
rescue ActiveRecord::RecordNotFound
|
103
109
|
result = scope.find_by(:name => id)
|
104
110
|
raise ActiveRecord::RecordNotFound unless result
|
111
|
+
|
105
112
|
result
|
106
113
|
end
|
107
114
|
|
108
115
|
def find_resource_or_create
|
109
116
|
find_resource
|
110
117
|
rescue ActiveRecord::RecordNotFound
|
111
|
-
@dlmlock = Dlmlock.create(:name => params[:id], :type => 'Dlmlock::Update')
|
118
|
+
@dlmlock = ForemanDlm::Dlmlock.create(:name => params[:id], :type => 'ForemanDlm::Dlmlock::Update')
|
112
119
|
end
|
113
120
|
|
114
121
|
def action_permission
|
115
122
|
case params[:action]
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
123
|
+
when 'release', 'acquire'
|
124
|
+
:edit
|
125
|
+
else
|
126
|
+
super
|
120
127
|
end
|
121
128
|
end
|
122
129
|
|
@@ -125,9 +132,17 @@ module Api
|
|
125
132
|
unless @host
|
126
133
|
logger.info 'Denying access because no host could be detected.'
|
127
134
|
if User.current
|
128
|
-
render_error 'access_denied',
|
135
|
+
render_error 'access_denied',
|
136
|
+
:status => :forbidden,
|
137
|
+
:locals => {
|
138
|
+
:details => 'You need to authenticate with a valid client cert. The DN has to match a known host.'
|
139
|
+
}
|
129
140
|
else
|
130
|
-
render_error 'unauthorized',
|
141
|
+
render_error 'unauthorized',
|
142
|
+
:status => :unauthorized,
|
143
|
+
:locals => {
|
144
|
+
:user_login => get_client_cert_hostname
|
145
|
+
}
|
131
146
|
end
|
132
147
|
end
|
133
148
|
true
|
@@ -148,7 +163,7 @@ module Api
|
|
148
163
|
deny_access
|
149
164
|
else
|
150
165
|
render_error 'precondition_failed', :status => :precondition_failed, :locals => {
|
151
|
-
:message => 'Precondition failed. Lock is in invalid state for this operation.'
|
166
|
+
:message => 'Precondition failed. Lock is in invalid state for this operation.'
|
152
167
|
}
|
153
168
|
end
|
154
169
|
end
|
@@ -3,7 +3,7 @@ module ForemanDlm
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
module ClassMethods
|
6
|
-
def authorize_host_by_client_cert(actions,
|
6
|
+
def authorize_host_by_client_cert(actions, _options = {})
|
7
7
|
skip_before_action :require_login, :only => actions, :raise => false
|
8
8
|
skip_before_action :authorize, :only => actions
|
9
9
|
skip_before_action :verify_authenticity_token, :only => actions
|
@@ -39,24 +39,18 @@ module ForemanDlm
|
|
39
39
|
|
40
40
|
return unless hostname
|
41
41
|
|
42
|
-
host ||= Host::Base.
|
43
|
-
|
42
|
+
host ||= Host::Base.find_by(certname: hostname) ||
|
43
|
+
Host::Base.find_by(name: hostname)
|
44
44
|
logger.info { "Found Host #{host} by client cert #{hostname}" } if host
|
45
45
|
host
|
46
46
|
end
|
47
47
|
|
48
48
|
def get_client_cert_hostname
|
49
|
-
|
50
|
-
unless
|
51
|
-
logger.info { "Client certificate is invalid: #{verify}" }
|
52
|
-
return
|
53
|
-
end
|
54
|
-
|
55
|
-
dn = request.env[Setting[:ssl_client_dn_env]]
|
56
|
-
return unless (dn && dn =~ /CN=([^\s\/,]+)/i)
|
49
|
+
client_certificate = Foreman::ClientCertificate.new(request: request)
|
50
|
+
return unless client_certificate.verified?
|
57
51
|
|
58
|
-
hostname =
|
59
|
-
logger.debug "Extracted hostname '#{hostname}' from client certificate."
|
52
|
+
hostname = client_certificate.subject
|
53
|
+
logger.debug "Extracted hostname '#{hostname}' from client certificate." if hostname
|
60
54
|
hostname
|
61
55
|
end
|
62
56
|
end
|
@@ -3,7 +3,7 @@ module ForemanDlm
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
module ClassMethods
|
6
|
-
def authorize_host_by_ip(actions,
|
6
|
+
def authorize_host_by_ip(actions, _options = {})
|
7
7
|
skip_before_action :require_login, :only => actions, :raise => false
|
8
8
|
skip_before_action :authorize, :only => actions
|
9
9
|
skip_before_action :verify_authenticity_token, :only => actions
|
@@ -0,0 +1,22 @@
|
|
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
|
+
|
17
|
+
facet = @detected_host.dlm_facet || @detected_host.build_dlm_facet
|
18
|
+
facet.save unless facet.persisted?
|
19
|
+
facet.touch(:last_checkin_at)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
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,82 @@
|
|
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
|
+
# see https://projects.theforeman.org/issues/25976
|
63
|
+
# can be removed for Foreman 1.22+
|
64
|
+
def auto_complete_controller_name
|
65
|
+
current_version = Gem::Version.new(Foreman::Version.new.notag)
|
66
|
+
return '/foreman_dlm/dlmlocks' if current_version >= Gem::Version.new('1.20') && current_version < Gem::Version.new('1.22')
|
67
|
+
|
68
|
+
controller_name
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def action_permission
|
74
|
+
case params[:action]
|
75
|
+
when 'release', 'disable', 'enable'
|
76
|
+
:edit
|
77
|
+
else
|
78
|
+
super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -3,13 +3,46 @@ module ForemanDlm
|
|
3
3
|
def dlmlock_status_icon_class(lock)
|
4
4
|
return 'ban' if lock.disabled?
|
5
5
|
return 'lock' if lock.taken?
|
6
|
+
|
6
7
|
'unlock'
|
7
8
|
end
|
8
9
|
|
9
10
|
def dlmlock_status_icon_color_class(lock)
|
10
11
|
return 'text-danger' if lock.disabled?
|
11
12
|
return 'text-success' if lock.taken?
|
13
|
+
|
12
14
|
'text-info'
|
13
15
|
end
|
16
|
+
|
17
|
+
def dlmlock_actions(lock, authorizer)
|
18
|
+
actions = []
|
19
|
+
|
20
|
+
if lock.enabled?
|
21
|
+
actions << display_link_if_authorized(
|
22
|
+
_('Disable'),
|
23
|
+
hash_for_disable_foreman_dlm_dlmlock_path(:id => lock.to_param).merge(auth_object: lock, authorizer: authorizer),
|
24
|
+
method: :put
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
if lock.disabled?
|
29
|
+
actions << display_link_if_authorized(
|
30
|
+
_('Enable'),
|
31
|
+
hash_for_enable_foreman_dlm_dlmlock_path(:id => lock.to_param).merge(auth_object: lock, authorizer: authorizer),
|
32
|
+
method: :put
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
if lock.taken?
|
37
|
+
actions << display_link_if_authorized(
|
38
|
+
_('Release'),
|
39
|
+
hash_for_release_foreman_dlm_dlmlock_path(:id => lock.to_param).merge(auth_object: lock, authorizer: authorizer),
|
40
|
+
method: :put
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
actions << display_delete_if_authorized(hash_for_foreman_dlm_dlmlock_path(:id => lock.to_param).merge(auth_object: lock, authorizer: authorizer), class: 'delete')
|
45
|
+
actions
|
46
|
+
end
|
14
47
|
end
|
15
48
|
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,36 @@
|
|
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
|
+
|
26
|
+
sleep sleep_time
|
27
|
+
end
|
28
|
+
duration = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) / 60).to_i
|
29
|
+
|
30
|
+
logger.info "Total #{to_s.underscore.humanize.pluralize} expired: #{total_count}, duration: #{duration} min(s)"
|
31
|
+
|
32
|
+
total_count
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -4,11 +4,30 @@ module ForemanDlm
|
|
4
4
|
|
5
5
|
included do
|
6
6
|
has_many :dlmlocks,
|
7
|
-
|
8
|
-
|
7
|
+
class_name: 'ForemanDlm::Dlmlock',
|
8
|
+
foreign_key: 'host_id',
|
9
|
+
dependent: :nullify,
|
10
|
+
inverse_of: :host
|
11
|
+
|
12
|
+
has_many :dlmlock_events,
|
13
|
+
class_name: 'ForemanDlm::DlmlockEvent',
|
14
|
+
foreign_key: 'host_id',
|
15
|
+
dependent: :destroy,
|
16
|
+
inverse_of: :host
|
9
17
|
|
10
18
|
define_model_callbacks :lock, :only => :after
|
11
19
|
define_model_callbacks :unlock, :only => :after
|
12
20
|
end
|
21
|
+
|
22
|
+
def can_acquire_update_locks?
|
23
|
+
param = host_param('can_acquire_update_locks')
|
24
|
+
return true if param.blank?
|
25
|
+
|
26
|
+
Foreman::Cast.to_bool(param)
|
27
|
+
end
|
28
|
+
|
29
|
+
def refresh_dlmlock_status
|
30
|
+
refresh_statuses([HostStatus::DlmlockStatus])
|
31
|
+
end
|
13
32
|
end
|
14
33
|
end
|
@@ -9,6 +9,7 @@ module ForemanDlm
|
|
9
9
|
|
10
10
|
def add_lock_monitoring_downtime
|
11
11
|
return unless monitored?
|
12
|
+
|
12
13
|
logger.info "Setting Monitoring downtime for #{self}"
|
13
14
|
monitoring.set_downtime_host(self, lock_monitoring_downtime_options)
|
14
15
|
true
|
@@ -18,6 +19,7 @@ module ForemanDlm
|
|
18
19
|
|
19
20
|
def remove_lock_monitoring_downtime
|
20
21
|
return unless monitored?
|
22
|
+
|
21
23
|
logger.info "Deleting Monitoring downtime for #{self}"
|
22
24
|
monitoring.del_downtime_host(self, lock_monitoring_downtime_options)
|
23
25
|
true
|