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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -6
  3. data/app/controllers/api/v2/dlmlock_events_controller.rb +42 -0
  4. data/app/controllers/api/v2/dlmlocks_controller.rb +31 -16
  5. data/app/controllers/concerns/foreman_dlm/find_host_by_client_cert.rb +7 -13
  6. data/app/controllers/concerns/foreman_dlm/find_host_by_ip.rb +1 -1
  7. data/app/controllers/concerns/foreman_dlm/update_checkin_time.rb +22 -0
  8. data/app/controllers/foreman_dlm/application_controller.rb +19 -0
  9. data/app/controllers/foreman_dlm/dlmlocks_controller.rb +82 -0
  10. data/app/helpers/foreman_dlm/dlmlock_helper.rb +33 -0
  11. data/app/jobs/foreman_dlm/refresh_dlmlock_status.rb +17 -0
  12. data/app/models/concerns/foreman_dlm/dlm_facet_host_extensions.rb +13 -0
  13. data/app/models/concerns/foreman_dlm/expirable.rb +36 -0
  14. data/app/models/concerns/foreman_dlm/host_extensions.rb +21 -2
  15. data/app/models/concerns/foreman_dlm/host_monitoring_extensions.rb +2 -0
  16. data/app/models/concerns/foreman_dlm/user_extensions.rb +13 -0
  17. data/app/models/foreman_dlm/dlm_facet.rb +7 -0
  18. data/app/models/foreman_dlm/dlmlock/update.rb +9 -0
  19. data/app/models/foreman_dlm/dlmlock.rb +137 -0
  20. data/app/models/foreman_dlm/dlmlock_event.rb +23 -0
  21. data/app/models/host_status/dlmlock_status.rb +44 -0
  22. data/app/views/api/v2/dlmlock_events/index.json.rabl +2 -0
  23. data/app/views/api/v2/dlmlocks/show.json.rabl +1 -1
  24. data/app/views/api/v2/errors/precondition_failed.json.rabl +1 -1
  25. data/app/views/foreman_dlm/api/v2/dlm_facets/base.json.rabl +1 -0
  26. data/app/views/foreman_dlm/api/v2/dlm_facets/base_with_root.json.rabl +3 -0
  27. data/app/views/foreman_dlm/api/v2/dlm_facets/show.json.rabl +3 -0
  28. data/app/views/foreman_dlm/dlmlocks/_details.html.erb +42 -0
  29. data/app/views/{dlmlocks → foreman_dlm/dlmlocks}/_list.html.erb +3 -1
  30. data/app/views/foreman_dlm/dlmlocks/index.html.erb +2 -0
  31. data/app/views/foreman_dlm/dlmlocks/show.html.erb +10 -0
  32. data/app/views/{dlmlocks → foreman_dlm/dlmlocks}/welcome.html.erb +2 -2
  33. data/app/views/hosts/_dlmlocks_tab.html.erb +39 -0
  34. data/config/routes.rb +20 -13
  35. data/contrib/systemd/foreman-dlm-expire-events.service +10 -0
  36. data/contrib/systemd/foreman-dlm-expire-events.timer +8 -0
  37. data/db/migrate/20170824084100_add_dlmlock.foreman_dlm.rb +1 -1
  38. data/db/migrate/20180627150003_rename_dlmlock_sti_models.rb +9 -0
  39. data/db/migrate/20180704162345_add_dlmlock_events.rb +11 -0
  40. data/db/migrate/20180711090022_add_hosts_fk_to_dlmlocks.rb +5 -0
  41. data/db/migrate/20180711111903_create_dlm_facets.foreman_dlm.rb +10 -0
  42. data/db/migrate/20180713113208_update_permissions_for_scoped_models.rb +13 -0
  43. data/lib/foreman_dlm/engine.rb +58 -19
  44. data/lib/foreman_dlm/version.rb +1 -1
  45. data/lib/tasks/dlmlock_events.rake +19 -0
  46. data/lib/tasks/foreman_dlm_tasks.rake +2 -4
  47. data/test/controllers/api/v2/dlmlocks_controller_test.rb +73 -52
  48. data/test/controllers/api/v2/dlmlocks_dlmlock_events_controller_test.rb +81 -0
  49. data/test/controllers/api/v2/hosts_controller_test.rb +28 -0
  50. data/test/controllers/find_host_by_client_cert_test.rb +2 -2
  51. data/test/controllers/foreman_dlm/dlmlocks_test.rb +55 -0
  52. data/test/controllers/hosts_controller_test.rb +12 -0
  53. data/test/factories/dlm_facets.rb +6 -0
  54. data/test/factories/dlmlock.rb +6 -2
  55. data/test/factories/dlmlock_events.rb +13 -0
  56. data/test/factories/host.rb +7 -0
  57. data/test/integration/foreman_dlm/dlmlocks_test.rb +28 -0
  58. data/test/jobs/refresh_dlmlock_status_test.rb +10 -0
  59. data/test/models/foreman_dlm/dlm_facet_test.rb +13 -0
  60. data/test/models/foreman_dlm/dlmlock_event_test.rb +19 -0
  61. data/test/models/foreman_dlm/dlmlock_test.rb +307 -0
  62. data/test/models/host_managed_test.rb +41 -0
  63. data/test/models/host_monitoring_test.rb +1 -1
  64. data/test/models/host_status/dlmlock_status_test.rb +45 -0
  65. data/test/models/user_test.rb +5 -0
  66. data/test/test_plugin_helper.rb +5 -3
  67. metadata +108 -30
  68. data/app/controllers/dlmlocks_controller.rb +0 -13
  69. data/app/models/dlmlock/update.rb +0 -5
  70. data/app/models/dlmlock.rb +0 -79
  71. data/app/views/dlmlocks/_details.html.erb +0 -35
  72. data/app/views/dlmlocks/index.html.erb +0 -2
  73. data/app/views/dlmlocks/show.html.erb +0 -7
  74. data/test/controllers/dlmlocks_test.rb +0 -24
  75. data/test/models/dlmlock_test.rb +0 -201
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d01a9afd6f4e70cd4d05bc1691faa86144c463cb69a46a20af4ce7365f7df66
4
- data.tar.gz: 6d1a4884600efc752622be4452aa9ac694f57eaa70c3cbb27d6d6dd16d2c03fa
3
+ metadata.gz: 46b88d4ab255520440c5a8aeb4222e8ab62e167027ca0a833eaa03bf4ee32885
4
+ data.tar.gz: 9b953cde9907b2e7be6282aedbf44fad05d447f13d97e420c62ffd47a3ccf685
5
5
  SHA512:
