oxidized 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -0
  3. data/CHANGELOG.md +6 -0
  4. data/Gemfile.lock +41 -0
  5. data/README.md +64 -35
  6. data/Rakefile +7 -9
  7. data/bin/console +9 -0
  8. data/bin/oxidized +1 -1
  9. data/extra/nagios_check_failing_nodes.rb +4 -4
  10. data/extra/oxidized-report-git-commits +80 -0
  11. data/extra/rvm.oxidized.upstart +18 -0
  12. data/lib/oxidized.rb +45 -2
  13. data/lib/oxidized/cli.rb +54 -4
  14. data/lib/oxidized/config.rb +45 -39
  15. data/lib/oxidized/config/vars.rb +8 -3
  16. data/lib/oxidized/core.rb +5 -12
  17. data/lib/oxidized/hook.rb +4 -4
  18. data/lib/oxidized/hook/exec.rb +2 -0
  19. data/lib/oxidized/hook/githubrepo.rb +57 -0
  20. data/lib/oxidized/input/cli.rb +2 -1
  21. data/lib/oxidized/input/ftp.rb +3 -3
  22. data/lib/oxidized/input/ssh.rb +9 -9
  23. data/lib/oxidized/input/telnet.rb +3 -3
  24. data/lib/oxidized/job.rb +3 -3
  25. data/lib/oxidized/model/fortios.rb +1 -1
  26. data/lib/oxidized/model/junos.rb +8 -10
  27. data/lib/oxidized/model/model.rb +3 -2
  28. data/lib/oxidized/model/powerconnect.rb +11 -6
  29. data/lib/oxidized/model/saos.rb +24 -0
  30. data/lib/oxidized/node.rb +16 -16
  31. data/lib/oxidized/nodes.rb +12 -12
  32. data/lib/oxidized/output/file.rb +15 -3
  33. data/lib/oxidized/output/git.rb +10 -7
  34. data/lib/oxidized/source/csv.rb +7 -7
  35. data/lib/oxidized/source/http.rb +1 -1
  36. data/lib/oxidized/source/source.rb +1 -1
  37. data/lib/oxidized/source/sql.rb +7 -7
  38. data/lib/oxidized/string.rb +1 -1
  39. data/lib/oxidized/version.rb +3 -0
  40. data/lib/oxidized/worker.rb +11 -9
  41. data/oxidized.gemspec +12 -3
  42. metadata +75 -7
  43. data/lib/oxidized/log.rb +0 -22
  44. data/spec/nodes_spec.rb +0 -46
@@ -11,7 +11,8 @@ module Oxidized
11
11
  klass.instance_variable_set '@cfg', Hash.new { |h,k| h[k] = [] }
12
12
  klass.instance_variable_set '@procs', Hash.new { |h,k| h[k] = [] }
13
13
  klass.instance_variable_set '@expect', []
14
- klass.const_set :CFG, CFG
14
+ klass.instance_variable_set '@comment', nil
15
+ klass.instance_variable_set '@prompt', nil
15
16
  end
16
17
  def comment _comment='# '
17
18
  return @comment if @comment
@@ -78,7 +79,7 @@ module Oxidized
78
79
  attr_accessor :input, :node
79
80
 
80
81
  def cmd string, &block
81
- out = @input.cmd string
82
+ out = "====================== #{string} ======================\n" + @input.cmd(string)
82
83
  return false unless out
83
84
  self.class.cmds[:all].each do |all_block|
84
85
  out = instance_exec Oxidized::String.new(out), string, &all_block
@@ -14,6 +14,9 @@ class PowerConnect < Oxidized::Model
14
14
  end
15
15
 
16
16
  cmd 'show version' do |cfg|
17
+ if (@stackable.nil?)
18
+ @stackable = true if cfg.match /(U|u)nit\s/
19
+ end
17
20
  cfg = cfg.split("\n").select { |line| not line[/Up\sTime/] }
18
21
  comment cfg.join("\n") + "\n"
19
22
  end
@@ -47,15 +50,17 @@ class PowerConnect < Oxidized::Model
47
50
 
48
51
  def clean cfg
