oxidized 0.35.0 → 0.37.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/.coderabbit.yaml +21 -0
  3. data/.github/workflows/publishdocker.yml +11 -9
  4. data/.github/workflows/ruby.yml +1 -3
  5. data/.rubocop.yml +16 -2
  6. data/.rubocop_todo.yml +21 -2
  7. data/CHANGELOG.md +76 -3
  8. data/README.md +2 -3
  9. data/Rakefile +1 -1
  10. data/docs/Configuration.md +40 -2
  11. data/docs/Creating-Models.md +129 -14
  12. data/docs/Docker.md +2 -1
  13. data/docs/Hooks.md +92 -67
  14. data/docs/Inputs.md +44 -12
  15. data/docs/Model-Notes/APC.md +72 -0
  16. data/docs/Model-Notes/ExaLink.md +43 -0
  17. data/docs/Model-Notes/Fortinet.md +75 -0
  18. data/docs/Model-Notes/GrandstreamHT8xx.md +8 -0
  19. data/docs/Model-Notes/IvantiConnectSecure.md +59 -0
  20. data/docs/Model-Notes/RouterOS.md +13 -0
  21. data/docs/Model-Notes/TrueNAS.md +23 -0
  22. data/docs/ModelUnitTests.md +23 -0
  23. data/docs/Outputs.md +18 -4
  24. data/docs/Release.md +7 -2
  25. data/docs/Ruby-API.md +86 -5
  26. data/docs/Supported-OS-Types.md +21 -9
  27. data/docs/Troubleshooting.md +1 -1
  28. data/extra/device2yaml.rb +2 -3
  29. data/extra/hooks/modelrules.rb +55 -0
  30. data/extra/hooks/modelrulesadvanced.rb +167 -0
  31. data/extra/hooks/srcipmap.rb +54 -0
  32. data/lib/oxidized/cli/support.rb +152 -0
  33. data/lib/oxidized/cli.rb +9 -0
  34. data/lib/oxidized/hook/githubrepo.rb +2 -1
  35. data/lib/oxidized/hook.rb +58 -8
  36. data/lib/oxidized/input/debugtext.rb +40 -0
  37. data/lib/oxidized/input/debugyaml.rb +82 -0
  38. data/lib/oxidized/input/exec.rb +1 -10
  39. data/lib/oxidized/input/ftp.rb +0 -17
  40. data/lib/oxidized/input/http.rb +39 -21
  41. data/lib/oxidized/input/input.rb +33 -13
  42. data/lib/oxidized/input/scp.rb +10 -64
  43. data/lib/oxidized/input/ssh.rb +36 -79
  44. data/lib/oxidized/input/sshbase.rb +102 -0
  45. data/lib/oxidized/input/telnet.rb +12 -13
  46. data/lib/oxidized/input/tftp.rb +7 -7
  47. data/lib/oxidized/model/aoscx.rb +18 -12
  48. data/lib/oxidized/model/aosw.rb +10 -11
  49. data/lib/oxidized/model/apc_aos.rb +4 -0
  50. data/lib/oxidized/model/apcaos.rb +39 -0
  51. data/lib/oxidized/model/arubainstant.rb +11 -20
  52. data/lib/oxidized/model/asa.rb +7 -7
  53. data/lib/oxidized/model/comware.rb +3 -1
  54. data/lib/oxidized/model/cumulus.rb +3 -3
  55. data/lib/oxidized/model/defacto.rb +26 -0
  56. data/lib/oxidized/model/dlinknextgen.rb +1 -0
  57. data/lib/oxidized/model/dslcommands.rb +93 -0
  58. data/lib/oxidized/model/dslsetup.rb +102 -0
  59. data/lib/oxidized/model/efos.rb +5 -5
  60. data/lib/oxidized/model/exalink.rb +36 -0
  61. data/lib/oxidized/model/fastiron.rb +2 -2
  62. data/lib/oxidized/model/firelinuxos.rb +1 -3
  63. data/lib/oxidized/model/fortigate.rb +160 -0
  64. data/lib/oxidized/model/fortios.rb +28 -69
  65. data/lib/oxidized/model/fsos.rb +1 -3
  66. data/lib/oxidized/model/grandstreamht8xx.rb +19 -0
  67. data/lib/oxidized/model/h3c.rb +1 -1
  68. data/lib/oxidized/model/ios.rb +23 -15
  69. data/lib/oxidized/model/ironware.rb +5 -3
  70. data/lib/oxidized/model/ivanti.rb +54 -0
  71. data/lib/oxidized/model/junos.rb +2 -2
  72. data/lib/oxidized/model/linuxgeneric.rb +4 -2
  73. data/lib/oxidized/model/macros.rb +60 -0
  74. data/lib/oxidized/model/mlnxos.rb +11 -7
  75. data/lib/oxidized/model/model.rb +28 -126
  76. data/lib/oxidized/model/ndms.rb +6 -0
  77. data/lib/oxidized/model/netgear.rb +5 -3
  78. data/lib/oxidized/model/nxos.rb +6 -3
  79. data/lib/oxidized/model/outputs.rb +5 -0
  80. data/lib/oxidized/model/perle.rb +14 -8
  81. data/lib/oxidized/model/routeros.rb +4 -0
  82. data/lib/oxidized/model/smartbyte.rb +48 -0
  83. data/lib/oxidized/model/tplink.rb +4 -6
  84. data/lib/oxidized/model/truenas.rb +63 -3
  85. data/lib/oxidized/model/voss.rb +3 -0
  86. data/lib/oxidized/model/vyos.rb +4 -1
  87. data/lib/oxidized/node.rb +25 -23
  88. data/lib/oxidized/nodes.rb +2 -0
  89. data/lib/oxidized/output/file.rb +7 -1
  90. data/lib/oxidized/output/git.rb +11 -1
  91. data/lib/oxidized/output/gitcrypt.rb +1 -1
  92. data/lib/oxidized/output/http.rb +12 -3
  93. data/lib/oxidized/source/csv.rb +5 -0
  94. data/lib/oxidized/source/jsonfile.rb +5 -0
  95. data/lib/oxidized/source/sql.rb +5 -0
  96. data/lib/oxidized/version.rb +2 -2
  97. data/lib/oxidized/worker.rb +36 -15
  98. data/lib/refinements.rb +18 -0
  99. data/oxidized.gemspec +28 -24
  100. metadata +103 -55
  101. data/docs/Model-Notes/APC_AOS.md +0 -65
  102. data/docs/Model-Notes/FortiOS.md +0 -44
