diff options
Diffstat (limited to 'lib/oxidized')
33 files changed, 696 insertions, 88 deletions
diff --git a/lib/oxidized/hook/awssns.rb b/lib/oxidized/hook/awssns.rb new file mode 100644 index 0000000..dbc2d47 --- /dev/null +++ b/lib/oxidized/hook/awssns.rb @@ -0,0 +1,27 @@ +require 'aws-sdk' + +class AwsSns < Oxidized::Hook + def validate_cfg! + raise KeyError, 'hook.region is required' unless cfg.has_key?('region') + raise KeyError, 'hook.topic_arn is required' unless cfg.has_key?('topic_arn') + end + + def run_hook(ctx) + sns = Aws::SNS::Resource.new(region: cfg.region) + topic = sns.topic(cfg.topic_arn) + message = { + :event => ctx.event.to_s + } + if ctx.node + message.merge!( + :group => ctx.node.group.to_s, + :model => ctx.node.model.class.name.to_s.downcase, + :node => ctx.node.name.to_s + ) + end + topic.publish({ + message: message.to_json + }) + end + +end diff --git a/lib/oxidized/input/ssh.rb b/lib/oxidized/input/ssh.rb index cd12167..9a5c508 100644 --- a/lib/oxidized/input/ssh.rb +++ b/lib/oxidized/input/ssh.rb @@ -17,8 +17,9 @@ module Oxidized class NoShell < OxidizedError; end def connect node - @node = node - @output = '' + @node = node + @output = '' + @pty_options = { term: "vt100" } @node.model.cfg['ssh'].each { |cb| instance_exec(&cb) } secure = Oxidized.config.input.ssh.secure @log = File.open(Oxidized::Config::Log + "/#{@node.ip}-ssh", 'w') if Oxidized.config.input.debug? @@ -32,9 +33,10 @@ module Oxidized :paranoid => secure, :auth_methods => %w(none publickey password keyboard-interactive), :number_of_password_prompts => 0, - :proxy => proxy + :proxy => proxy, } - ssh_opts[:kex] = vars(:ssh_kex).split(/,\s*/) if vars(:ssh_kex) + ssh_opts[:keys] = vars(:ssh_keys).is_a?(Array) ? vars(:ssh_keys) : [vars(:ssh_keys)] if vars(:ssh_keys) + ssh_opts[:kex] = vars(:ssh_kex).split(/,\s*/) if vars(:ssh_kex) ssh_opts[:encryption] = vars(:ssh_encryption).split(/,\s*/) if vars(:ssh_encryption) Oxidized.logger.debug "lib/oxidized/input/ssh.rb: Connecting to #{@node.name}" @@ -42,7 +44,7 @@ module Oxidized unless @exec shell_open @ssh begin - @username ? shell_login : expect(@node.prompt) + login rescue Timeout::Error raise PromptUndetect, [ @output, 'not matching configured prompt', @node.prompt ].join(' ') end @@ -71,6 +73,10 @@ module Oxidized @output end + def pty_options hash + @pty_options = @pty_options.merge hash + end + private def disconnect @@ -93,7 +99,7 @@ module Oxidized @output << data @output = @node.model.expects @output end - ch.request_pty (_opts={:term=>'vt100'}) do |_ch, success_pty| + ch.request_pty (@pty_options) do |_ch, success_pty| raise NoShell, "Can't get PTY" unless success_pty ch.send_channel_request 'shell' do |_ch, success_shell| raise NoShell, "Can't get shell" unless success_shell @@ -102,13 +108,18 @@ module Oxidized end end - # Cisco WCS has extremely dubious SSH implementation, SSH auth is always - # success, it always opens shell and then run auth in shell. I guess - # they'll never support exec() :) - def shell_login - expect username - cmd @node.auth[:username], password - cmd @node.auth[:password] + # some models have SSH auth or terminal auth based on version of code + # if SSH is configured for terminal auth, we'll still try to detect prompt + def login + if @username + match = expect username, @node.prompt + if match == username + cmd @node.auth[:username], password + cmd @node.auth[:password] + end + else + expect @node.prompt + end end def exec state=nil @@ -123,14 +134,18 @@ module Oxidized @output end - def expect regexp - Oxidized.logger.debug "lib/oxidized/input/ssh.rb: expecting #{regexp.inspect} at #{node.name}" + def expect *regexps + regexps = [regexps].flatten + Oxidized.logger.debug "lib/oxidized/input/ssh.rb: expecting #{regexps.inspect} at #{node.name}" Timeout::timeout(Oxidized.config.timeout) do @ssh.loop(0.1) do sleep 0.1 - not @output.match regexp + match = regexps.find { |regexp| @output.match regexp } + return match if match + true end end end + end end diff --git a/lib/oxidized/input/tftp.rb b/lib/oxidized/input/tftp.rb new file mode 100644 index 0000000..78164d0 --- /dev/null +++ b/lib/oxidized/input/tftp.rb @@ -0,0 +1,41 @@ +module Oxidized + require 'stringio' + require_relative 'cli' + + begin + require 'net/tftp' + rescue LoadError + raise OxidizedError, 'net/tftp not found: sudo gem install net-tftp' + end + + class TFTP < Input + + include Input::CLI + + # TFTP utilizes UDP, there is not a connection. We simply specify an IP and send/receive data. + def connect node + @node = node + + @node.model.cfg['tftp'].each { |cb| instance_exec(&cb) } + @log = File.open(Oxidized::Config::Log + "/#{@node.ip}-tftp", 'w') if Oxidized.config.input.debug? + @tftp = Net::TFTP.new @node.ip + end + + def cmd file + Oxidized.logger.debug "TFTP: #{file} @ #{@node.name}" + config = StringIO.new + @tftp.getbinary file, config + config.rewind + config.read + end + + private + + def disconnect + # TFTP uses UDP, there is no connection to close + ensure + @log.close if Oxidized.config.input.debug? + end + + end +end diff --git a/lib/oxidized/model/acos.rb b/lib/oxidized/model/acos.rb index bb9846e..47649a2 100644 --- a/lib/oxidized/model/acos.rb +++ b/lib/oxidized/model/acos.rb @@ -6,6 +6,13 @@ class ACOS < Oxidized::Model ##ACOS prompt changes depending on the state of the device prompt /^([-\w.\/:?\[\]\(\)]+[#>]\s?)$/ + cmd :secret do |cfg| + cfg.gsub!(/community read encrypted (\S+)/, 'community read encrypted <hidden>') # snmp + cfg.gsub!(/secret encrypted (\S+)/, 'secret encrypted <hidden>') # tacacs-server + cfg.gsub!(/password encrypted (\S+)/, 'password encrypted <hidden>') # user + cfg + end + cmd 'show version' do |cfg| cfg.gsub! /\s(Last configuration saved at).*/, ' \\1 <removed>' cfg.gsub! /\s(Memory).*/, ' \\1 <removed>' @@ -22,11 +29,20 @@ class ACOS < Oxidized::Model comment cfg end + cmd 'show partition-config all' do |cfg| + cfg.gsub! /(Current configuration).*/, '\\1 <removed>' + cfg.gsub! /(Configuration last updated at).*/, '\\1 <removed>' + cfg.gsub! /(Configuration last saved at).*/, '\\1 <removed>' + cfg.gsub! /(Configuration last synchronized at).*/, '\\1 <removed>' + cfg + end + cmd 'show running-config all-partitions' do |cfg| cfg.gsub! /(Current configuration).*/, '\\1 <removed>' cfg.gsub! /(Configuration last updated at).*/, '\\1 <removed>' cfg.gsub! /(Configuration last saved at).*/, '\\1 <removed>' cfg.gsub! /(Configuration last synchronized at).*/, '\\1 <removed>' + cfg end cmd 'show aflex all-partitions' do |cfg| diff --git a/lib/oxidized/model/alvarion.rb b/lib/oxidized/model/alvarion.rb new file mode 100644 index 0000000..3c762de --- /dev/null +++ b/lib/oxidized/model/alvarion.rb @@ -0,0 +1,13 @@ +class Alvarion < Oxidized::Model + + # Used in Alvarion wisp equipment + + # Run this command as an instance of Model so we can access node + pre do + cmd "#{node.auth[:password]}.cfg" + end + + + cfg :tftp {} + +end diff --git a/lib/oxidized/model/aosw.rb b/lib/oxidized/model/aosw.rb index 394561f..11d8442 100644 --- a/lib/oxidized/model/aosw.rb +++ b/lib/oxidized/model/aosw.rb @@ -28,19 +28,21 @@ class AOSW < Oxidized::Model cmd 'show version' do |cfg| cfg = cfg.each_line.select { |line| not line.match /Switch uptime/i } - comment cfg.join + rstrip_cfg comment cfg.join end cmd 'show inventory' do |cfg| - clean cfg + rstrip_cfg clean cfg end cmd 'show slots' do |cfg| - comment cfg + rstrip_cfg comment cfg end + cmd 'show license' do |cfg| - comment cfg + rstrip_cfg comment cfg end + cmd 'show running-config' do |cfg| out = [] cfg.each_line do |line| @@ -60,8 +62,8 @@ class AOSW < Oxidized::Model cfg :telnet, :ssh do if vars :enable post_login do - send 'enable\n' - send vars(:enable) + '\n' + send "enable\n" + cmd vars(:enable) end end post_login 'no paging' @@ -72,6 +74,15 @@ class AOSW < Oxidized::Model pre_logout 'exit' end + def rstrip_cfg cfg + out = [] + cfg.each_line do |line| + out << line.rstrip + end + out = out.join "\n" + out << "\n" + end + def clean cfg out = [] cfg.each_line do |line| diff --git a/lib/oxidized/model/apc_aos.rb b/lib/oxidized/model/apc_aos.rb new file mode 100644 index 0000000..530d436 --- /dev/null +++ b/lib/oxidized/model/apc_aos.rb @@ -0,0 +1,11 @@ +class Apc_aos < Oxidized::Model + + cmd 'config.ini' do |cfg| + cfg.gsub! /^; Configuration file\, generated on.*/, '' + end + + cfg :ftp do + end + +end + diff --git a/lib/oxidized/model/asa.rb b/lib/oxidized/model/asa.rb index a41348e..df30059 100644 --- a/lib/oxidized/model/asa.rb +++ b/lib/oxidized/model/asa.rb @@ -15,7 +15,7 @@ class ASA < Oxidized::Model cfg.gsub! /username (\S+) password (\S+) (.*)/, 'username \1 password <secret hidden> \3' cfg.gsub! /ikev2 pre-shared-key (\S+)/, 'ikev2 pre-shared-key <secret hidden>' cfg.gsub! /ikev2 (remote|local)-authentication pre-shared-key (\S+)/, 'ikev2 \1-authentication pre-shared-key <secret hidden>' - cfg.gsub! /^(aaa-server TACACS\+ \(\S+\) host.*\n\skey) \S+$/m, '\1 <secret hidden>' + cfg.gsub! /^(aaa-server TACACS\+? \(\S+\) host.*\n\skey) \S+$/mi, '\1 <secret hidden>' cfg end diff --git a/lib/oxidized/model/casa.rb b/lib/oxidized/model/casa.rb new file mode 100644 index 0000000..e85c904 --- /dev/null +++ b/lib/oxidized/model/casa.rb @@ -0,0 +1,46 @@ +class Casa < Oxidized::Model + # Casa Systems CMTS + + prompt /^([\w.@()-]+[#>]\s?)$/ + comment '! ' + + cmd :secret do |cfg| + cfg.gsub! /^(snmp community) \S+/, '\\1 <configuration removed>' + cfg.gsub! /^(snmp comm-tbl) \S+ \S+/, '\\1 <removed> <removed>' + cfg.gsub! /^(console-password encrypted) \S+/, '\\1 <secret hidden>' + cfg.gsub! /^(password encrypted) \S+/, '\\1 <secret hidden>' + cfg.gsub! /^(tacacs-server key) \S+/, '\\1 <secret hidden>' + cfg + end + + cmd :all do |cfg| + cfg.each_line.to_a[1..-2].join + end + + cmd 'show system' do |cfg| + comment cfg.each_line.reject { |line| line.match /^\s+System (Time|Uptime): / }.join + end + + cmd 'show version' do |cfg| + comment cfg + end + + cmd 'show run' + + cfg :telnet do + username /^Username:/ + password /^Password:/ + end + + cfg :telnet, :ssh do + post_login 'page-off' + # preferred way to handle additional passwords + if vars :enable + post_login do + send "enable\n" + cmd vars(:enable) + end + end + pre_logout 'logout' + end +end diff --git a/lib/oxidized/model/catos.rb b/lib/oxidized/model/catos.rb index 874ebbc..bac9eec 100644 --- a/lib/oxidized/model/catos.rb +++ b/lib/oxidized/model/catos.rb @@ -1,6 +1,6 @@ class Catos < Oxidized::Model - prompt /^[\w.@-]+> \(enable\) $/ + prompt /^[\w.@-]+>\s?(\(enable\) )?$/ comment '# ' cmd :all do |cfg| @@ -28,8 +28,15 @@ class Catos < Oxidized::Model password /^Password:/ end - cfg :ssh, :telnet do + cfg :telnet, :ssh do post_login 'set length 0' + # preferred way to handle additional passwords + if vars :enable + post_login do + send "enable\n" + cmd vars(:enable) + end + end pre_logout 'exit' end diff --git a/lib/oxidized/model/ciscosmb.rb b/lib/oxidized/model/ciscosmb.rb index 3ef9a85..e5501d5 100644 --- a/lib/oxidized/model/ciscosmb.rb +++ b/lib/oxidized/model/ciscosmb.rb @@ -33,14 +33,13 @@ class CiscoSMB < Oxidized::Model cfg end - cfg :telnet do - username /^User Name:/ - password /^\r?Password:$/ - end - cfg :telnet, :ssh do + username /^User ?[nN]ame:/ + password /^\r?Password:$/ post_login 'terminal datadump' # Disable pager post_login 'terminal width 0' + post_login 'terminal len 0' + pre_logout 'exit' #exit returns to previous priv level, no way to quit from exec(#) pre_logout 'exit' end diff --git a/lib/oxidized/model/dlink.rb b/lib/oxidized/model/dlink.rb new file mode 100644 index 0000000..5756bad --- /dev/null +++ b/lib/oxidized/model/dlink.rb @@ -0,0 +1,36 @@ +class Dlink < Oxidized::Model + # D-LINK Switches + + prompt /^(\r*[\w.@():-]+[#>]\s?)$/ + comment '# ' + + cmd :secret do |cfg| + cfg.gsub! /^(create snmp community) \S+/, '\\1 <removed>' + cfg.gsub! /^(create snmp group) \S+/, '\\1 <removed>' + cfg + end + + cmd :all do |cfg| + cfg.each_line.to_a[2..-2].map{|line|line.delete("\r").rstrip}.join("\n") + "\n" + end + + cmd 'show switch' do |cfg| + comment cfg + end + + cmd 'show vlan' do |cfg| + comment cfg + end + + cmd 'show config current' + + cfg :telnet do + username /\r*username:/ + password /\r*password:/ + end + + cfg :telnet, :ssh do + post_login 'disable clipaging' + pre_logout 'logout' + end +end diff --git a/lib/oxidized/model/dnos.rb b/lib/oxidized/model/dnos.rb index 1c31aad..a44630e 100644 --- a/lib/oxidized/model/dnos.rb +++ b/lib/oxidized/model/dnos.rb @@ -33,15 +33,16 @@ class DNOS < Oxidized::Model end cfg :telnet, :ssh do - post_login 'terminal length 0' - post_login 'terminal width 0' if vars :enable post_login do send "enable\n" - send vars(:enable) + "\n" + cmd vars(:enable) end end + post_login 'terminal length 0' + post_login 'terminal width 0' pre_logout 'exit' + pre_logout 'exit' end end diff --git a/lib/oxidized/model/eos.rb b/lib/oxidized/model/eos.rb index 75da0fa..a9f3ff3 100644 --- a/lib/oxidized/model/eos.rb +++ b/lib/oxidized/model/eos.rb @@ -22,7 +22,7 @@ class EOS < Oxidized::Model comment cfg end - cmd 'show running-config | no-more' do |cfg| + cmd 'show running-config | no-more | exclude ! Time:' do |cfg| cfg end diff --git a/lib/oxidized/model/fiberdriver.rb b/lib/oxidized/model/fiberdriver.rb new file mode 100644 index 0000000..8f8eb07 --- /dev/null +++ b/lib/oxidized/model/fiberdriver.rb @@ -0,0 +1,21 @@ +class FiberDriver < Oxidized::Model + prompt /\w+#/ + comment "! " + + cmd :all do |cfg| + cfg.each_line.to_a[1..-2].join + end + cmd 'show inventory' do |cfg| + comment cfg + end + + cmd "show running-config" do |cfg| + cfg.each_line.to_a[3..-1].join + end + + cfg :ssh do + post_login 'terminal length 0' + post_login 'terminal width 512' + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/fujitsupy.rb b/lib/oxidized/model/fujitsupy.rb new file mode 100644 index 0000000..20a78dd --- /dev/null +++ b/lib/oxidized/model/fujitsupy.rb @@ -0,0 +1,42 @@ +class FujitsuPY < Oxidized::Model + + prompt /^(\([\w.-]*\)\s#|^\S+\#\s)$/ + comment '! ' + + cmd :all do |cfg| + cfg.each_line.to_a[1..-2].join + end + +# 1Gbe switch + cmd 'show version' do |cfg| + cfg.gsub! /^(<ERROR> : 2 : format error)$/, '' + comment cfg + end + +# 10Gbe switch + cmd 'show system information' do |cfg| + cfg.gsub! /^Current-time : [\w\s:]*$/, '' + cfg.gsub! /^(\s{33}\^)$/, '' + cfg.gsub! /^(\% Invalid input detected at '\^' marker.)$/, '' + comment cfg + end + + cmd 'show running-config' do |cfg| + cfg + end + + cfg :telnet do + username /^Username:/ + password /^Password:/ + end + + cfg :telnet, :ssh do + post_login 'no pager' + post_login 'terminal pager disable' + pre_logout do + send "quit\n" + send "n\n" + end + end + +end diff --git a/lib/oxidized/model/hatteras.rb b/lib/oxidized/model/hatteras.rb new file mode 100644 index 0000000..4192cbc --- /dev/null +++ b/lib/oxidized/model/hatteras.rb @@ -0,0 +1,52 @@ +class Hatteras < Oxidized::Model + # Hatteras Networks + + prompt /^(\r?[\w.@()-]+[#>]\s?)$/ + comment '# ' + + expect /WARNING: System configuration changes will be lost when the device restarts./ do |data, re| + send "y\r" + data.sub re, '' + end + + + cmd :secret do |cfg| + cfg.gsub! /^(community) \S+/, '\\1 "<configuration removed>"' + cfg.gsub! /^(communityString) "\S+"/, '\\1 "<configuration removed>"' + cfg.gsub! /^(key) "\S+"/, '\\1 "<secret hidden>"' + cfg + end + + cmd :all do |cfg| + cfg.each_line.to_a[1..-2].join + end + + cmd "show switch\r" do |cfg| + cfg = cfg.each_line.reject { |line| line.match /Switch uptime|Switch temperature|Last reset reason/ or + line.match /TermCpuUtil|^\s+\^$|ERROR: Bad command/ }.join + comment cfg + end + + cmd "show card\r" do |cfg| + cfg = cfg.each_line.reject { |line| line.match /Card uptime|Card temperature|Last reset reason/ or + line.match /TermCpuUtil|^\s+\^$|ERROR: Bad command/ }.join + comment cfg + end + + cmd "show sfp *\r" do |cfg| + comment cfg + end + + cmd "show config run\r" do |cfg| + cfg + end + + cfg :telnet do + username /^Login:/ + password /^Password:/ + end + + cfg :telnet, :ssh do + pre_logout "logout\r" + end +end diff --git a/lib/oxidized/model/hpebladesystem.rb b/lib/oxidized/model/hpebladesystem.rb new file mode 100644 index 0000000..5e34de8 --- /dev/null +++ b/lib/oxidized/model/hpebladesystem.rb @@ -0,0 +1,83 @@ +class HPEBladeSystem < Oxidized::Model + # HPE Onboard Administrator + + prompt /.*> / + comment '# ' + + expect /^\s*--More--\s+.*$/ do |data, re| + send ' ' + data.sub re, '' + end + + cmd :all do |cfg| + cfg = cfg.delete("\r").each_line.to_a[0..-1].map{|line|line.rstrip}.join("\n") + "\n" + cfg.each_line.to_a[0..-2].join + end + + cmd :secret do |cfg| + cfg.gsub! /^(SET SNMP COMMUNITY (READ|WRITE)).*/, '\\1 <configuration removed>' + cfg + end + + cmd 'show oa info' do |cfg| + comment cfg + end + + cmd 'show oa network' do |cfg| + comment cfg + end + + cmd 'show oa certificate' do |cfg| + comment cfg + end + + cmd 'show sshfingerprint' do |cfg| + comment cfg + end + + cmd 'show fru' do |cfg| + comment cfg + end + + cmd 'show network' do |cfg| + comment cfg + end + + cmd 'show vlan' do |cfg| + comment cfg + end + + cmd 'show rack name' do |cfg| + comment cfg + end + + cmd 'show server list' do |cfg| + comment cfg + end + + cmd 'show server names' do |cfg| + comment cfg + end + + cmd 'show server port map all' do |cfg| + comment cfg + end + + cmd 'show server info all' do |cfg| + comment cfg + end + + cmd 'show config' do |cfg| + cfg.gsub! /^#(Generated on:) .*$/, '\\1 <removed>' + cfg.gsub /^\s+/, '' + end + + cfg :telnet do + username /\slogin:/ + password /^Password: / + end + + cfg :telnet, :ssh do + pre_logout "exit" + end +end diff --git a/lib/oxidized/model/ios.rb b/lib/oxidized/model/ios.rb index 1f099c8..3cbe0f0 100644 --- a/lib/oxidized/model/ios.rb +++ b/lib/oxidized/model/ios.rb @@ -26,7 +26,9 @@ class IOS < Oxidized::Model cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' cfg.gsub! /username (\S+) privilege (\d+) (\S+).*/, '<secret hidden>' cfg.gsub! /^username \S+ password \d \S+/, '<secret hidden>' + cfg.gsub! /^username \S+ secret \d \S+/, '<secret hidden>' cfg.gsub! /^enable password \d \S+/, '<secret hidden>' + cfg.gsub! /^enable secret \d \S+/, '<secret hidden>' cfg.gsub! /wpa-psk ascii \d \S+/, '<secret hidden>' cfg.gsub! /^tacacs-server key \d \S+/, '<secret hidden>' cfg diff --git a/lib/oxidized/model/ironware.rb b/lib/oxidized/model/ironware.rb index 1e8c30e..db341d1 100644 --- a/lib/oxidized/model/ironware.rb +++ b/lib/oxidized/model/ironware.rb @@ -2,14 +2,14 @@ class IronWare < Oxidized::Model prompt /^.*(telnet|ssh)\@.+[>#]\s?$/i comment '! ' - + #to handle pager without enable #expect /^((.*)--More--(.*))$/ do |data, re| # send ' ' # data.sub re, '' #end - + #to remove backspace (if handle pager without enable) #expect /^((.*)[\b](.*))$/ do |data, re| # data.sub re, '' @@ -44,14 +44,14 @@ class IronWare < Oxidized::Model out << sc.rest cfg = out end - + comment cfg end - + cmd 'show flash' do |cfg| comment cfg end - + cmd 'show module' do |cfg| cfg.gsub! /^((Invalid input)|(Type \?)).*$/, '' # some ironware devices are fixed config comment cfg @@ -74,7 +74,7 @@ class IronWare < Oxidized::Model if vars :enable post_login do send "enable\r\n" - send vars(:enable) + "\r\n" + cmd vars(:enable) end end post_login '' diff --git a/lib/oxidized/model/pfsense.rb b/lib/oxidized/model/pfsense.rb index cd6885c..c02c0d0 100644 --- a/lib/oxidized/model/pfsense.rb +++ b/lib/oxidized/model/pfsense.rb @@ -1,20 +1,14 @@ class PfSense < Oxidized::Model - - comment '# ' - - #add a comment in the final conf - def add_comment comment - "\n###### #{comment} ######\n" - end + # use other use than 'admin' user, 'admin' user cannot get ssh/exec. See issue #535 + cmd :all do |cfg| cfg.each_line.to_a[1..-2].join end - #show the persistent configuration - pre do - cfg = add_comment 'Configuration' - cfg += cmd 'cat /cf/conf/config.xml' + cmd 'cat /cf/conf/config.xml' do |cfg| + cfg.gsub! /\s<revision>\s*.*\s*<time>\d*<\/time>\s*.*\s*<\/revision>/, '' + cfg end cfg :ssh do diff --git a/lib/oxidized/model/planet.rb b/lib/oxidized/model/planet.rb new file mode 100644 index 0000000..05a369a --- /dev/null +++ b/lib/oxidized/model/planet.rb @@ -0,0 +1,83 @@ +class Planet < Oxidized::Model + + prompt /^\r?([\w.@()-]+[#>]\s?)$/ + comment '! ' + + # example how to handle pager + #expect /^\s--More--\s+.*$/ do |data, re| + # send ' ' + # data.sub re, '' + #end + + # non-preferred way to handle additional PW prompt + #expect /^[\w.]+>$/ do |data| + # send "enable\n" + # send vars(:enable) + "\n" + # data + #end + + cmd :all do |cfg| + #cfg.gsub! /\cH+\s{8}/, '' # example how to handle pager + #cfg.gsub! /\cH+/, '' # example how to handle pager + cfg.each_line.to_a[1..-2].join + end + + cmd :secret do |cfg| + cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' + cfg.gsub! /username (\S+) privilege (\d+) (\S+).*/, '<secret hidden>' + cfg.gsub! /^username \S+ password \d \S+/, '<secret hidden>' + cfg.gsub! /^enable password \d \S+/, '<secret hidden>' + cfg.gsub! /wpa-psk ascii \d \S+/, '<secret hidden>' + cfg.gsub! /^tacacs-server key \d \S+/, '<secret hidden>' + cfg + end + + cmd 'show version' do |cfg| + cfg.gsub! "\n\r", "\n" + @planetgs = true if cfg.match /^System Name\w*:\w*GS-.*$/ + @planetsgs = true if cfg.match /SGS-(.*) Device, Compiled on .*$/ + + cfg = cfg.each_line.to_a[0...-2] + + # Strip system time and system uptime from planet gs switches + cfg = cfg.reject { |line| line.match /System Time\s*:.*/ } + cfg = cfg.reject { |line| line.match /System Uptime\s*:.*/ } + + comment cfg.join + end + + + cmd 'show running-config' do |cfg| + cfg.gsub! "\n\r", "\n" + cfg = cfg.each_line.to_a + + cfg = cfg.reject { |line| line.match "Building configuration..." } + + if @planetsgs + cfg << cmd('show transceiver detail | include transceiver detail information|found|Type|length|Nominal|wavelength|Base information') do |cfg| + comment cfg + end + end + + cfg.join + end + + + cfg :telnet do + username /^Username:/ + password /^Password:/ + end + + cfg :telnet, :ssh do + post_login 'terminal length 0' + # preferred way to handle additional passwords + if vars :enable + post_login do + send "enable\n" + cmd vars(:enable) + end + end + pre_logout 'exit' + end + +end diff --git a/lib/oxidized/model/powerconnect.rb b/lib/oxidized/model/powerconnect.rb index f0fa3df..ac36c26 100644 --- a/lib/oxidized/model/powerconnect.rb +++ b/lib/oxidized/model/powerconnect.rb @@ -39,7 +39,7 @@ class PowerConnect < Oxidized::Model if vars :enable post_login do send "enable\n" - send vars(:enable) + "\n" + cmd vars(:enable) end end diff --git a/lib/oxidized/model/procurve.rb b/lib/oxidized/model/procurve.rb index da792e6..c117df3 100644 --- a/lib/oxidized/model/procurve.rb +++ b/lib/oxidized/model/procurve.rb @@ -57,4 +57,8 @@ class Procurve < Oxidized::Model pre_logout "logout\ny\nn" end + cfg :ssh do + pty_options({ chars_wide: 1000 }) + end + end diff --git a/lib/oxidized/model/routeros.rb b/lib/oxidized/model/routeros.rb index 4822500..a92ad5e 100644 --- a/lib/oxidized/model/routeros.rb +++ b/lib/oxidized/model/routeros.rb @@ -1,5 +1,5 @@ class RouterOS < Oxidized::Model - prompt /\[\w+@\S+\]\s?>\s?$/ + prompt /\[\w+@\S+(\s?\S+)*\]\s?>\s?$/ comment "# " cmd '/system routerboard print' do |cfg| @@ -8,6 +8,7 @@ class RouterOS < Oxidized::Model cmd '/export' do |cfg| cfg.gsub! /\x1B\[([0-9]{1,3}((;[0-9]{1,3})*)?)?[m|K]/, '' # strip ANSI colours + cfg.gsub! /\\\r\n\s+/, '' # strip new line cfg = cfg.split("\n").select { |line| not line[/^\#\s\w{3}\/\d{2}\/\d{4}.*$/] } cfg.join("\n") + "\n" end diff --git a/lib/oxidized/model/trango.rb b/lib/oxidized/model/trango.rb new file mode 100644 index 0000000..b2aa1e7 --- /dev/null +++ b/lib/oxidized/model/trango.rb @@ -0,0 +1,62 @@ +class Trango < Oxidized::Model + # take a Trangolink sysinfo output and turn it into a configuration file + + prompt /^#>\s?/ + comment '# ' + + cmd 'sysinfo' do |cfg| + out = [] + comments = [] + cfg.each_line do |line| + if line.match /\[Opmode\] (off|on) \[Default Opmode\] (off|on)/ + out << "opmode " + Regexp.last_match[1] + out << "defaultopmode " + Regexp.last_match[2] + end + if line.match /\[Tx Power\] ([\-\d]+) dBm/ + out << "power " + Regexp.last_match[1] + end + if line.match /\[Active Channel\] (\d+) (v|h)/ + out << "freq " + Regexp.last_match[1] + ' ' + Regexp.last_match[2] + end + if line.match /\[Peer ID\] ([A-F0-9]+)/ + out << "peerid " + Regexp.last_match[1] + end + if line.match /\[Unit Type\] (\S+)/ + out << "utype " + Regexp.last_match[1] + end + if line.match /\[(Hardware Version|Firmware Version|Model|S\/N)\] (\S+)/ + comments << '# ' + Regexp.last_match[1] + ': ' + Regexp.last_match[2] + end + if line.match /\[Remarks\] (\S+)/ + out << "remarks " + Regexp.last_match[1] + end + if line.match /\[RSSI LED\] (on|off)/ + out << "rssiled " + Regexp.last_match[1] + end + if line.match /\[Speed\] (\d+) Mbps/ + speed = Regexp.last_match[1] + end + if line.match /\[Tx MIR\] (\d+) Kbps/ + out << "mir ".concat(Regexp.last_match[1]) + end + if line.match /\[Auto Rate Shift\] (on|off)/ + out << "autorateshift ".concat(Regexp.last_match[1]) + if Regexp.last_match[1].eql? 'off' + out << "speed $speed" + end + end + if line.match /\[IP\] (\S+) \[Subnet Mask\] (\S+) \[Gateway\] (\S+)/ + out << "ipconfig " + Regexp.last_match[1] + ' ' + + Regexp.last_match[2] + ' ' + + Regexp.last_match[3] + end + end + comments.push(*out).join "\n" + end + + cfg :telnet do + password /Password:/ + pre_logout 'exit' + end + +end diff --git a/lib/oxidized/node.rb b/lib/oxidized/node.rb index f2b125a..b13ce0e 100644 --- a/lib/oxidized/node.rb +++ b/lib/oxidized/node.rb @@ -128,24 +128,15 @@ module Oxidized end def resolve_auth opt - # Resolve configured username/password, give priority to group level configuration - # TODO: refactor to use revised behaviour of Asetus - cfg_username, cfg_password = - if Oxidized.config.groups.has_key?(@group) and ['username', 'password'].all? {|e| Oxidized.config.groups[@group].has_key?(e)} - [Oxidized.config.groups[@group].username, Oxidized.config.groups[@group].password] - elsif ['username', 'password'].all? {|e| Oxidized.config.has_key?(e)} - [Oxidized.config.username, Oxidized.config.password] - else - [nil, nil] - end - auth = {} - auth[:username] = (opt[:username] or cfg_username) - auth[:password] = (opt[:password] or cfg_password) - auth + # Resolve configured username/password + { + username: resolve_key(:username, opt), + password: resolve_key(:password, opt), + } end def resolve_input opt - inputs = (opt[:input] or Oxidized.config.input.default) + inputs = resolve_key :input, opt, Oxidized.config.input.default inputs.split(/\s*,\s*/).map do |input| if not Oxidized.mgr.input[input] Oxidized.mgr.add_input input or raise MethodNotFound, "#{input} not found for node #{ip}" @@ -155,7 +146,7 @@ module Oxidized end def resolve_output opt - output = (opt[:output] or Oxidized.config.output.default) + output = resolve_key :output, opt, Oxidized.config.output.default if not Oxidized.mgr.output[output] Oxidized.mgr.add_output output or raise MethodNotFound, "#{output} not found for node #{ip}" end @@ -163,7 +154,7 @@ module Oxidized end def resolve_model opt - model = (opt[:model] or Oxidized.config.model) + model = resolve_key :model, opt if not Oxidized.mgr.model[model] Oxidized.logger.debug "lib/oxidized/node.rb: Loading model #{model.inspect}" Oxidized.mgr.add_model model or raise ModelNotFound, "#{model} not found for node #{ip}" @@ -187,6 +178,33 @@ module Oxidized end end + def resolve_key key, opt, global=nil + # resolve key, first get global, then get group then get node config + key_sym = key.to_sym + key_str = key.to_s + value = global + Oxidized.logger.debug "node.rb: resolving node key '#{key}', with passed global value of '#{value}' and node value '#{opt[key_sym]}'" + + #global + if not value and Oxidized.config.has_key?(key_str) + value = Oxidized.config[key_str] + Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from global" + end + + #group + if Oxidized.config.groups.has_key?(@group) + if Oxidized.config.groups[@group].has_key?(key_str) + value = Oxidized.config.groups[@group][key_str] + Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from group" + end + end + + #node + value = opt[key_sym] || value + Oxidized.logger.debug "node.rb: returning node key '#{key}' with value '#{value}'" + value + end + def is_git? opt (opt[:output] || Oxidized.config.output.default) == 'git' end diff --git a/lib/oxidized/output/file.rb b/lib/oxidized/output/file.rb index bb13827..45f72e1 100644 --- a/lib/oxidized/output/file.rb +++ b/lib/oxidized/output/file.rb @@ -17,7 +17,7 @@ class OxidizedFile < Output end def store node, outputs, opt={} - file = @cfg.directory + file = File.expand_path @cfg.directory if opt[:group] file = File.join File.dirname(file), opt[:group] end @@ -28,18 +28,22 @@ class OxidizedFile < Output end def fetch node, group - cfg_dir = @cfg.directory + cfg_dir = File.expand_path @cfg.directory + node_name = node.name + if group # group is explicitly defined by user - IO.readlines File.join(cfg_dir, group, node) + cfg_dir = File.join File.dirname(cfg_dir), group + File.read File.join(cfg_dir, node_name) else - if File.exists? File.join(cfg_dir, node) # node configuration file is stored on base directory - IO.readlines File.join(cfg_dir, node) + if File.exists? File.join(cfg_dir, node_name) # node configuration file is stored on base directory + File.read File.join(cfg_dir, node_name) else - path = Dir.glob File.join(cfg_dir, '**', node) # fetch node in all groups - return nil if path[0].nil? - open(path[0], 'r').readlines + path = Dir.glob(File.join(File.dirname(cfg_dir), '**', node_name)).first # fetch node in all groups + File.read path end end + rescue Errno::ENOENT + return nil end def version node, group diff --git a/lib/oxidized/source/csv.rb b/lib/oxidized/source/csv.rb index a0ce848..d498e0b 100644 --- a/lib/oxidized/source/csv.rb +++ b/lib/oxidized/source/csv.rb @@ -20,18 +20,20 @@ class CSV < Source nodes = [] open(File.expand_path @cfg.file).each_line do |line| next if line.match(/^\s*#/) - data = line.chomp.split @cfg.delimiter + data = line.chomp.split(@cfg.delimiter, -1) next if data.empty? # map node parameters keys = {} @cfg.map.each do |key, position| - keys[key.to_sym] = data[position] + keys[key.to_sym] = node_var_interpolate data[position] end keys[:model] = map_model keys[:model] if keys.key? :model - # map node specific vars, empty value is considered as nil + # map node specific vars vars = {} - @cfg.vars_map.each { |key, position| vars[key.to_sym] = data[position].to_s.empty? ? nil : data[position] } + @cfg.vars_map.each do |key, position| + vars[key.to_sym] = node_var_interpolate data[position] + end keys[:vars] = vars unless vars.empty? nodes << keys diff --git a/lib/oxidized/source/http.rb b/lib/oxidized/source/http.rb index 93361a2..4fd388b 100644 --- a/lib/oxidized/source/http.rb +++ b/lib/oxidized/source/http.rb @@ -20,6 +20,7 @@ class HTTP < Source uri = URI.parse(@cfg.url) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true if uri.scheme == 'https' + http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @cfg.secure # map headers headers = {} @@ -39,13 +40,15 @@ class HTTP < Source # map node parameters keys = {} @cfg.map.each do |key, position| - keys[key.to_sym] = line[position] + keys[key.to_sym] = node_var_interpolate line[position] end keys[:model] = map_model keys[:model] if keys.key? :model - # map node specific vars, empty value is considered as nil + # map node specific vars vars = {} - @cfg.vars_map.each { |key, position| vars[key.to_sym] = line[position].to_s.empty? ? nil : line[position] } + @cfg.vars_map.each do |key, position| + vars[key.to_sym] = node_var_interpolate line[position] + end keys[:vars] = vars unless vars.empty? nodes << keys diff --git a/lib/oxidized/source/source.rb b/lib/oxidized/source/source.rb index 7862dd1..9b8bc94 100644 --- a/lib/oxidized/source/source.rb +++ b/lib/oxidized/source/source.rb @@ -1,11 +1,23 @@ module Oxidized class Source class NoConfig < OxidizedError; end + def initialize @map = (Oxidized.config.model_map or {}) end + def map_model model @map.has_key?(model) ? @map[model] : model end + + def node_var_interpolate var + case var + when "nil" then nil + when "false" then false + when "true" then true + else var + end + end + end end diff --git a/lib/oxidized/source/sql.rb b/lib/oxidized/source/sql.rb index fc1caa8..13fc39b 100644 --- a/lib/oxidized/source/sql.rb +++ b/lib/oxidized/source/sql.rb @@ -26,12 +26,14 @@ class SQL < Source query.each do |node| # map node parameters keys = {} - @cfg.map.each { |key, sql_column| keys[key.to_sym] = node[sql_column.to_sym] } + @cfg.map.each { |key, sql_column| keys[key.to_sym] = node_var_interpolate node[sql_column.to_sym] } keys[:model] = map_model keys[:model] if keys.key? :model # map node specific vars vars = {} - @cfg.vars_map.each { |key, sql_column| vars[key.to_sym] = node[sql_column.to_sym] } + @cfg.vars_map.each do |key, sql_column| + vars[key.to_sym] = node_var_interpolate node[sql_column.to_sym] + end keys[:vars] = vars unless vars.empty? nodes << keys diff --git a/lib/oxidized/version.rb b/lib/oxidized/version.rb index 54defae..073aae9 100644 --- a/lib/oxidized/version.rb +++ b/lib/oxidized/version.rb @@ -1,3 +1,3 @@ module Oxidized - VERSION = '0.16.3' + VERSION = '0.19.0' end |