49
52
  out = []
50
- skip_block = false
53
+ skip_blocks = 0
51
54
  cfg.each_line do |line|
52
- if line.match /Up\sTime|Temperature|Power Supplies/i
53
- # For 34xx, 35xx, 54xx, 55xx, 62xx and 8024F we should skip this block (terminated by a blank line)
54
- skip_block = true if @model =~ /^(34|35)(24|48)$|^(54|55)(24|48)$|^(62)(24|48)$|^8024$/
55
+ # If this is a stackable switch we should skip this block of information
56
+ if (line.match /Up\sTime|Temperature|Power Suppl(ies|y)|Fans/i and @stackable == true)
57
+ skip_blocks = 1
58
+ # Some switches have another empty line. This is identified by this line having a colon
59
+ skip_blocks = 2 if line.match /:/
55
60
  end
56
61
  # If we have lines to skip do this until we reach and empty line
57
- if skip_block
58
- skip_block = false if /\S/ !~ line
62
+ if skip_blocks > 0
63
+ skip_blocks -= 1 if /\S/ !~ line
59
64
  next
60
65
  end
61
66
  out << line.strip
@@ -0,0 +1,24 @@
1
+ class SAOS < Oxidized::Model
2
+
3
+ # Ciena SAOS switch
4
+ # used for 6.x devices
5
+
6
+ comment '! '
7
+
8
+ cmd :all do |cfg|
9
+ cfg.each_line.to_a[1..-2].join
10
+ end
11
+
12
+ cmd 'configuration show' do |cfg|
13
+ cfg
14
+ end
15
+
16
+ cfg :telnet do
17
+ username /login:/
18
+ password /assword:/
19
+ end
20
+ cfg :telnet do
21
+ post_login 'system shell session set more off'
22
+ pre_logout 'exit'
23
+ end
24
+ end
data/lib/oxidized/node.rb CHANGED
@@ -5,10 +5,13 @@ module Oxidized
5
5
  class MethodNotFound < OxidizedError; end
6
6
  class ModelNotFound < OxidizedError; end
7
7
  class Node
8
- attr_reader :name, :ip, :model, :input, :output, :group, :auth, :prompt, :vars, :last
8
+ attr_reader :name, :ip, :model, :input, :output, :group, :auth, :prompt, :vars, :last, :repo
9
9
  attr_accessor :running, :user, :msg, :from, :stats, :retry
10
10
  alias :running? :running
11
11
  def initialize opt
12
+ if Oxidized.config.debug == true or opt[:debug] == true
13
+ puts 'resolving DNS for %s...' % opt[:name]
14
+ end
12
15
  @name = opt[:name]
13
16
  @ip = IPAddr.new(opt[:ip]).to_s rescue nil
14
17
  @ip ||= Resolv.new.getaddress @name
@@ -21,6 +24,7 @@ module Oxidized
21
24
  @vars = opt[:vars]
22
25
  @stats = Stats.new
23
26
  @retry = 0
27
+ @repo = Oxidized.config.output.git.repo
24
28
 
25
29
  # model instance needs to access node instance
26
30
  @model.node = self
@@ -54,9 +58,7 @@ module Oxidized
54
58
  end
55
59
  end
56
60
  begin
57
- if input.connect self
58
- input.get
59
- end
61
+ input.connect(self) and input.get
60
62
  rescue *rescue_fail.keys => err
61
63
  resc = ''
62
64
  if not level = rescue_fail[err.class]
@@ -64,7 +66,7 @@ module Oxidized
64
66
  level = rescue_fail[resc]
65
67
  resc = " (rescued #{resc})"
66
68
  end
67
- Log.send(level, '%s raised %s%s with msg "%s"' % [self.ip, err.class, resc, err.message])
69
+ Oxidized.logger.send(level, '%s raised %s%s with msg "%s"' % [self.ip, err.class, resc, err.message])
68
70
  return false
69
71
  rescue => err
70
72
  file = Oxidized::Config::Crash + '.' + self.ip.to_s
@@ -74,7 +76,7 @@ module Oxidized
74
76
  fh.puts '-' * 50
