foreman_dlm 1.0.0 → 1.1.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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -1
  3. data/app/controllers/api/v2/dlmlock_events_controller.rb +41 -0
  4. data/app/controllers/api/v2/dlmlocks_controller.rb +10 -4
  5. data/app/controllers/concerns/foreman_dlm/update_checkin_time.rb +21 -0
  6. data/app/controllers/foreman_dlm/application_controller.rb +19 -0
  7. data/app/controllers/foreman_dlm/dlmlocks_controller.rb +73 -0
  8. data/app/helpers/foreman_dlm/dlmlock_helper.rb +31 -0
  9. data/app/jobs/foreman_dlm/refresh_dlmlock_status.rb +17 -0
  10. data/app/models/concerns/foreman_dlm/dlm_facet_host_extensions.rb +13 -0
  11. data/app/models/concerns/foreman_dlm/expirable.rb +35 -0
  12. data/app/models/concerns/foreman_dlm/host_extensions.rb +11 -0
  13. data/app/models/concerns/foreman_dlm/user_extensions.rb +13 -0
  14. data/app/models/foreman_dlm/dlm_facet.rb +7 -0
  15. data/app/models/foreman_dlm/dlmlock.rb +136 -0
  16. data/app/models/foreman_dlm/dlmlock/update.rb +9 -0
  17. data/app/models/foreman_dlm/dlmlock_event.rb +23 -0
  18. data/app/models/host_status/dlmlock_status.rb +44 -0
  19. data/app/models/settings/dlm.rb +20 -0
  20. data/app/views/api/v2/dlmlock_events/index.json.rabl +2 -0
  21. data/app/views/foreman_dlm/api/v2/dlm_facets/base.json.rabl +1 -0
  22. data/app/views/foreman_dlm/api/v2/dlm_facets/base_with_root.json.rabl +3 -0
  23. data/app/views/foreman_dlm/api/v2/dlm_facets/show.json.rabl +3 -0
  24. data/app/views/foreman_dlm/dlmlocks/_details.html.erb +42 -0
  25. data/app/views/{dlmlocks → foreman_dlm/dlmlocks}/_list.html.erb +3 -1
  26. data/app/views/{dlmlocks → foreman_dlm/dlmlocks}/index.html.erb +0 -0
  27. data/app/views/foreman_dlm/dlmlocks/show.html.erb +10 -0
  28. data/app/views/{dlmlocks → foreman_dlm/dlmlocks}/welcome.html.erb +0 -0
  29. data/app/views/hosts/_dlmlocks_tab.html.erb +39 -0
  30. data/config/routes.rb +10 -3
  31. data/contrib/systemd/foreman-dlm-expire-events.service +10 -0
  32. data/contrib/systemd/foreman-dlm-expire-events.timer +8 -0
  33. data/db/migrate/20180627150003_rename_dlmlock_sti_models.rb +9 -0
  34. data/db/migrate/20180704162345_add_dlmlock_events.rb +11 -0
  35. data/db/migrate/20180711090022_add_hosts_fk_to_dlmlocks.rb +5 -0
  36. data/db/migrate/20180711111903_create_dlm_facets.foreman_dlm.rb +10 -0
  37. data/db/migrate/20180713113208_update_permissions_for_scoped_models.rb +13 -0
  38. data/lib/foreman_dlm/engine.rb +43 -7
  39. data/lib/foreman_dlm/version.rb +1 -1
  40. data/lib/tasks/dlmlock_events.rake +19 -0
  41. data/test/controllers/api/v2/dlmlocks_controller_test.rb +32 -11
  42. data/test/controllers/api/v2/dlmlocks_dlmlock_events_controller_test.rb +81 -0
  43. data/test/controllers/api/v2/hosts_controller_test.rb +28 -0
  44. data/test/controllers/foreman_dlm/dlmlocks_test.rb +55 -0
  45. data/test/controllers/hosts_controller_test.rb +12 -0
  46. data/test/factories/dlm_facets.rb +6 -0
  47. data/test/factories/dlmlock.rb +6 -2
  48. data/test/factories/dlmlock_events.rb +13 -0
  49. data/test/factories/host.rb +7 -0
  50. data/test/jobs/refresh_dlmlock_status_test.rb +10 -0
  51. data/test/models/foreman_dlm/dlm_facet_test.rb +13 -0
  52. data/test/models/foreman_dlm/dlmlock_event_test.rb +19 -0
  53. data/test/models/foreman_dlm/dlmlock_test.rb +299 -0
  54. data/test/models/host_managed_test.rb +23 -0
  55. data/test/models/host_status/dlmlock_status_test.rb +49 -0
  56. data/test/models/user_test.rb +5 -0
  57. metadata +63 -15
  58. data/app/controllers/dlmlocks_controller.rb +0 -12
  59. data/app/models/dlmlock.rb +0 -79
  60. data/app/models/dlmlock/update.rb +0 -7
  61. data/app/views/dlmlocks/_details.html.erb +0 -35
  62. data/app/views/dlmlocks/show.html.erb +0 -7
  63. data/test/controllers/dlmlocks_test.rb +0 -24
  64. data/test/models/dlmlock_test.rb +0 -201