@@ -48,19 +48,17 @@ class TPLink < Oxidized::Model
48
48
  lines[0..lines.index("end\n")].join
49
49
  end
50
50
 
51
+ macro :enable, regex: /^[pP]assword:/
52
+
51
53
  cfg :telnet, :ssh do
52
54
  username /^User ?[nN]ame:/
53
55
  password /^\r?Password:/
56
+ newline "\r\n"
54
57
  end
55
58
 
56
59
  cfg :telnet, :ssh do
57
60
  post_login do
58
- if vars(:enable) == true
59
- cmd "enable"
60
- elsif vars(:enable)
61
- cmd "enable", /^[pP]assword:/
62
- cmd vars(:enable)
63
- end
61
+ cmd 'terminal length 0'
64
62
  end
65
63
 
66
64
  pre_logout do
@@ -5,14 +5,74 @@ class TrueNAS < Oxidized::Model
5
5
 
6
6
  cmd('uname -a') { |cfg| comment cfg }
7
7
  cmd('cat /etc/version') { |cfg| comment cfg }
8
- cmd('sqlite3 "file:///data/freenas-v1.db?mode=ro&immutable=1" .dump') do |cfg|
8
+
9
+ # for TrueNAS SCALE machines, make sure the user you use to connect can run
10
+ # this command, or if needed, with passwordless sudo. Try putting this in
11
+ # /etc/sudoers
12
+ # oxidized ALL=(ALL) NOPASSWD: /usr/bin/find /mnt/.ix-apps/app_configs *, /usr/bin/sqlite3 -readonly file\:/data/freenas-v1.db *
13
+
14
+ cmd("sqlite3 -readonly 'file:/data/freenas-v1.db' .dump") do |cfg|
15
+ if cfg.include? "Error: unable to open database"
16
+ # retry with sudo
17
+ cfg = cmd("sudo sqlite3 -readonly 'file:/data/freenas-v1.db' .dump")
18
+ end
9
19
  cfg.lines.reject do |line|