75
77
  fh.puts err.backtrace
76
78
  end
77
- Log.error '%s raised %s with msg "%s", %s saved' % [self.ip, err.class, err.message, file]
79
+ Oxidized.logger.error '%s raised %s with msg "%s", %s saved' % [self.ip, err.class, err.message, file]
78
80
  return false
79
81
  end
80
82
  end
@@ -122,19 +124,17 @@ module Oxidized
122
124
  private
123
125
 
124
126
  def resolve_prompt opt
125
- prompt = opt[:prompt]
126
- prompt ||= @model.prompt
127
- prompt ||= CFG.prompt
127
+ opt[:prompt] || @model.prompt || Oxidized.config.prompt
128
128
  end
129
129
 
130
130
  def resolve_auth opt
131
131
  # Resolve configured username/password, give priority to group level configuration
132
132
  # TODO: refactor to use revised behaviour of Asetus
133
133
  cfg_username, cfg_password =
134
- if CFG.groups.has_key?(@group) and ['username', 'password'].all? {|e| CFG.groups[@group].has_key?(e)}
135
- [CFG.groups[@group].username, CFG.groups[@group].password]
136
- elsif ['username', 'password'].all? {|e| CFG.has_key?(e)}
137
- [CFG.username, CFG.password]
134
+ if Oxidized.config.groups.has_key?(@group) and ['username', 'password'].all? {|e| Oxidized.config.groups[@group].has_key?(e)}
135
+ [Oxidized.config.groups[@group].username, Oxidized.config.groups[@group].password]
136
+ elsif ['username', 'password'].all? {|e| Oxidized.config.has_key?(e)}
137
+ [Oxidized.config.username, Oxidized.config.password]
138
138
  else
139
139
  [nil, nil]
140
140
  end
@@ -145,7 +145,7 @@ module Oxidized
145
145
  end
146
146
 
147
147
  def resolve_input opt
148
- inputs = (opt[:input] or CFG.input.default)
148
+ inputs = (opt[:input] or Oxidized.config.input.default)
149
149
  inputs.split(/\s*,\s*/).map do |input|
150
150
  if not Oxidized.mgr.input[input]
151
151
  Oxidized.mgr.add_input input or raise MethodNotFound, "#{input} not found for node #{ip}"
@@ -155,7 +155,7 @@ module Oxidized
155
155
  end
156
156
 
157
157
  def resolve_output opt
158
- output = (opt[:output] or CFG.output.default)
158
+ output = (opt[:output] or Oxidized.config.output.default)
159
159
  if not Oxidized.mgr.output[output]
160
160
  Oxidized.mgr.add_output output or raise MethodNotFound, "#{output} not found for node #{ip}"
161
161
  end
@@ -163,7 +163,7 @@ module Oxidized
163
163
  end
164
164
 
165
165
  def resolve_model opt
166
- model = (opt[:model] or CFG.model)
166
+ model = (opt[:model] or Oxidized.config.model)
167
167
  if not Oxidized.mgr.model[model]
168
168
  Oxidized.mgr.add_model model or raise ModelNotFound, "#{model} not found for node #{ip}"
169
169
  end
@@ -1,15 +1,15 @@
1
1
  module Oxidized
2
- require 'ipaddr'
3
- require 'oxidized/node'
4
- class Oxidized::NotSupported < OxidizedError; end
5
- class Oxidized::NodeNotFound < OxidizedError; end
2
+ require 'ipaddr'
3
+ require 'oxidized/node'
4
+ class Oxidized::NotSupported < OxidizedError; end
5
+ class Oxidized::NodeNotFound < OxidizedError; end
6
6
  class Nodes < Array
7
7
  attr_accessor :source
8
8
  alias :put :unshift
9
9
  def load node_want=nil
10
10
  with_lock do
11
11
  new = []
12
- @source = CFG.source.default
12
+ @source = Oxidized.config.source.default
13
13
  Oxidized.mgr.add_source @source
14
14
  Oxidized.mgr.source[@source].new.load.each do |node|
15
15
  # we want to load specific node(s), not all of them