6
- metadata.gz: 834f7277d2779b95cef01c64a15a146f3c5c2ead8f2d808914d2b9b248d4f7b6702581a4b460994b9df0c46cc80b5a62d30f62969d3d91a7cb8cf407d963bec0
7
- data.tar.gz: 384039584a5bb43567a14ec986db25a7295df791f5293b2809485063f096ba92bfc894350becfa5c1dc18022e29b3036b455ad295a07f82979b87e98d848403b
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 | any |
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 dm-drogerie markt GmbH & Co. KG, https://dm.de
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. See the
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. If not, see <http://www.gnu.org/licenses/>.
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, "/dlmlocks/:id/", N_("Update a DLM lock")
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 <<-EOS
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
- EOS
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 <<-EOS
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
- EOS
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
- when 'release', 'acquire'
117
- :edit
118
- else
119
- super
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', :status => :forbidden, :locals => { :details => 'You need to authenticate with a valid client cert. The DN has to match a known host.' }
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', :status => :unauthorized, :locals => { :user_login => get_client_cert_hostname }
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, options = {})
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.find_by_certname(hostname) ||
43
- Host::Base.find_by_name(hostname)
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
- verify = request.env[Setting[:ssl_client_verify_env]]
50
- unless verify == 'SUCCESS'
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 = $1.downcase
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, options = {})
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
- foreign_key: 'host_id',
8
- dependent: :nullify
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
@@ -0,0 +1,13 @@
1
+ module ForemanDlm
2
+ module UserExtensions
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_many :dlmlock_events,
7
+ class_name: 'ForemanDlm::DlmlockEvent',
8
+ foreign_key: 'user_id',
9
+ dependent: :nullify,
10
+ inverse_of: :user
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module ForemanDlm
2
+ class DlmFacet < ApplicationRecord
3
+ include Facets::Base
4
+
5
+ validates :host, presence: true, allow_blank: false
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module ForemanDlm
2
+ class Dlmlock
3
+ class Update < Dlmlock
4
+ def humanized_type
5
+ _('Update Lock')
6
+ end
7
+ end
8
+ end
9
+ end