@@ -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
@@ -0,0 +1,23 @@
1
+ module ForemanDlm
2
+ class DlmlockEvent < ApplicationRecord
3
+ include Authorizable
4
+ include Expirable
5
+
6
+ TYPES = %w[release acquire enable disable fail].freeze
7
+ validates :event_type, inclusion: { in: TYPES }
8
+
9
+ def self.humanize_class_name
10
+ N_('Distributed Lock Event')
11
+ end
12
+
13
+ belongs_to_host
14
+ belongs_to :dlmlock, inverse_of: :dlmlock_events, class_name: 'ForemanDlm::Dlmlock'
15
+ belongs_to :user, inverse_of: :dlmlock_events
16
+
17
+ scoped_search on: :event_type, complete_value: true
18
+
19
+ def humanized_type
20
+ _('Dlmlock Event')
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,44 @@
1
+ module HostStatus
2
+ class DlmlockStatus < Status
3
+ OK = 0
4
+ STALE = 1
5
+
6
+ def self.status_name
7
+ N_('Distributed Lock')
8
+ end
9
+
10
+ def to_label(_options = {})
11
+ case to_status
12
+ when OK
13
+ N_('Ok')
14
+ when STALE
15
+ N_('Stale')
16
+ else
17
+ N_('Unknown')
18
+ end
19
+ end
20
+
21
+ def to_global(_options = {})
22
+ case to_status
23
+ when OK
24
+ HostStatus::Global::OK
25
+ else
26
+ HostStatus::Global::ERROR
27
+ end
28
+ end
29
+
30
+ def to_status(_options = {})
31
+ ok? ? OK : STALE
32
+ end
33
+
34
+ def relevant?(_options = {})
35
+ host.dlmlocks.any?
36
+ end
37
+
38
+ private
39
+
40
+ def ok?
41
+ host.dlmlocks.stale.empty?
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,20 @@
1
+ class Setting
2
+ class Dlm < ::Setting
3
+ def self.load_defaults
4
+ return unless ActiveRecord::Base.connection.table_exists?('settings')
5
+ return unless super
6
+
7
+ Setting.transaction do
8
+ [
9
+ set('dlm_stale_time', N_('Number of hours after which locked Distributed Lock is stale'), 4, N_('Distributed Lock stale time'))
10
+ ].compact.each { |s| Setting::General.create s.update(category: 'Setting::General') }
11
+ end
12
+
13
+ true
14
+ end
15
+
16
+ def self.humanized_category
17
+ N_('Distributed Locks')
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,2 @@
1
+ collection @events
2
+ attributes :id, :event_type, :created_at, :updated_at
@@ -0,0 +1 @@
1
+ attributes :id, :last_checkin_at
@@ -0,0 +1,3 @@
1
+ child :dlm_facet => :dlm_facet_attributes do |_dlm_facet|
2
+ extends 'foreman_dlm/api/v2/dlm_facets/base'
3
+ end
@@ -0,0 +1,3 @@
1
+ child :dlm_facet => :dlm_facet_attributes do
2
+ extends 'foreman_dlm/api/v2/dlm_facets/base'
3
+ end
@@ -0,0 +1,42 @@
1
+ <% if authorizer.can?(:view_dlmlock_events) %>
2
+ <table class="<%= table_css_classes('table-fixed') %>">
3
+ <thead>
4
+ <tr>
5
+ <th><%= _("Host") %></th>
6
+ <th><%= _("User") %></th>
7
+ <th><%= _("Type") %></th>
8
+ <th><%= _("Timestamp") %></th>
9
+ </tr>
10
+ </thead>
11
+ <tbody>
12
+ <% @dlmlock.dlmlock_events.includes(:user).reorder(created_at: :desc).each do |event| %>
13
+
14
+ <tr>
15
+ <td>
16
+ <% if event.host %>
17
+ <%= link_to_if_authorized(event.host.name, hash_for_host_path(:id => event.host)) %>
18
+ <% end %>
19
+ </td>
20
+
21
+ <td>
22
+ <% if event.user %>
23
+ <% if event.user.hidden? %>
24
+ <em><%= event.user.name %></em>
25
+ <% else %>
26
+ <%= link_to_if_authorized(event.user.name, hash_for_edit_user_path(event.user)) %>
27
+ <% end %>
28
+ <% end %>
29
+ </td>
30
+
31
+ <td>
32
+ <%= _(event.event_type.humanize) %>
33
+ </td>
34
+
35
+ <td>
36
+ <%= event.created_at %>
37
+ </td>
38
+ </tr>
39
+ <% end %>
40
+ </tbody>
41
+ </table>
42
+ <% end %>
@@ -5,6 +5,7 @@
5
5
  <th><%= sort :host, :as => _("Owner") %></th>