@@ -18,13 +18,13 @@ module Oxidized
18
18
  _node = Node.new node
19
19
  new.push _node
20
20
  rescue ModelNotFound => err
21
- Log.error "node %s raised %s with message '%s'" % [node, err.class, err.message]
21
+ Oxidized.logger.error "node %s raised %s with message '%s'" % [node, err.class, err.message]
22
22
  rescue Resolv::ResolvError => err
23
- Log.error "node %s is not resolvable, raised %s with message '%s'" % [node, err.class, err.message]
23
+ Oxidized.logger.error "node %s is not resolvable, raised %s with message '%s'" % [node, err.class, err.message]
24
24
  end
25
25
  end
26
26
  size == 0 ? replace(new) : update_nodes(new)
27
- Log.info "Loaded #{size} nodes"
27
+ Oxidized.logger.info "Loaded #{size} nodes"
28
28
  end
29
29
  end
30
30
 
@@ -148,9 +148,9 @@ module Oxidized
148
148
  end
149
149
  end
150
150
  end
151
-
151
+
152
152
  public
153
-
153
+
154
154
  def version node, group
155
155
  with_lock do
156
156
  i = find_node_index node
@@ -159,7 +159,7 @@ module Oxidized
159
159
  output.version node, group
160
160
  end
161
161
  end
162
-
162
+
163
163
  def get_version node, group, oid
164
164
  with_lock do
165
165
  i = find_node_index node
@@ -168,7 +168,7 @@ module Oxidized
168
168
  output.get_version node, group, oid
169
169
  end
170
170
  end
171
-
171
+
172
172
  def get_diff node, group, oid1, oid2
173
173
  with_lock do
174
174
  i = find_node_index node
@@ -2,14 +2,16 @@ module Oxidized
2
2
  class OxidizedFile < Output
3
3
  require 'fileutils'
4
4
 
5
+ attr_reader :commitref
6
+
5
7
  def initialize
6
- @cfg = CFG.output.file
8
+ @cfg = Oxidized.config.output.file
7
9
  end
8
10
 
9
11
  def setup
10
12
  if @cfg.empty?
11
- CFGS.user.output.file.directory = File.join(Config::Root, 'configs')
12
- CFGS.save :user
13
+ Oxidized.asetus.user.output.file.directory = File.join(Config::Root, 'configs')
14
+ Oxidized.asetus.save :user
13
15
  raise NoConfig, 'no output file config, edit ~/.config/oxidized/config'
14
16
  end
15
17
  end
@@ -22,6 +24,7 @@ class OxidizedFile < Output
22
24
  FileUtils.mkdir_p file
23
25
  file = File.join file, node
24
26
  open(file, 'w') { |fh| fh.write outputs.to_cfg }
27
+ @commitref = file
25
28
  end
26
29
 
27
30
  def fetch node, group
@@ -39,5 +42,14 @@ class OxidizedFile < Output
39
42
  end
40
43
  end
41
44
 
45
+ def version node, group
46
+ # not supported
47
+ []
48
+ end
49
+
50
+ def get_version node, group, oid
51
+ 'not supported'
52
+ end
53
+
42
54
  end
43
55
  end
@@ -7,16 +7,18 @@ class Git < Output
7
7
  raise OxidizedError, 'rugged not found: sudo gem install rugged'
8
8
  end
9
9
 
10
+ attr_reader :commitref
11
+
10
12
  def initialize
11
- @cfg = CFG.output.git
13
+ @cfg = Oxidized.config.output.git
12
14
  end
13
15
 
14
16
  def setup
15
17
  if @cfg.empty?
16
- CFGS.user.output.git.user = 'Oxidized'
17
- CFGS.user.output.git.email = 'o@example.com'
18
- CFGS.user.output.git.repo = File.join(Config::Root, 'oxidized.git')
19
- CFGS.save :user
18
+ Oxidized.asetus.user.output.git.user = 'Oxidized'
19
+ Oxidized.asetus.user.output.git.email = 'o@example.com'
20
+ Oxidized.asetus.user.output.git.repo = File.join(Config::Root, 'oxidized.git')
21
+ Oxidized.asetus.save :user
20
22
  raise NoConfig, 'no output git config, edit ~/.config/oxidized/config'
