diff options
| author | nertwork <webmaster@nertwork.com> | 2016-12-20 10:49:24 -0800 | 
|---|---|---|
| committer | nertwork <webmaster@nertwork.com> | 2016-12-20 10:49:24 -0800 | 
| commit | e0621bbb81daab0de9fccc031c3e875031c2b67b (patch) | |
| tree | 9b527895c0a07d216f67728526b0ed7f82a40d12 /lib/oxidized | |
| parent | bcdd40d552cbf5e32dafebf4e531d407eb85bc84 (diff) | |
| parent | 1466f0f635d1e014ea993179729306d3a9a8d381 (diff) | |
Merge remote-tracking branch 'upstream/master'
* upstream/master: (109 commits)
  delete secret password if it is called secret
  bump up version
  update changelogs
  Recursively search from one dir above specified
  Fix suggested by ytti for issue #610
  Remove trailing whitespace and enable prompt detection
  Update eos.rb
  exclude time from output
  New hook: awssns - Publish messages to AWS SNS topics
  Updated config options
  Added option to disable ssl verification checks for http source
  Update ciscosmb.rb
  Update ciscosmb.rb
  Update ciscosmb.rb
  expect prompt after entering enable password
  add support for PLANET SG switches
  renamed  alvarion -> alvarion.rb
  This adds support for Hatteras Networks devices
  This adds support for D-Link switches
  This adds support for the Casa C1G CMTS
  ...
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 | 