10
- line.match(/^INSERT INTO storage_replication /) ||
20
+ line.match(/^INSERT INTO storage_replication /) || # ignore storage_replication because repl_status field changes on every run
11
21
  line.match(/^INSERT INTO system_alert /) || # ignore system alerts in db
12
- line.match(/^INSERT INTO sqlite_sequence VALUES\('system_alert',/) # ignore system alerts in db
22
+ line.match(/^INSERT INTO sqlite_sequence VALUES\('system_alert',/) || # ignore system alerts in db
23
+ line.match(/^INSERT INTO tasks_cloudsync /) # ignore cloudsync tasks because job field changes on every run
13
24
  end.join
14
25
  end
15
26
 
27
+ post do
28
+ filter_column("storage_replication", "repl_state")
29
+ end
30
+
31
+ post do
32
+ filter_column("tasks_cloudsync", "job")
33
+ end
34
+
35
+ post do
36
+ collect_ixapps_configurations
37
+ end
38
+
39
+ def filter_column(table_name, column_name)
40
+ sqlite_cmd = "sqlite3 -readonly 'file:/data/freenas-v1.db'"
41
+
42
+ # This SQL command will create a SELECT query with all columns except the one we want to skip.
43
+ generate_select_cmd = "select 'select ' || group_concat(name,', ') || ' FROM #{table_name};' FROM pragma_table_info('#{table_name}') WHERE name != '#{column_name}';"
44
+
45
+ select_stmt = cmd("#{sqlite_cmd} \"#{generate_select_cmd}\"")
46
+ if select_stmt.include? "Error: unable to open database"
47
+ # retry with sudo
48
+ sqlite_cmd = "sudo #{sqlite_cmd}"
49
+ select_stmt = cmd("#{sqlite_cmd} \"#{generate_select_cmd}\"")
50
+ end
51
+
52
+ insert_cmds = "-header '.mode insert #{table_name}' '#{select_stmt}'"
53
+ cmd("#{sqlite_cmd} #{insert_cmds}") do |cfg|
54
+ if cfg.include? "INSERT"
55
+ # Don't add a COMMIT; if the query came up with no rows
56
+ cfg + "COMMIT;\n"
57
+ end
58
+ end
59
+ end
60
+
61
+ def collect_ixapps_configurations
62
+ config_command = <<~'CMDEND'
63
+ if [ -d /mnt/.ix-apps ];
64
+ then
65
+ sudo find /mnt/.ix-apps/app_configs
66
+ \( -name "app.yaml" -or -name "user_config.yaml" -or -name "metadata.yaml" \)
67
+ -printf "\n\n#######################\n# %p\n#######################\n"
68
+ -exec cat {} \; ;
69
+ else
70
+ echo "# No Apps configuration found in /mnt/.ix-apps";
71
+ fi
72
+ CMDEND
73
+ cmd(config_command.gsub("\n", ""))
74
+ end
75
+
16
76
  cfg :ssh do
17
77
  exec true # don't run shell, run each command in exec channel
18
78
  end
@@ -23,6 +23,9 @@ class Voss < Oxidized::Model
23
23
  cfg.gsub! /(^((.*)Last Vlan Change(.*):(.*))$)/, 'removed Last Vlan Change'
24
24
  cfg.gsub! /(^((.*)Temperature(.*):(.*))$)/, 'removed Temperature'
25
25
  cfg.gsub! /(^((.*)Total Power Usage(.*):(.*))$)/, 'removed Total Power Usage'
26
+ cfg.gsub! /(^((.*)(Tray \d+ Fan \d+)\s+(\w+)\s+(\w+)\s+(.*))$)/, '\3\4 \5 [removed RPM values]'
27
+ cfg.gsub! /(^((.*)Command Execution Time: (.*))$)/, 'Command Execution Time [removed]'
28
+ cfg.gsub! /(^((.*)(Ambient\s+\d+|CPU|INTERNAL\s+MAC|SODIMM)\s+\d+\s+\d+(.*))$)/, '\3\4 [removed temperature readings]'
26
29
  comment "#{cfg}\n"
27
30
  end
28
31
 
@@ -15,8 +15,11 @@ class Vyos < Oxidized::Model
15
15
  cmd :secret do |cfg|
16
16
  cfg.gsub! /secret (\S+).*/, 'secret <secret removed>'
17
17
  cfg.gsub! /password (\S+).*/, 'password <secret removed>'
18
- cfg.gsub! /community (\S+)/, 'community <secret removed>'
18
+ cfg.gsub! /snmp community (\S+)/, 'snmp community <secret removed>'
19
+ cfg.gsub! /snmp trap-target ([^\s\\]*) community (\S+)/, 'snmp trap-target \1 community <secret removed>'
20
+ cfg.gsub! /preshared-key (\S+).*/, 'preshared-key <secret removed>'
19
21
  cfg.gsub! /private key (\S+).*/, 'private key <secret removed>'
22
+ cfg.gsub! /private-key (\S+).*/, 'private-key <secret removed>'
20
23
  # password in URLs like protocol://user:password@domain.tld/
21
24
  cfg.gsub! /([a-z]+:\/\/[^:\s]+:)\S+@/, '\1<secret removed>@'
22
25
  cfg
data/lib/oxidized/node.rb CHANGED
@@ -8,8 +8,9 @@ module Oxidized
8
8
  include SemanticLogger::Loggable
9
9
 
10
10
  attr_reader :name, :ip, :model, :input, :output, :group, :auth, :prompt, :timeout, :vars, :last, :repo
11
- attr_accessor :running, :user, :email, :msg, :from, :stats, :retry, :err_type, :err_reason
11
+ attr_accessor :running, :user, :email, :msg, :from, :stats, :retry, :err_type, :err_reason, :nexted
12
12
  alias running? running
13
+ alias nexted? nexted
13
14
 
14
15
  # opt is a hash with the node parameters given in the source (:name, :group, :ip...)
15
16
  def initialize(opt)
@@ -34,6 +35,7 @@ module Oxidized
34
35
  @repo = resolve_repo opt
35
36
  @err_type = nil
36
37
  @err_reason = nil
38
+ @nexted = false
37
39
 
38
40
  # model instance needs to access node instance
39
41
  @model.node = self
@@ -41,37 +43,37 @@ module Oxidized
41
43
 
42
44
  def run
43
45
  status = :fail
44
- config = nil
45
- @input.each do |input|
46
- # don't try input if model is missing config block, we may need strong config to class_name map
47
- cfg_name = input.to_s.split('::').last.downcase
48
- next unless @model.cfg[cfg_name] && (not @model.cfg[cfg_name].empty?)
49
-
50
- @model.input = input = input.new
51
- if (config = run_input(input))
52
- logger.debug "#{input.class.name} ran for #{name} successfully"
53
- status = :success
54
- break
46
+ config = Oxidized::Model::Outputs.new
47
+ input_sequence = @model.class.input_sequence(@input)
48
+
49
+ input_sequence.each do |sequence|
50
+ status = :fail
51
+ sequence_config = nil
52
+ sequence.each do |input|
53
+ @model.input = input = input.new
54
+ if (sequence_config = run_input(input))
55
+ logger.debug "#{input.class.name} ran for #{name} successfully"
56
+ status = :success
57
+ break
58
+ else
59
+ logger.debug "#{input.class.name} failed for #{name}"
60
+ status = :no_connection
61
+ end
62
+ end
63
+ if status == :success
64
+ config.merge! sequence_config
55
65
  else
56
- logger.debug "#{input.class.name} failed for #{name}"
57
- status = :no_connection
66
+ config = nil
67
+ break
58
68
  end
59
69
  end
60
- logger.error "No suitable input found for #{name}" unless @model.input
61
70
 
62
71
  @model.input = nil
63
72
  [status, config]
64
73
  end
65
74
 
66
75
  def run_input(input)
67
- rescue_fail = {}
68
- [input.class::RESCUE_FAIL, input.class.superclass::RESCUE_FAIL].each do |hash|
69
- hash.each do |level, errors|
70
- errors.each do |err|
71
- rescue_fail[err] = level
72
- end
73
- end
74
- end
76
+ rescue_fail = input.class.rescue_fail
75
77
  begin
76
78
  input.connect(self) && input.get
77
79
  rescue *rescue_fail.keys => err
@@ -88,6 +88,8 @@ module Oxidized
88
88
  n.from = opt['from']
89
89
  # set last job to nil so that the node is picked for immediate update
90
90
  n.last = nil
91
+ # set nexted to true so that the node will not be skipped with interval 0
92
+ n.nexted = true
91
93
  put n
92
94
  jobs.increment if Oxidized.config.next_adds_job?
93
95
  end
@@ -21,8 +21,14 @@ module Oxidized
21
21
 
22
22
  # node: node name (String)
23
23
  # outputs: Oxidized::Models::Outputs
24
- # opts: hash of node vars
24
+ # opts: dict of optional parameters:
25
+ # - group: node group
26
+ # - significant_changes:
27
+ # nil / not set / true -> store as usual
28
+ # false -> do not store
25
29
  def store(node, outputs, opt = {})
30
+ return false if opt[:significant_changes] == false
31
+
26
32
  file = ::File.expand_path @cfg.directory
27
33
  file = ::File.join ::File.dirname(file), opt[:group] if opt[:group]
28
34
  FileUtils.mkdir_p file
@@ -35,6 +35,16 @@ module Oxidized
35
35
  end
36
36
  end
37
37
 
38
+ # file: node name (String)
39
+ # outputs: Oxidized::Models::Outputs
40
+ # opts: dict of optional parameters:
41
+ # - msg: commit message
42
+ # - email: committer email
43
+ # - user: committer name
44
+ # - group: node group
45
+ # - significant_changes:
46
+ # nil / not set / true: store as usual
47
+ # false: skip general config, only store configs where type != nil
38
48
  def store(file, outputs, opt = {})
39
49
  @msg = opt[:msg]
40
50
  @user = opt[:user] || @cfg.user
@@ -58,7 +68,7 @@ module Oxidized
58
68
  update type_repo, file, type_cfg
59
69
  end
60
70
 
61
- update repo, file, outputs.to_cfg
71
+ update repo, file, outputs.to_cfg unless opt[:significant_changes] == false
62
72
  end
63
73
 
64
74
  # Returns the configuration of group/node_name
@@ -86,7 +86,7 @@ module Oxidized
86
86
  update type_repo, file, type_cfg
87
87
  end
88
88
 
89
- update repo, file, outputs.to_cfg
89
+ update repo, file, outputs.to_cfg unless opt[:significant_changes] == false
90
90
  end
91
91
 
92
92
  def fetch(node, group)
@@ -26,9 +26,18 @@ module Oxidized
26
26
  @commitref = nil
27
27
  uri = URI.parse @cfg.url
28
28
  http = Net::HTTP.new uri.host, uri.port
29
- # http.use_ssl = true if uri.scheme = 'https'
30
- req = Net::HTTP::Post.new(uri.request_uri, 'Content-Type' => 'application/json')
31
- req.basic_auth @cfg.user, @cfg.password
29
+
30
+ # if uri scheme is https, enable ssl and set verify mode
31
+ if uri.scheme == "https"
32
+ http.use_ssl = true
33
+ http.verify_mode = @cfg.ssl_verify? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
34
+ end
35
+
36
+ headers = @cfg.headers? ? @cfg.headers : {}
37
+
38
+ req = Net::HTTP::Post.new(uri.request_uri, headers.merge('Content-Type' => 'application/json'))
39
+ req.basic_auth(@cfg.user, @cfg.password) if @cfg.user? && @cfg.password?
40
+
32
41
  req.body = generate_json(node, outputs, opt)
33
42
  response = http.request req
34
43
 
@@ -47,6 +47,11 @@ module Oxidized
47
47
  end
48
48
  keys[:vars] = vars unless vars.empty?
49
49
 
50
+ keys = Oxidized.hooks.source_node_transform(node: keys,
51
+ node_raw: data,
52
+ context: self)
53
+ next if keys.nil?
54
+
50
55
  nodes << keys
51
56
  end
52
57
  nodes
@@ -54,6 +54,11 @@ module Oxidized
54
54
  end
55
55
  keys[:vars] = vars unless vars.empty?
56
56
 
57
+ keys = Oxidized.hooks.source_node_transform(node: keys,
58
+ node_raw: node,
59
+ context: self)
60
+ next if keys.nil?
61
+
57
62
  nodes << keys
58
63
  end
59
64
  nodes
@@ -46,6 +46,11 @@ module Oxidized
46
46
  end
47
47
  keys[:vars] = vars unless vars.empty?
48
48
 
49
+ keys = Oxidized.hooks.source_node_transform(node: keys,
50
+ node_raw: node.to_hash,
51
+ context: self)
52
+ next if keys.nil?
53
+
49
54
  nodes << keys
50
55
  end
51
56
  db.disconnect
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Oxidized
4
- VERSION = '0.35.0'
5
- VERSION_FULL = '0.35.0'
4
+ VERSION = '0.37.0'
5
+ VERSION_FULL = '0.37.0'
6
6
  def self.version_set
7
7
  version_full = %x(git describe --tags).chop rescue ""
8
8
  version = %x(git describe --tags --abbrev=0).chop rescue ""
@@ -23,11 +23,11 @@ module Oxidized
23
23
  "#{@jobs_done} of #{@nodes.size}"
24
24
  # ask for next node in queue non destructive way
25
25
  nextnode = @nodes.first
26
- unless nextnode.last.nil?
27
- # Set unobtainable value for 'last' if interval checking is disabled
28
- last = Oxidized.config.interval.zero? ? Time.now.utc + 10 : nextnode.last.end
29
- break if last + Oxidized.config.interval > Time.now.utc
30
- end
26
+ break if Oxidized.config.interval.zero? && !nextnode.nexted?
27
+
28
+ nextnode.nexted = false
29
+ break if !nextnode.last.nil? && (nextnode.last.end + Oxidized.config.interval > Time.now.utc)
30
+
31
31
  # shift nodes and get the next node
32
32
  node = @nodes.get
33
33
  node.running? ? next : node.running = true
@@ -64,21 +64,43 @@ module Oxidized
64
64
 
65
65
  private
66
66
 
67
+ def significant_changes?(job, output)
68
+ node = job.node
69
+ model = node.model
70
+ return true unless model.vars(:output_store_mode) == "on_significant"
71
+
72
+ unless output.respond_to?(:fetch)
73
+ logger.error("Detection of significant changes needs an output " \
74
+ "capable of fetching the last configuration")
75
+ return true
76
+ end
77
+
78
+ old = model.significant_changes output.fetch(node, node.group)
79
+ new = model.significant_changes job.config.to_cfg
80
+ if old == new
81
+ logger.debug "No significant change on node #{node.name}"
82
+ false
83
+ else
84
+ true
85
+ end
86
+ end
87
+
67
88
  def process_success(node, job)
68
89
  @jobs_done += 1 # needed for :nodes_done hook
69
- Oxidized.hooks.handle :node_success, node: node,
70
- job: job
90
+ Oxidized.hooks.node_success(node: node, job: job)
71
91
  msg = "update #{node.group}/#{node.name}"
72
92
  msg += " from #{node.from}" if node.from
73
93
  msg += " with message '#{node.msg}'" if node.msg
74
94
  output = node.output.new
75
- if output.store node.name, job.config,
76
- msg: msg, email: node.email, user: node.user, group: node.group
95
+
96
+ significant_changes = significant_changes?(job, output)
97
+ if output.store(node.name, job.config,
98
+ msg: msg, email: node.email, user: node.user,
99
+ group: node.group,
100
+ significant_changes: significant_changes)
77
101
  node.modified
78
102
  logger.info "Configuration updated for #{node.group}/#{node.name}"
79
- Oxidized.hooks.handle :post_store, node: node,
80
- job: job,
81
- commitref: output.commitref
103
+ Oxidized.hooks.post_store(node: node, job: job, commitref: output.commitref)
82
104
  end
83
105
  node.reset
84
106
  end
@@ -97,8 +119,7 @@ module Oxidized
97
119
  @jobs_done += 1
98
120
  msg += ", retries exhausted, giving up"
99
121
  node.retry = 0
100
- Oxidized.hooks.handle :node_fail, node: node,
101
- job: job
122
+ Oxidized.hooks.node_fail(node: node, job: job)
102
123
  end
103
124
  logger.warn msg
104
125
  end
@@ -110,7 +131,7 @@ module Oxidized
110
131
 
111
132
  def run_done_hook
112
133
  logger.debug "Running :nodes_done hook"
113
- Oxidized.hooks.handle :nodes_done
134
+ Oxidized.hooks.nodes_done
114
135
  rescue StandardError => e
115
136
  # swallow the hook erros and continue as normal
116
137
  logger.error e.message
data/lib/refinements.rb CHANGED
@@ -32,6 +32,24 @@ module Refinements
32
32
  # rubocop:enable Naming/MemoizedInstanceVariableName
33
33
  end
34
34
 
35
+ # keeps lines matching any pattern (String or Regexp)
36
+ def keep_lines(patterns)
37
+ each_line.select do |line|
38
+ patterns.any? do |pattern|
39
+ pattern.is_a?(Regexp) ? line =~ pattern : line.include?(pattern)
40
+ end
41
+ end.join
42
+ end
43
+
44
+ # remove lines matching any pattern (String or Regexp)
45
+ def reject_lines(patterns)
46
+ each_line.reject do |line|
47
+ patterns.any? do |pattern|
48
+ pattern.is_a?(Regexp) ? line =~ pattern : line.include?(pattern)
49
+ end
50
+ end.join
51
+ end
52
+
35
53
  # Initializes the String instance variables from another String instance
36
54
  # when the given str is an instance of String with Oxidized refinements applied
37
55
  def init_from_string(str = '')
data/oxidized.gemspec CHANGED
@@ -18,47 +18,51 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.metadata['rubygems_mfa_required'] = 'true'
20
20
 
21
- s.required_ruby_version = '>= 3.1'
21
+ # Ruby version strategy
22
+ #
23
+ # We try to keep the minimum version of Ruby to match supported LTS versions
24
+ # of common Linux distributions, as long as the maintenance effort remains
25
+ # reasonable.
26
+ s.required_ruby_version = '>= 3.0'
22
27
 
23
28
  # Gemspec strategy
24
29
  #
25
30
  # For dependency and optional dependencies, we try to set the minimal
26
- # dependency lower than the Ubuntu Noble or Debian Bookworm package version,
31
+ # dependency lower or equal to the Debian 13 "Trixie" package version,
27
32
  # so that native packages can be used.
28
- # We limit the maximal version so that dependabot can warn about new versions
29
- # and we can test them before activating them in Oxidized.
30
- #
31
- # development dependencies are set to the latest minor version of a library
32
- # and updated after having tested them
33
+ # We limit the maximal minor version so that dependabot can warn about new
34
+ # major versions and we can test them before activating them in Oxidized.
33
35
 
34
36
  s.add_dependency 'asetus', '~> 0.4'
35
37
  s.add_dependency 'bcrypt_pbkdf', '~> 1.0'
36
38
  s.add_dependency 'ed25519', '~> 1.2'
37
39
  s.add_dependency 'net-ftp', '~> 0.2'
38
40
  s.add_dependency 'net-http-digest_auth', '~> 1.4'
39
- s.add_dependency 'net-scp', '~> 4.1'
40
41
  s.add_dependency 'net-ssh', '~> 7.3'
41
42
  s.add_dependency 'net-telnet', '~> 0.2'
42
43
  s.add_dependency 'psych', '~> 5.0'
43
44
  s.add_dependency 'rugged', '~> 1.6'
44
- s.add_dependency 'semantic_logger', '~> 4.17.0'
45
+ s.add_dependency 'semantic_logger', '~> 4.17'
45
46
  s.add_dependency 'slop', '~> 4.6'
46
- s.add_dependency 'syslog', '~> 0.3.0'
47
- s.add_dependency 'syslog_protocol', '~> 0.9.2'
47
+ s.add_dependency 'syslog', '~> 0.3'
48
+ s.add_dependency 'syslog_protocol', '~> 0.9'
48
49
 
49
- # ruby-git 4.0 requests ruby >= 3.2, we stick to ruby >= 3.1 (Ubuntu Noble/Debian Bookworm)
50
- s.add_development_dependency 'git', '>= 2.0', '< 3.2.0'
51
- s.add_development_dependency 'minitest', '~> 5.26.0'
52
- s.add_development_dependency 'mocha', '~> 2.1'
53
- s.add_development_dependency 'pry', '~> 0.15.0'
54
- s.add_development_dependency 'rake', '~> 13.0'
55
- s.add_development_dependency 'rubocop', '~> 1.81.0'
56
- s.add_development_dependency 'rubocop-minitest', '~> 0.38.0'
57
- s.add_development_dependency 'rubocop-rake', '~> 0.7.0'
58
- s.add_development_dependency 'rubocop-sequel', '~> 0.4.0'
59
- s.add_development_dependency 'simplecov', '~> 0.22.0'
50
+ s.add_development_dependency 'minitest', '>= 5.18', '<7'
51
+ s.add_development_dependency 'mocha', '>= 2.1', '<4'
52
+ s.add_development_dependency 'pry', '~> 0.15'
53
+ s.add_development_dependency 'rake', '~> 13.0'
54
+ # Rubocop introduces new rules in minor versions, so we limit automatic
55
+ # updates to patches
56
+ s.add_development_dependency 'rubocop', '~> 1.86.0'
57
+ s.add_development_dependency 'rubocop-minitest', '~> 0.39.1'
58
+ s.add_development_dependency 'rubocop-rake', '~> 0.7.0'
59
+ s.add_development_dependency 'rubocop-sequel', '~> 0.4.0'
60
+ s.add_development_dependency 'simplecov', '~> 0.22'
60
61
 
61
62
  # Dependencies on optional libraries, used for unit tests & development
62
- s.add_development_dependency 'oxidized-web', '>= 0.17.1'
63
- s.add_development_dependency 'sequel', '>= 5.63.0', '< 5.100.0'
63
+ s.add_development_dependency 'git', '>= 2.0', '< 5'
64
+ s.add_development_dependency 'net-scp', '~> 4.1'
65
+ s.add_development_dependency 'net-tftp', '~> 0.1.0'
66
+ s.add_development_dependency 'oxidized-web', '>= 0.17.1'
67
+ s.add_development_dependency 'sequel', '>= 5.63.0', '< 6'
64
68
  end