21
23
  end
22
24
  @cfg.repo = File.expand_path @cfg.repo
@@ -27,6 +29,7 @@ class Git < Output
27
29
  @user = (opt[:user] or @cfg.user)
28
30
  @email = (opt[:email] or @cfg.email)
29
31
  @opt = opt
32
+ @commitref = nil
30
33
  repo = @cfg.repo
31
34
 
32
35
  outputs.types.each do |type|
@@ -63,7 +66,7 @@ class Git < Output
63
66
  end
64
67
  end
65
68
 
66
- #give a hash of all oid revision for the givin node, and the date of the commit
69
+ #give a hash of all oid revision for the given node, and the date of the commit
67
70
  def version node, group
68
71
  begin
69
72
  repo = @cfg.repo
@@ -176,7 +179,7 @@ class Git < Output
176
179
  if tree_old != tree_new
177
180
  repo.config['user.name'] = user
178
181
  repo.config['user.email'] = email
179
- Rugged::Commit.create(repo,
182
+ @commitref = Rugged::Commit.create(repo,
180
183
  :tree => index.write_tree(repo),
181
184
  :message => msg,
182
185
  :parents => repo.empty? ? [] : [repo.head.target].compact,
@@ -1,17 +1,17 @@
1
1
  module Oxidized
2
2
  class CSV < Source
3
3
  def initialize
4
- @cfg = CFG.source.csv
4
+ @cfg = Oxidized.config.source.csv
5
5
  super
6
6
  end
7
7
 
8
8
  def setup
9
9
  if @cfg.empty?
10
- CFGS.user.source.csv.file = File.join(Config::Root, 'router.db')
11
- CFGS.user.source.csv.delimiter = /:/
12
- CFGS.user.source.csv.map.name = 0
13
- CFGS.user.source.csv.map.model = 1
14
- CFGS.save :user
10
+ Oxidized.asetus.user.source.csv.file = File.join(Config::Root, 'router.db')
11
+ Oxidized.asetus.user.source.csv.delimiter = /:/
12
+ Oxidized.asetus.user.source.csv.map.name = 0
13
+ Oxidized.asetus.user.source.csv.map.model = 1
14
+ Oxidized.asetus.save :user
15
15
  raise NoConfig, 'no source csv config, edit ~/.config/oxidized/config'
16
16
  end
17
17
  end
@@ -19,7 +19,7 @@ class CSV < Source
19
19
  def load
20
20
  nodes = []
21
21
  open(File.expand_path @cfg.file).each_line do |line|
22
- next if line.match /^\s*#/
22
+ next if line.match(/^\s*#/)
23
23
  data = line.chomp.split @cfg.delimiter
24
24
  next if data.empty?
25
25
  # map node parameters
@@ -1,7 +1,7 @@
1
1
  module Oxidized
2
2
  class HTTP < Source
3
3
  def initialize
4
- @cfg = CFG.source.http
4
+ @cfg = Oxidized.config.source.http
5
5
  super
6
6
  end
7
7
 
@@ -2,7 +2,7 @@ module Oxidized
2
2
  class Source
3
3
  class NoConfig < OxidizedError; end
4
4
  def initialize
5
- @map = (CFG.model_map or {})
5
+ @map = (Oxidized.config.model_map or {})
6
6
  end
7
7
  def map_model model
8
8
  @map.has_key?(model) ? @map[model] : model
@@ -8,12 +8,12 @@ class SQL < Source
8
8
 
9
9
  def setup
10
10
  if @cfg.empty?
11
- CFGS.user.source.sql.adapter = 'sqlite'
12
- CFGS.user.source.sql.database = File.join(Config::Root, 'sqlite.db')
13
- CFGS.user.source.sql.table = 'devices'
14
- CFGS.user.source.sql.map.name = 'name'
15
- CFGS.user.source.sql.map.model = 'rancid'
16
- CFGS.save :user
11
+ Oxidized.asetus.user.source.sql.adapter = 'sqlite'
12
+ Oxidized.asetus.user.source.sql.database = File.join(Config::Root, 'sqlite.db')
13
+ Oxidized.asetus.user.source.sql.table = 'devices'
14
+ Oxidized.asetus.user.source.sql.map.name = 'name'
15
+ Oxidized.asetus.user.source.sql.map.model = 'rancid'
16
+ Oxidized.asetus.save :user
17
17
  raise NoConfig, 'no source sql config, edit ~/.config/oxidized/config'
18
18
  end
19
19
  end
@@ -44,7 +44,7 @@ class SQL < Source
44
44
 
45
45
  def initialize
46
46
  super
47
- @cfg = CFG.source.sql
47
+ @cfg = Oxidized.config.source.sql
48
48
  end
49
49
 
50
50
  def connect
@@ -16,7 +16,7 @@ module Oxidized
16
16
  # sets @cmd and @name unless @name is already set
17
17
  def set_cmd command
18
18
  @cmd = command
19
- @name = @cmd.strip.gsub(/\s+/, '_') if @name == nil
19
+ @name ||= @cmd.strip.gsub(/\s+/, '_')
20
20
  end
21
21
 
22
22
  def initialize str=''
@@ -0,0 +1,3 @@
1
+ module Oxidized
2
+ VERSION = '0.10.0'
3
+ end
@@ -4,7 +4,7 @@ module Oxidized
4
4
  class Worker
5
5
  def initialize nodes
6
6
  @nodes = nodes
7
- @jobs = Jobs.new CFG.threads, CFG.interval, @nodes
7
+ @jobs = Jobs.new(Oxidized.config.threads, Oxidized.config.interval, @nodes)
8
8
  Thread.abort_on_exception = true
9
9
  end
10
10
 
@@ -14,11 +14,11 @@ module Oxidized
14
14
  ended.each { |job| process job }
15
15
  @jobs.work
16
16
  while @jobs.size < @jobs.want
17
- Log.debug "Jobs #{@jobs.size}, Want: #{@jobs.want}"
17
+ Oxidized.logger.debug "Jobs #{@jobs.size}, Want: #{@jobs.want}"
18
18
  # ask for next node in queue non destructive way
19
19
  nextnode = @nodes.first
20
20
  unless nextnode.last.nil?
21
- break if nextnode.last.end + CFG.interval > Time.now.utc
21
+ break if nextnode.last.end + Oxidized.config.interval > Time.now.utc
22
22
  end
23
23
  # shift nodes and get the next node
24
24
  node = @nodes.get
@@ -39,16 +39,18 @@ module Oxidized
39
39
  msg = "update #{node.name}"
40
40
  msg += " from #{node.from}" if node.from
41
41
  msg += " with message '#{node.msg}'" if node.msg
42
- if node.output.new.store node.name, job.config,
42
+ output = node.output.new
43
+ if output.store node.name, job.config,
43
44
  :msg => msg, :user => node.user, :group => node.group
44
- Log.info "Configuration updated for #{node.group}/#{node.name}"
45
+ Oxidized.logger.info "Configuration updated for #{node.group}/#{node.name}"
45
46
  Oxidized.Hooks.handle :post_store, :node => node,
46
- :job => job
47
+ :job => job,
48
+ :commitref => output.commitref
47
49
  end
48
50
  node.reset
49
51
  else
50
52
  msg = "#{node.name} status #{job.status}"
51
- if node.retry < CFG.retries
53
+ if node.retry < Oxidized.config.retries
52
54
  node.retry += 1
53
55
  msg += ", retry attempt #{node.retry}"
54
56
  @nodes.next node.name
@@ -58,10 +60,10 @@ module Oxidized
58
60
  Oxidized.Hooks.handle :node_fail, :node => node,
59
61
  :job => job
60
62
  end
61
- Log.warn msg
63
+ Oxidized.logger.warn msg
62
64
  end
63
65
  rescue NodeNotFound
64
- Log.warn "#{node.name} not found, removed while collecting?"
66
+ Oxidized.logger.warn "#{node.name} not found, removed while collecting?"
65
67
  end
66
68
 
67
69
  end