diff options
-rw-r--r-- | .rubocop_todo.yml | 2 | ||||
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | docs/Supported-OS-Types.md | 1 | ||||
-rw-r--r-- | lib/oxidized/input/telnet.rb | 20 | ||||
-rw-r--r-- | lib/oxidized/model/boss.rb | 4 | ||||
-rw-r--r-- | lib/oxidized/model/opengear.rb | 2 | ||||
-rw-r--r-- | lib/oxidized/model/openwrt.rb | 77 | ||||
-rw-r--r-- | lib/oxidized/source/csv.rb | 2 | ||||
-rw-r--r-- | lib/oxidized/worker.rb | 7 |
10 files changed, 108 insertions, 13 deletions
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 77c1026..6598a37 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -140,7 +140,7 @@ Metrics/CyclomaticComplexity: # Offense count: 53 # Configuration parameters: CountComments. Metrics/MethodLength: - Max: 72 + Max: 75 # Offense count: 2 # Configuration parameters: CountKeywordArgs. diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e03883..4e7f09e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Master + +* FEATURE: openwrt model (@z00nx) + ## 0.21.0 * FEATURE: routeros include system history (@InsaneSplash) @@ -1,4 +1,4 @@ -# Oxidized [![Build Status](https://travis-ci.org/ytti/oxidized.svg)](https://travis-ci.org/ytti/oxidized) [![codecov.io](https://codecov.io/gh/ytti/oxidized/coverage.svg?branch=master)](https://codecov.io/gh/ytti/oxidized?branch=master) [![Gem Version](https://badge.fury.io/rb/oxidized.svg)](http://badge.fury.io/rb/oxidized) [![Join the chat at https://gitter.im/oxidized/Lobby](https://badges.gitter.im/oxidized/Lobby.svg)](https://gitter.im/oxidized/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +# Oxidized [![Build Status](https://api.travis-ci.com/ytti/oxidized.svg)](https://travis-ci.com/ytti/oxidized) [![codecov.io](https://codecov.io/gh/ytti/oxidized/coverage.svg?branch=master)](https://codecov.io/gh/ytti/oxidized?branch=master) [![Gem Version](https://badge.fury.io/rb/oxidized.svg)](http://badge.fury.io/rb/oxidized) [![Join the chat at https://gitter.im/oxidized/Lobby](https://badges.gitter.im/oxidized/Lobby.svg)](https://gitter.im/oxidized/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Oxidized is a network device configuration backup tool. It's a RANCID replacement! diff --git a/docs/Supported-OS-Types.md b/docs/Supported-OS-Types.md index 8839cc3..75845b9 100644 --- a/docs/Supported-OS-Types.md +++ b/docs/Supported-OS-Types.md @@ -127,6 +127,7 @@ * [OneOS](/lib/oxidized/model/oneos.rb) * Opengear * [Opengear](/lib/oxidized/model/opengear.rb) +* [OpenWRT](/lib/oxidized/model/openwrt.rb) * [OPNsense](/lib/oxidized/model/opnsense.rb) * Palo Alto * [PANOS](/lib/oxidized/model/panos.rb) diff --git a/lib/oxidized/input/telnet.rb b/lib/oxidized/input/telnet.rb index b1b3222..e7c2ee3 100644 --- a/lib/oxidized/input/telnet.rb +++ b/lib/oxidized/input/telnet.rb @@ -10,15 +10,15 @@ module Oxidized @node = node @timeout = Oxidized.config.timeout @node.model.cfg['telnet'].each { |cb| instance_exec(&cb) } + @log = File.open(Oxidized::Config::Log + "/#{@node.ip}-telnet", 'w') if Oxidized.config.input.debug? port = vars(:telnet_port) || 23 - opt = { 'Host' => @node.ip, - 'Port' => port.to_i, - 'Timeout' => @timeout, - 'Model' => @node.model } - opt['Output_log'] = Oxidized::Config::Log + "/#{@node.ip}-telnet" if Oxidized.config.input.debug? + telnet_opts = { 'Host' => @node.ip, + 'Port' => port.to_i, + 'Timeout' => @timeout, + 'Model' => @node.model } - @telnet = Net::Telnet.new opt + @telnet = Net::Telnet.new telnet_opts if @node.auth[:username] and @node.auth[:username].length > 0 expect username @telnet.puts @node.auth[:username] @@ -62,6 +62,9 @@ module Oxidized disconnect_cli @telnet.close rescue Errno::ECONNRESET + ensure + @log.close if Oxidized.config.input.debug? + (@telnet.close rescue true) unless @telnet.closed? end end end @@ -137,7 +140,10 @@ class Net::Telnet buf.gsub!(/#{EOL}/no, "\n") end end - @log.print(buf) if @options.has_key?("Output_log") + if Oxidized.config.input.debug? + @log.print buf + @log.flush + end line += buf line = model.expects line line = yield line if block_given? diff --git a/lib/oxidized/model/boss.rb b/lib/oxidized/model/boss.rb index cf762ee..0adf4c0 100644 --- a/lib/oxidized/model/boss.rb +++ b/lib/oxidized/model/boss.rb @@ -1,5 +1,5 @@ class Boss < Oxidized::Model - # Avaya Baystack Operating System Software(BOSS) + # Extreme Baystack Operating System Software(BOSS) # Created by danielcoxman@gmail.com # May 15, 2017 # This was tested on ers3510, ers5530, ers4850, ers5952 @@ -28,6 +28,8 @@ class Boss < Oxidized::Model expect /ommand Line Interface\.\.\./ do |data, re| send "c" data.sub re, '' + send "\n" + data.sub re, '' end # needed for proper formatting diff --git a/lib/oxidized/model/opengear.rb b/lib/oxidized/model/opengear.rb index e0d4c0a..1f94edb 100644 --- a/lib/oxidized/model/opengear.rb +++ b/lib/oxidized/model/opengear.rb @@ -1,7 +1,7 @@ class OpenGear < Oxidized::Model comment '# ' - prompt /^(\$\s)?$/ + prompt /^(\$\s)$/ cmd :secret do |cfg| cfg.gsub!(/password (\S+)/, 'password <secret removed>') diff --git a/lib/oxidized/model/openwrt.rb b/lib/oxidized/model/openwrt.rb new file mode 100644 index 0000000..7ba9e98 --- /dev/null +++ b/lib/oxidized/model/openwrt.rb @@ -0,0 +1,77 @@ +class OpenWrt < Oxidized::Model + prompt /^[^#]+#/ + comment '#' + + cmd 'cat /etc/banner' do |cfg| + comment "#### Info: /etc/banner #####\n#{cfg}" + end + + cmd 'cat /proc/cpuinfo' do |cfg| + comment "#### Info: /proc/cpuinfo #####\n#{cfg}" + end + + cmd 'cat /etc/openwrt_release' do |cfg| + comment "#### Info: /etc/openwrt_release #####\n#{cfg}" + end + + cmd 'sysupgrade -l' do |cfg| + @sysupgradefiles = cfg + comment "#### Info: sysupgrade -l #####\n#{cfg}" + end + + cmd 'cat /proc/mtd' do |cfg| + @mtdpartitions = cfg + comment "#### Info: /proc/mtd #####\n#{cfg}" + end + + post do + cfg = [] + binary_files = vars(:openwrt_binary_files) || %w[/etc/dropbear/dropbear_rsa_host_key] + non_sensitive_files = vars(:openwrt_non_sensitive_files) || %w[rpcd uhttpd] + partitions_to_backup = vars(:openwrt_partitions_to_backup) || %w[art devinfo u_env config caldata] + @sysupgradefiles.lines.each do |sysupgradefile| + sysupgradefile = sysupgradefile.strip + if sysupgradefile.start_with?('/etc/config/') + unless sysupgradefile.end_with?('-opkg') + filename = sysupgradefile.split('/')[-1] + cfg << comment("#### File: #{sysupgradefile} #####") + uciexport = cmd("uci export #{filename}") + Oxidized.logger.debug "Exporting uci config - #{filename}" + if vars(:remove_secret) && !(non_sensitive_files.include? filename) + Oxidized.logger.debug "Scrubbing uci config - #{filename}" + uciexport.gsub!(/^(\s+option\s+(password|key)\s+')[^']+'/, '\\1<secret hidden>\'') + end + cfg << uciexport + end + elsif binary_files.include? sysupgradefile + Oxidized.logger.debug "Exporting binary file - #{sysupgradefile}" + cfg << comment("#### Binary file: #{sysupgradefile} #####") + cfg << comment("Decode using 'echo -en <data> | gzip -dc > #{sysupgradefile}'") + cfg << cmd("gzip -c #{sysupgradefile} | hexdump -ve '1/1 \"_x%.2x\"' | tr _ \\") + elsif vars(:remove_secret) && sysupgradefile == '/etc/shadow' + Oxidized.logger.debug 'Exporting and scrubbing /etc/shadow' + cfg << comment("#### File: #{sysupgradefile} #####") + shadow = cmd("cat #{sysupgradefile}") + shadow.gsub!(/^([^:]+:)[^:]*(:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:)/, '\\1\\2') + cfg << shadow + else + Oxidized.logger.debug "Exporting file - #{sysupgradefile}" + cfg << comment("#### File: #{sysupgradefile} #####") + cfg << cmd("cat #{sysupgradefile}") + end + end + @mtdpartitions.scan(/(\w+):\s+\w+\s+\w+\s+"(.*)"/).each do |partition, name| + next unless vars(:openwrt_backup_partitions) && partitions_to_backup.include?(name) + Oxidized.logger.debug "Exporting partition - #{name}(#{partition})" + cfg << comment("#### Partition: #{name} /dev/#{partition} #####") + cfg << comment("Decode using 'echo -en <data> | gzip -dc > #{name}'") + cfg << cmd("dd if=/dev/#{partition} 2>/dev/null | gzip -c | hexdump -ve '1/1 \"%.2x\"'") + end + cfg.join "\n" + end + + cfg :ssh do + exec true + pre_logout 'exit' + end +end diff --git a/lib/oxidized/source/csv.rb b/lib/oxidized/source/csv.rb index ae1c0a9..7b771f6 100644 --- a/lib/oxidized/source/csv.rb +++ b/lib/oxidized/source/csv.rb @@ -18,7 +18,7 @@ module Oxidized require 'gpgme' if @cfg.gpg? end - def load + def load _node_want = nil nodes = [] file = File.expand_path(@cfg.file) file = if @cfg.gpg? diff --git a/lib/oxidized/worker.rb b/lib/oxidized/worker.rb index 692b060..5d5fc01 100644 --- a/lib/oxidized/worker.rb +++ b/lib/oxidized/worker.rb @@ -42,9 +42,9 @@ module Oxidized node.stats.add job @jobs.duration job.time node.running = false - @jobs_done += 1 # needed for worker_done event if job.status == :success + @jobs_done += 1 # needed for :nodes_done hook Oxidized.Hooks.handle :node_success, :node => node, :job => job msg = "update #{node.name}" @@ -66,6 +66,11 @@ module Oxidized msg += ", retry attempt #{node.retry}" @nodes.next node.name else + # Only increment the @jobs_done when we give up retries for a node (or success). + # As it would otherwise cause @jobs_done to be incremented with generic retries. + # This would cause :nodes_done hook to desync from running at the end of the nodelist and + # be fired when the @jobs_done > @nodes.count (could be mid-cycle on the next cycle). + @jobs_done += 1 msg += ", retries exhausted, giving up" node.retry = 0 Oxidized.Hooks.handle :node_fail, :node => node, |