6
6
  <th><%= sort :type, :as => _("Type") %></th>
7
7
  <th><%= sort :enabled, :as => _("Enabled") %></th>
8
+ <th class="col-md-2"><%= _('Actions') %></th>
8
9
  </tr>
9
10
  </thead>
10
11
  <tbody>
@@ -12,7 +13,7 @@
12
13
  <tr class="<%= lock.enabled? ? '' : 'warning' %>">
13
14
  <td>
14
15
  <%= icon_text(dlmlock_status_icon_class(lock),
15
- link_to_if_authorized(lock.name, hash_for_dlmlock_path(:id => lock)),
16
+ link_to_if_authorized(lock.name, hash_for_foreman_dlm_dlmlock_path(:id => lock)),
16
17
  kind: 'fa',
17
18
  class: "#{dlmlock_status_icon_color_class(lock)} fa-lg")
18
19
  %>
@@ -38,6 +39,7 @@
38
39
  <%= icon_text('toggle-off', 'Disabled', {kind: 'fa', class: 'center fa-lg', title: _('Disabled')}) %>
39
40
  <% end %>
40
41
  </td>
42
+ <td><%= action_buttons(*dlmlock_actions(lock, authorizer)) %></td>
41
43
  </tr>
42
44
  <% end %>
43
45
  </tbody>
@@ -0,0 +1,10 @@
1
+ <% title @dlmlock.name %>
2
+ <% breadcrumbs(
3
+ resource_url: api_dlmlocks_path
4
+ ) if respond_to?(:breadcrumbs) # Requires Foreman >= 1.18 %>
5
+
6
+ <%= title_actions link_to(_('Back'), foreman_dlm_dlmlocks_path, :class => 'btn btn-default'),
7
+ ((link_to(_("Host details"), @dlmlock.host, :class => 'btn btn-default') if @dlmlock.host.present?))
8
+ %>
9
+
10
+ <%= render 'details' %>
@@ -0,0 +1,39 @@
1
+ <% if @host.dlmlocks.any? %>
2
+ <%= alert class: 'alert-info',
3
+ header: '',
4
+ text: n_(
5
+ 'This host holds %s lock.',
6
+ 'This host holds %s locks.',
7
+ @host.dlmlocks.count
8
+ ) % @host.dlmlocks.count
9
+ %>
10
+ <% end %>
11
+
12
+ <table class="<%= table_css_classes %> table-fixed">
13
+ <thead>
14
+ <tr>
15
+ <th colspan="2"><%= _('Locks') %></th>
16
+ </tr>
17
+ </thead>
18
+ <tbody>
19
+ <tr>
20
+ <td><%= _('Last checkin') %></td>
21
+ <td><%= @host.dlm_facet && @host.dlm_facet.last_checkin_at ? date_time_relative(@host.dlm_facet.last_checkin_at) : _('N/A') %></td>
22
+ </tr>
23
+ <tr>
24
+ <td><%= _('Holds Locks') %></td>
25
+ <td>
26
+ <% if @host.dlmlocks.any? %>
27
+ <% @host.dlmlocks.authorized(:view_dlmlocks).each do |lock| %>
28
+ <%= link_to_if_authorized(lock.name, hash_for_foreman_dlm_dlmlock_path(lock)) %>
29
+ <span class="label label-<%= lock.updated_at > 6.hours.ago ? 'info' : 'warning' %>">
30
+ <%= _('acquired') %> <%= date_time_relative_value(lock.updated_at) %>
31
+ </span>
32
+ <% end %>
33
+ <% else %>
34
+ <%= _('None') %>
35
+ <% end %>
36
+ </td>
37
+ </tr>
38
+ </tbody>
39
+ </table>
@@ -6,6 +6,8 @@ Rails.application.routes.draw do
6
6
  :constraints => ApiConstraints.new(:version => 2, :default => true) do
7
7
  constraints(id: /[^\/]+/) do
8
8
  resources :dlmlocks, only: [:index, :show, :update, :destroy] do
9
+ resources :dlmlock_events, only: [:index]
10
+
9
11
  get :lock, on: :member, action: :show, controller: 'dlmlocks'
10
12
  put :lock, on: :member, action: :acquire, controller: 'dlmlocks'
11
13
  delete :lock, on: :member, action: :release, controller: 'dlmlocks'
@@ -15,10 +17,15 @@ Rails.application.routes.draw do
15
17
  end
16
18
  end
17
19
 
18
- scope '/foreman_dlm' do
19
- resources :dlmlocks, only: [:index, :show] do
20
+ namespace :foreman_dlm do
21
+ resources :dlmlocks, only: [:index, :show, :destroy] do
20
22
  collection do
21
- get 'auto_complete_search'
23
+ get :auto_complete_search
24
+ end
25
+ member do
26
+ put :enable
27
+ put :disable
28
+ put :release
22
29
  end
23
30
  end
24
31
  end
@@ -0,0 +1,10 @@
1
+ [Unit]
2
+ Description=Clean up expired Foreman DLM events
3
+ Documentation=https://github.com/dm-drogeriemarkt/foreman_dlm/blob/master/README.md
4
+ Requires=network.target
5
+
6
+ [Service]
7
+ Type=oneshot
8
+ EnvironmentFile=-/etc/sysconfig/foreman_dlm
9
+ ExecStart=/sbin/foreman-rake dlmlocks:expire
10
+ TimeoutSec=30m
@@ -0,0 +1,8 @@
1
+ [Unit]
2
+ Description=Run foreman-dlm-expire-events.service nightly
3
+
4
+ [Timer]
5
+ OnCalendar=*-*-* 02:00:00
6
+
7
+ [Install]
8
+ WantedBy=timers.target
@@ -0,0 +1,9 @@
1
+ class RenameDlmlockStiModels < ActiveRecord::Migration[5.1]
2
+ def up
3
+ execute "UPDATE dlmlocks set type='ForemanDlm::Dlmlock::Update' where type='Dlmlock::Update';"
4
+ end
5
+
6
+ def down
7
+ execute "UPDATE dlmlocks set type='Dlmlock::Update' where type='ForemanDlm::Dlmlock::Update';"
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ class AddDlmlockEvents < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :dlmlock_events do |t|
4
+ t.integer :dlmlock_id, index: true, null: false
5
+ t.string :event_type, index: true
6
+ t.integer :host_id, index: true
7
+ t.integer :user_id
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ class AddHostsFkToDlmlocks < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_foreign_key :dlmlocks, :hosts
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ class CreateDlmFacets < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :dlm_facets do |t|
4
+ t.references :host, null: false, foreign_key: true, index: true, unique: true
5
+ t.column :last_checkin_at, :datetime
6
+
7
+ t.timestamps null: false
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ class UpdatePermissionsForScopedModels < ActiveRecord::Migration[5.1]
2
+ class FakePermission < ApplicationRecord
3
+ self.table_name = 'permissions'
4
+ end
5
+
6
+ def up
7
+ FakePermission.where(resource_type: 'Dlmlock').update_all(resource_type: 'ForemanDlm::Dlmlock')
8
+ end
9
+
10
+ def down
11
+ FakePermission.where(resource_type: 'ForemanDlm::Dlmlock').update_all(resource_type: 'Dlmlock')
12
+ end
13
+ end
@@ -4,6 +4,15 @@ module ForemanDlm
4
4
 
5
5
  config.autoload_paths += Dir["#{config.root}/app/controllers/concerns"]
6
6
  config.autoload_paths += Dir["#{config.root}/app/models/concerns"]
7
+ config.autoload_paths += Dir["#{config.root}/app/jobs"]
8
+
9
+ initializer 'foreman_dlm.load_default_settings', before: :load_config_initializers do
10
+ require_dependency File.expand_path('../../app/models/settings/dlm.rb', __dir__) if begin
11
+ Setting.table_exists?
12
+ rescue StandardError
13
+ (false)
14
+ end
15
+ end
7
16
 
8
17
  # Add any db migrations
9
18
  initializer 'foreman_dlm.load_app_instance_data' do |app|
@@ -21,32 +30,57 @@ module ForemanDlm
21
30
  # Add permissions
22
31
  security_block :foreman_dlm do
23
32
  permission :view_dlmlocks, {
24
- :dlmlocks => [:index, :show, :auto_complete_search],
33
+ :'foreman_dlm/dlmlocks' => [:index, :show, :auto_complete_search],
25
34
  :'api/v2/dlmlocks' => [:index, :show]
26
- }, :resource_type => 'Dlmlock'
35
+ }, :resource_type => 'ForemanDlm::Dlmlock'
27
36
 
28
37
  permission :create_dlmlocks, {
29
38
  :'api/v2/dlmlocks' => [:create]
30
- }, :resource_type => 'Dlmlock'
39
+ }, :resource_type => 'ForemanDlm::Dlmlock'
31
40
 
32
41
  permission :edit_dlmlocks, {
42
+ :'foreman_dlm/dlmlocks' => [:release, :enable, :disable],
33
43
  :'api/v2/dlmlocks' => [:update, :acquire, :release]
34
- }, :resource_type => 'Dlmlock'
44
+ }, :resource_type => 'ForemanDlm::Dlmlock'
35
45
 
36
46
  permission :destroy_dlmlocks, {
47
+ :'foreman_dlm/dlmlocks' => [:destroy],
37
48
  :'api/v2/dlmlocks' => [:destroy]
38
- }, :resource_type => 'Dlmlock'
49
+ }, :resource_type => 'ForemanDlm::Dlmlock'
50
+
51
+ permission :view_dlmlock_events, {
52
+ :'api/v2/dlmlock_events' => [:index]
53
+ }, :resource_type => 'ForemanDlm::DlmlockEvent'
39
54
  end
40
55
 
41
56
  # Add a new role called 'Distributed Lock Manager' if it doesn't exist
42
- role 'Distributed Lock Manager', [:view_dlmlocks, :create_dlmlocks, :edit_dlmlocks, :destroy_dlmlocks]
57
+ role 'Distributed Lock Manager', [:view_dlmlocks,
58
+ :create_dlmlocks,
59
+ :edit_dlmlocks,
60
+ :destroy_dlmlocks,
61
+ :view_dlmlock_events]
43
62
 
44
63
  # add menu entry
45
64
  menu :top_menu, :distributed_locks,
46
- url_hash: { controller: :dlmlocks, action: :index },
65
+ url_hash: { controller: :'foreman_dlm/dlmlocks', action: :index },
47
66
  caption: N_('Distributed Locks'),
48
67
  parent: :monitor_menu,
49
68
  after: :audits
69
+
70
+ # Dlm Facet
71
+ register_facet(ForemanDlm::DlmFacet, :dlm_facet) do
72
+ api_view list: 'foreman_dlm/api/v2/dlm_facets/base_with_root', single: 'foreman_dlm/api/v2/dlm_facets/show'
73
+ end
74
+
75
+ register_custom_status HostStatus::DlmlockStatus
76
+
77
+ # extend host show page
78
+ extend_page('hosts/show') do |context|
79
+ context.add_pagelet :main_tabs,
80
+ :name => N_('Locks'),
81
+ :partial => 'hosts/dlmlocks_tab',
82
+ :onlyif => proc { |host| host.dlm_facet }
83
+ end
50
84
  end
51
85
  end
52
86
 
@@ -54,6 +88,8 @@ module ForemanDlm
54
88
  config.to_prepare do
55
89
  begin
56
90
  Host::Managed.send(:include, ForemanDlm::HostExtensions)
91
+ User.send(:include, ForemanDlm::UserExtensions)
92
+ Host::Managed.send(:include, ForemanDlm::DlmFacetHostExtensions)
57
93
 
58
94
  Host::Managed.send(:include, ForemanDlm::HostMonitoringExtensions) if ForemanDlm.with_monitoring?
59
95
  rescue StandardError => e
@@ -1,3 +1,3 @@
1
1
  module ForemanDlm
2
- VERSION = '1.0.0'.freeze
2
+ VERSION = '1.1.0'.freeze
3
3
  end
@@ -0,0 +1,19 @@
1
+ desc <<-TASK_DESCRIPTION
2
+ Expire lock events automatically
3
+
4
+ Available conditions:
5
+ * days => number of days to keep reports (defaults to 14)
6
+ * batch_size => number of records deleted in single SQL transaction (defaults to 1k)
7
+ * sleep_time => delay in seconds between batches (defaults to 0.2)
8
+
9
+ TASK_DESCRIPTION
10
+
11
+ namespace :dlmlocks do
12
+ task expire: :environment do
13
+ created_before = ENV['days'].to_i.days if ENV['days']
14
+ batch_size = ENV['batch_size'].to_i if ENV['batch_size']
15
+ sleep_time = ENV['sleep_time'].to_f if ENV['sleep_time']
16
+
17
+ ForemanDlm::DlmlockEvent.expire(created_before: created_before, batch_size: batch_size, sleep_time: sleep_time)
18
+ end
19
+ end