diff options
Diffstat (limited to 'lib')
130 files changed, 3075 insertions, 1093 deletions
diff --git a/lib/oxidized/cli.rb b/lib/oxidized/cli.rb index 9a09d41..23cc28c 100644 --- a/lib/oxidized/cli.rb +++ b/lib/oxidized/cli.rb @@ -40,11 +40,11 @@ module Oxidized end def parse_opts - opts = Slop.new(:help=>true) do + opts = Slop.new(:help => true) do on 'd', 'debug', 'turn on debugging' on 'daemonize', 'Daemonize/fork the process' on 'v', 'version', 'show version' do - puts Oxidized::VERSION + puts Oxidized::VERSION_FULL Kernel.exit end end @@ -62,7 +62,7 @@ module Oxidized def write_pid if pidfile? begin - File.open(pidfile, ::File::CREAT | ::File::EXCL | ::File::WRONLY){|f| f.write("#{Process.pid}") } + File.open(pidfile, ::File::CREAT | ::File::EXCL | ::File::WRONLY) { |f| f.write("#{Process.pid}") } at_exit { File.delete(pidfile) if File.exists?(pidfile) } rescue Errno::EEXIST check_pid diff --git a/lib/oxidized/config.rb b/lib/oxidized/config.rb index a92cea6..0a8fdfd 100644 --- a/lib/oxidized/config.rb +++ b/lib/oxidized/config.rb @@ -13,7 +13,7 @@ module Oxidized HookDir = File.join Directory, %w(lib oxidized hook) Sleep = 1 - def self.load(cmd_opts={}) + def self.load(cmd_opts = {}) asetus = Asetus.new(name: 'oxidized', load: false, key_to_s: true) Oxidized.asetus = asetus @@ -29,13 +29,17 @@ module Oxidized asetus.default.retries = 3 asetus.default.prompt = /^([\w.@-]+[#>]\s?)$/ asetus.default.rest = '127.0.0.1:8888' # or false to disable - asetus.default.vars = {} # could be 'enable'=>'enablePW' - asetus.default.groups = {} # group level configuration + asetus.default.next_adds_job = false # if true, /next adds job, so device is fetched immmeiately + asetus.default.vars = {} # could be 'enable'=>'enablePW' + asetus.default.groups = {} # group level configuration + asetus.default.models = {} # model level configuration asetus.default.pid = File.join(Oxidized::Config::Root, 'pid') - asetus.default.input.default = 'ssh, telnet' - asetus.default.input.debug = false # or String for session log file - asetus.default.input.ssh.secure = false # complain about changed certs + asetus.default.input.default = 'ssh, telnet' + asetus.default.input.debug = false # or String for session log file + asetus.default.input.ssh.secure = false # complain about changed certs + asetus.default.input.ftp.passive = true # ftp passive mode + asetus.default.input.utf8_encoded = true # configuration is utf8 encoded or ascii-8bit asetus.default.output.default = 'file' # file, git asetus.default.source.default = 'csv' # csv, sql diff --git a/lib/oxidized/config/vars.rb b/lib/oxidized/config/vars.rb index 09f9781..d926ee2 100644 --- a/lib/oxidized/config/vars.rb +++ b/lib/oxidized/config/vars.rb @@ -8,8 +8,12 @@ module Oxidized::Config::Vars r ||= Oxidized.config.groups[@node.group].vars[name.to_s] end end + if Oxidized.config.models.has_key?(@node.model.class.name.to_s.downcase) + if Oxidized.config.models[@node.model.class.name.to_s.downcase].vars.has_key?(name.to_s) + r ||= Oxidized.config.models[@node.model.class.name.to_s.downcase].vars[name.to_s] + end + end r ||= Oxidized.config.vars[name.to_s] if Oxidized.config.vars.has_key?(name.to_s) r end end - diff --git a/lib/oxidized/core.rb b/lib/oxidized/core.rb index e007c9d..440d8e2 100644 --- a/lib/oxidized/core.rb +++ b/lib/oxidized/core.rb @@ -11,9 +11,9 @@ module Oxidized def initialize args Oxidized.mgr = Manager.new Oxidized.Hooks = HookManager.from_config(Oxidized.config) - nodes = Nodes.new + nodes = Nodes.new raise NoNodesFound, 'source returns no usable nodes' if nodes.size == 0 - @worker = Worker.new nodes + @worker = Worker.new nodes trap('HUP') { nodes.load } if Oxidized.config.rest? begin @@ -22,7 +22,7 @@ module Oxidized raise OxidizedError, 'oxidized-web not found: sudo gem install oxidized-web - \ or disable web support by setting "rest: false" in your configuration' end - @rest = API::Web.new nodes, Oxidized.config.rest + @rest = API::Web.new nodes, Oxidized.config.rest @rest.run end run diff --git a/lib/oxidized/hook.rb b/lib/oxidized/hook.rb index 029688d..915299b 100644 --- a/lib/oxidized/hook.rb +++ b/lib/oxidized/hook.rb @@ -1,88 +1,88 @@ module Oxidized -class HookManager - class << self - def from_config cfg - mgr = new - cfg.hooks.each do |name,h_cfg| - h_cfg.events.each do |event| - mgr.register event.to_sym, name, h_cfg.type, h_cfg + class HookManager + class << self + def from_config cfg + mgr = new + cfg.hooks.each do |name, h_cfg| + h_cfg.events.each do |event| + mgr.register event.to_sym, name, h_cfg.type, h_cfg + end end + mgr end - mgr end - end - - # HookContext is passed to each hook. It can contain anything related to the - # event in question. At least it contains the event name - class HookContext < OpenStruct; end - # RegisteredHook is a container for a Hook instance - class RegisteredHook < Struct.new(:name, :hook); end + # HookContext is passed to each hook. It can contain anything related to the + # event in question. At least it contains the event name + class HookContext < OpenStruct; end - Events = [ - :node_success, - :node_fail, - :post_store, - ] - attr_reader :registered_hooks + # RegisteredHook is a container for a Hook instance + class RegisteredHook < Struct.new(:name, :hook); end - def initialize - @registered_hooks = Hash.new {|h,k| h[k] = []} - end + Events = [ + :node_success, + :node_fail, + :post_store, + :nodes_done + ] + attr_reader :registered_hooks - def register event, name, hook_type, cfg - unless Events.include? event - raise ArgumentError, - "unknown event #{event}, available: #{Events.join ','}" + def initialize + @registered_hooks = Hash.new { |h, k| h[k] = [] } end - Oxidized.mgr.add_hook hook_type - begin - hook = Oxidized.mgr.hook.fetch(hook_type).new - rescue KeyError - raise KeyError, "cannot find hook #{hook_type.inspect}" - end + def register event, name, hook_type, cfg + unless Events.include? event + raise ArgumentError, + "unknown event #{event}, available: #{Events.join ','}" + end - hook.cfg = cfg + Oxidized.mgr.add_hook hook_type + begin + hook = Oxidized.mgr.hook.fetch(hook_type).new + rescue KeyError + raise KeyError, "cannot find hook #{hook_type.inspect}" + end - @registered_hooks[event] << RegisteredHook.new(name, hook) - Oxidized.logger.debug "Hook #{name.inspect} registered #{hook.class} for event #{event.inspect}" - end + hook.cfg = cfg - def handle event, ctx_params={} - ctx = HookContext.new ctx_params - ctx.event = event + @registered_hooks[event] << RegisteredHook.new(name, hook) + Oxidized.logger.debug "Hook #{name.inspect} registered #{hook.class} for event #{event.inspect}" + end - @registered_hooks[event].each do |r_hook| - begin - r_hook.hook.run_hook ctx - rescue => e - Oxidized.logger.error "Hook #{r_hook.name} (#{r_hook.hook}) failed " + - "(#{e.inspect}) for event #{event.inspect}" + def handle event, ctx_params = {} + ctx = HookContext.new ctx_params + ctx.event = event + + @registered_hooks[event].each do |r_hook| + begin + r_hook.hook.run_hook ctx + rescue => e + Oxidized.logger.error "Hook #{r_hook.name} (#{r_hook.hook}) failed " + + "(#{e.inspect}) for event #{event.inspect}" + end end end end -end -# Hook abstract base class -class Hook - attr_reader :cfg + # Hook abstract base class + class Hook + attr_reader :cfg - def initialize - end + def initialize + end - def cfg=(cfg) - @cfg = cfg - validate_cfg! if self.respond_to? :validate_cfg! - end + def cfg=(cfg) + @cfg = cfg + validate_cfg! if self.respond_to? :validate_cfg! + end - def run_hook ctx - raise NotImplementedError - end + def run_hook ctx + raise NotImplementedError + end - def log(msg, level=:info) - Oxidized.logger.send(level, "#{self.class.name}: #{msg}") + def log(msg, level = :info) + Oxidized.logger.send(level, "#{self.class.name}: #{msg}") + end end - -end end diff --git a/lib/oxidized/hook/awssns.rb b/lib/oxidized/hook/awssns.rb index dbc2d47..183cd2c 100644 --- a/lib/oxidized/hook/awssns.rb +++ b/lib/oxidized/hook/awssns.rb @@ -19,9 +19,8 @@ class AwsSns < Oxidized::Hook :node => ctx.node.name.to_s ) end - topic.publish({ + topic.publish( message: message.to_json - }) + ) end - end diff --git a/lib/oxidized/hook/ciscosparkdiff.rb b/lib/oxidized/hook/ciscosparkdiff.rb new file mode 100644 index 0000000..e45d7c6 --- /dev/null +++ b/lib/oxidized/hook/ciscosparkdiff.rb @@ -0,0 +1,49 @@ +require 'cisco_spark' + +# defaults to posting a diff, if messageformat is supplied them a message will be posted too +# diffenable defaults to true +# Modified from slackdiff + +class CiscoSparkDiff < Oxidized::Hook + def validate_cfg! + raise KeyError, 'hook.accesskey is required' unless cfg.has_key?('accesskey') + raise KeyError, 'hook.space is required' unless cfg.has_key?('space') + end + + def run_hook(ctx) + return unless ctx.node + return unless ctx.event.to_s == "post_store" + log "Connecting to Cisco Spark" + CiscoSpark.configure do |config| + config.api_key = cfg.accesskey + config.proxy = cfg.proxy if cfg.has_key?('proxy') + end + space = cfg.space + client = CiscoSpark::Room.new(id: space) + client.fetch + log "Connected" + diffenable = true + if cfg.has_key?('diff') == true + if cfg.diff == false + diffenable = false + end + end + if diffenable == true + gitoutput = ctx.node.output.new + diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil + title = ctx.node.name.to_s + log "Posting diff as snippet to #{cfg.space}" + message = CiscoSpark::Message.new(text: 'Device ' + title + ' modified:' + "\n" + diff[:patch].lines.to_a[4..-1].join) + room = CiscoSpark::Room.new(id: space) + room.send_message(message) + end + if cfg.has_key?('message') == true + log cfg.message + msg = cfg.message % { :node => ctx.node.name.to_s, :group => ctx.node.group.to_s, :commitref => ctx.commitref, :model => ctx.node.model.class.name.to_s.downcase } + log msg + log "Posting message to #{cfg.space}" + client.chat_postMessage(channel: cfg.channel, text: msg, as_user: true) + end + log "Finished" + end +end diff --git a/lib/oxidized/hook/exec.rb b/lib/oxidized/hook/exec.rb index 58d6fd5..069b888 100644 --- a/lib/oxidized/hook/exec.rb +++ b/lib/oxidized/hook/exec.rb @@ -23,10 +23,9 @@ class Exec < Oxidized::Hook @cmd = cfg.cmd raise "invalid cmd value" unless @cmd.is_a?(String) || @cmd.is_a?(Array) end - rescue RuntimeError => e raise ArgumentError, - "#{self.class.name}: configuration invalid: #{e.message}" + "#{self.class.name}: configuration invalid: #{e.message}" end def run_hook ctx @@ -45,7 +44,7 @@ class Exec < Oxidized::Hook def run_cmd! env pid, status = nil, nil Timeout.timeout(@timeout) do - pid = spawn env, @cmd , :unsetenv_others => true + pid = spawn env, @cmd, :unsetenv_others => true pid, status = wait2 pid unless status.exitstatus.zero? msg = "#{@cmd.inspect} failed with exit value #{status.exitstatus}" @@ -53,11 +52,11 @@ class Exec < Oxidized::Hook raise msg end end - rescue TimeoutError + rescue Timeout::Error kill "TERM", pid msg = "#{@cmd} timed out" log msg, :error - raise TimeoutError, msg + raise Timeout::Error, msg end def make_env ctx @@ -67,10 +66,11 @@ class Exec < Oxidized::Hook if ctx.node env.merge!( "OX_NODE_NAME" => ctx.node.name.to_s, + "OX_NODE_IP" => ctx.node.ip.to_s, "OX_NODE_FROM" => ctx.node.from.to_s, "OX_NODE_MSG" => ctx.node.msg.to_s, "OX_NODE_GROUP" => ctx.node.group.to_s, - "OX_EVENT" => ctx.event.to_s, + "OX_NODE_MODEL" => ctx.node.model.class.name, "OX_REPO_COMMITREF" => ctx.commitref.to_s, "OX_REPO_NAME" => ctx.node.repo.to_s, ) diff --git a/lib/oxidized/hook/githubrepo.rb b/lib/oxidized/hook/githubrepo.rb index d33e54e..e077d5d 100644 --- a/lib/oxidized/hook/githubrepo.rb +++ b/lib/oxidized/hook/githubrepo.rb @@ -35,26 +35,32 @@ class GithubRepo < Oxidized::Hook end Rugged::Commit.create(repo, { - parents: [repo.head.target, their_branch.target], - tree: merge_index.write_tree(repo), - message: "Merge remote-tracking branch '#{their_branch.name}'", - update_ref: "HEAD" - }) + parents: [repo.head.target, their_branch.target], + tree: merge_index.write_tree(repo), + message: "Merge remote-tracking branch '#{their_branch.name}'", + update_ref: "HEAD" + }) end private def credentials - @credentials ||= if cfg.has_key?('username') && cfg.has_key?('password') - log "Using https auth", :debug - Rugged::Credentials::UserPassword.new(username: cfg.username, password: cfg.password) - else - if cfg.has_key?('publickey') && cfg.has_key?('privatekey') - log "Using ssh auth with key", :debug - Rugged::Credentials::SshKey.new(username: 'git', publickey: File.expand_path(cfg.publickey), privatekey: File.expand_path(cfg.privatekey)) + Proc.new do |url, username_from_url, allowed_types| + if cfg.has_key?('username') + git_user = cfg.username + else + git_user = username_from_url ? username_from_url : 'git' + end + + if cfg.has_key?('password') + log "Authenticating using username and password as '#{git_user}'", :debug + Rugged::Credentials::UserPassword.new(username: git_user, password: cfg.password) + elsif cfg.has_key?('publickey') && cfg.has_key?('privatekey') + log "Authenticating using ssh keys as '#{git_user}'", :debug + Rugged::Credentials::SshKey.new(username: git_user, publickey: File.expand_path(cfg.publickey), privatekey: File.expand_path(cfg.privatekey), passphrase: ENV["OXIDIZED_SSH_PASSPHRASE"]) else - log "Using ssh auth with agentforwarding", :debug - Rugged::Credentials::SshKeyFromAgent.new(username: 'git') + log "Authenticating using ssh agent as '#{git_user}'", :debug + Rugged::Credentials::SshKeyFromAgent.new(username: git_user) end end end diff --git a/lib/oxidized/hook/slackdiff.rb b/lib/oxidized/hook/slackdiff.rb new file mode 100644 index 0000000..baaf291 --- /dev/null +++ b/lib/oxidized/hook/slackdiff.rb @@ -0,0 +1,53 @@ +require 'slack' + +# defaults to posting a diff, if messageformat is supplied them a message will be posted too +# diffenable defaults to true + +class SlackDiff < Oxidized::Hook + def validate_cfg! + raise KeyError, 'hook.token is required' unless cfg.has_key?('token') + raise KeyError, 'hook.channel is required' unless cfg.has_key?('channel') + end + + def run_hook(ctx) + return unless ctx.node + return unless ctx.event.to_s == "post_store" + log "Connecting to slack" + Slack.configure do |config| + config.token = cfg.token + config.proxy = cfg.proxy if cfg.has_key?('proxy') + end + client = Slack::Client.new + client.auth_test + log "Connected" + # diff snippet - default + diffenable = true + if cfg.has_key?('diff') == true + if cfg.diff == false + diffenable = false + end + end + if diffenable == true + gitoutput = ctx.node.output.new + diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil + unless diff == "no diffs" + title = "#{ctx.node.name} #{ctx.node.group} #{ctx.node.model.class.name.to_s.downcase}" + log "Posting diff as snippet to #{cfg.channel}" + client.files_upload(channels: cfg.channel, as_user: true, + content: diff[:patch].lines.to_a[4..-1].join, + filetype: "diff", + title: title, + filename: "change") + end + end + # message custom formatted - optional + if cfg.has_key?('message') == true + log cfg.message + msg = cfg.message % { :node => ctx.node.name.to_s, :group => ctx.node.group.to_s, :commitref => ctx.commitref, :model => ctx.node.model.class.name.to_s.downcase } + log msg + log "Posting message to #{cfg.channel}" + client.chat_postMessage(channel: cfg.channel, text: msg, as_user: true) + end + log "Finished" + end +end diff --git a/lib/oxidized/hook/xmppdiff.rb b/lib/oxidized/hook/xmppdiff.rb new file mode 100644 index 0000000..6acb172 --- /dev/null +++ b/lib/oxidized/hook/xmppdiff.rb @@ -0,0 +1,58 @@ +require 'xmpp4r' +require 'xmpp4r/muc/helper/simplemucclient' + +class XMPPDiff < Oxidized::Hook + def validate_cfg! + raise KeyError, 'hook.jid is required' unless cfg.has_key?('jid') + raise KeyError, 'hook.password is required' unless cfg.has_key?('password') + raise KeyError, 'hook.channel is required' unless cfg.has_key?('channel') + raise KeyError, 'hook.nick is required' unless cfg.has_key?('nick') + end + + def run_hook(ctx) + return unless ctx.node + return unless ctx.event.to_s == "post_store" + begin + Timeout.timeout(15) do + gitoutput = ctx.node.output.new + diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil + + interesting = diff[:patch].lines.to_a[4..-1].any? do |line| + ["+", "-"].include?(line[0]) and not ["#", "!"].include?(line[1]) + end + interesting &&= diff[:patch].lines.to_a[5..-1].any? { |line| line[0] == '-' } + interesting &&= diff[:patch].lines.to_a[5..-1].any? { |line| line[0] == '+' } + + if interesting + log "Connecting to XMPP" + client = Jabber::Client.new(Jabber::JID.new(cfg.jid)) + client.connect + sleep 1 + client.auth(cfg.password) + sleep 1 + + log "Connected" + + m = Jabber::MUC::SimpleMUCClient.new(client) + m.join(cfg.channel + "/" + cfg.nick) + + log "Joined" + + title = "#{ctx.node.name} #{ctx.node.group} #{ctx.node.model.class.name.to_s.downcase}" + log "Posting diff as snippet to #{cfg.channel}" + + m.say(title + "\n\n" + diff[:patch].lines.to_a[4..-1].join) + + sleep 1 + + client.close + + log "Finished" + + end + end + rescue Timeout::Error + log "timed out" + end + end +end diff --git a/lib/oxidized/input/cli.rb b/lib/oxidized/input/cli.rb index 660e173..d434e33 100644 --- a/lib/oxidized/input/cli.rb +++ b/lib/oxidized/input/cli.rb @@ -32,26 +32,25 @@ module Oxidized @pre_logout.each { |command, block| block ? block.call : (cmd command, nil) } end - def post_login _post_login=nil, &block + def post_login _post_login = nil, &block unless @exec @post_login << [_post_login, block] end end - def pre_logout _pre_logout=nil, &block + def pre_logout _pre_logout = nil, &block unless @exec - @pre_logout << [_pre_logout, block] + @pre_logout << [_pre_logout, block] end end - def username re=/^(Username|login)/ + def username re = /^(Username|login)/ @username or @username = re end - def password re=/^Password/ + def password re = /^Password/ @password or @password = re end - end end end diff --git a/lib/oxidized/input/ftp.rb b/lib/oxidized/input/ftp.rb index 80de257..ebe50ef 100644 --- a/lib/oxidized/input/ftp.rb +++ b/lib/oxidized/input/ftp.rb @@ -6,20 +6,22 @@ module Oxidized class FTP < Input RescueFail = { :debug => [ - #Net::SSH::Disconnect, + # Net::SSH::Disconnect, ], :warn => [ - #RuntimeError, - #Net::SSH::AuthenticationFailed, + # RuntimeError, + # Net::SSH::AuthenticationFailed, ], } include Input::CLI def connect node - @node = node + @node = node @node.model.cfg['ftp'].each { |cb| instance_exec(&cb) } @log = File.open(Oxidized::Config::Log + "/#{@node.ip}-ftp", 'w') if Oxidized.config.input.debug? - @ftp = Net::FTP.new @node.ip, @node.auth[:username], @node.auth[:password] + @ftp = Net::FTP.new(@node.ip) + @ftp.passive = Oxidized.config.input.ftp.passive + @ftp.login @node.auth[:username], @node.auth[:password] connected? end @@ -45,10 +47,9 @@ module Oxidized def disconnect @ftp.close - #rescue Errno::ECONNRESET, IOError + # rescue Errno::ECONNRESET, IOError ensure @log.close if Oxidized.config.input.debug? end - end end diff --git a/lib/oxidized/input/ssh.rb b/lib/oxidized/input/ssh.rb index 9a5c508..6e86d13 100644 --- a/lib/oxidized/input/ssh.rb +++ b/lib/oxidized/input/ssh.rb @@ -24,17 +24,27 @@ module Oxidized secure = Oxidized.config.input.ssh.secure @log = File.open(Oxidized::Config::Log + "/#{@node.ip}-ssh", 'w') if Oxidized.config.input.debug? port = vars(:ssh_port) || 22 - if proxy_host = vars(:ssh_proxy) - proxy = Net::SSH::Proxy::Command.new("ssh #{proxy_host} -W %h:%p") - end + ssh_opts = { - :port => port.to_i, - :password => @node.auth[:password], :timeout => Oxidized.config.timeout, - :paranoid => secure, - :auth_methods => %w(none publickey password keyboard-interactive), - :number_of_password_prompts => 0, - :proxy => proxy, + port: port.to_i, + paranoid: secure, + keepalive: true, + password: @node.auth[:password], :timeout => Oxidized.config.timeout, + number_of_password_prompts: 0, } + + auth_methods = vars(:auth_methods) || %w(none publickey password) + ssh_opts[:auth_methods] = auth_methods + Oxidized.logger.info "AUTH METHODS::#{auth_methods}" + + if proxy_host = vars(:ssh_proxy) + proxy_command = "ssh " + proxy_command += "-o StrictHostKeyChecking=no " unless secure + proxy_command += "#{proxy_host} -W %h:%p" + proxy = Net::SSH::Proxy::Command.new(proxy_command) + ssh_opts[:proxy] = proxy + end + 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) @@ -46,7 +56,7 @@ module Oxidized begin login rescue Timeout::Error - raise PromptUndetect, [ @output, 'not matching configured prompt', @node.prompt ].join(' ') + raise PromptUndetect, [@output, 'not matching configured prompt', @node.prompt].join(' ') end end connected? @@ -56,7 +66,7 @@ module Oxidized @ssh and not @ssh.closed? end - def cmd cmd, expect=node.prompt + def cmd cmd, expect = node.prompt Oxidized.logger.debug "lib/oxidized/input/ssh.rb #{cmd} @ #{node.name} with expect: #{expect.inspect}" if @exec @ssh.exec! cmd @@ -94,7 +104,7 @@ module Oxidized ch.on_data do |_ch, data| if Oxidized.config.input.debug? @log.print data - @log.fsync + @log.flush end @output << data @output = @node.model.expects @output @@ -122,8 +132,8 @@ module Oxidized end end - def exec state=nil - state == nil ? @exec : (@exec=state) unless vars :ssh_no_exec + def exec state = nil + state == nil ? @exec : (@exec = state) unless vars :ssh_no_exec end def cmd_shell(cmd, expect_re) @@ -146,6 +156,5 @@ module Oxidized end end end - end end diff --git a/lib/oxidized/input/telnet.rb b/lib/oxidized/input/telnet.rb index a5561b9..b1b3222 100644 --- a/lib/oxidized/input/telnet.rb +++ b/lib/oxidized/input/telnet.rb @@ -18,7 +18,7 @@ module Oxidized 'Model' => @node.model } opt['Output_log'] = Oxidized::Config::Log + "/#{@node.ip}-telnet" if Oxidized.config.input.debug? - @telnet = Net::Telnet.new opt + @telnet = Net::Telnet.new opt if @node.auth[:username] and @node.auth[:username].length > 0 expect username @telnet.puts @node.auth[:username] @@ -28,7 +28,7 @@ module Oxidized begin expect @node.prompt rescue Timeout::Error - raise PromptUndetect, [ 'unable to detect prompt:', @node.prompt ].join(' ') + raise PromptUndetect, ['unable to detect prompt:', @node.prompt].join(' ') end end @@ -36,7 +36,7 @@ module Oxidized @telnet and not @telnet.sock.closed? end - def cmd cmd, expect=@node.prompt + def cmd cmd, expect = @node.prompt Oxidized.logger.debug "Telnet: #{cmd} @#{@node.name}" args = { 'String' => cmd } args.merge!({ 'Match' => expect, 'Timeout' => @timeout }) if expect @@ -64,11 +64,9 @@ module Oxidized rescue Errno::ECONNRESET end end - end end - class Net::Telnet ## FIXME: we just need 'line = model.expects line' to handle pager ## how to do this, without redefining the whole damn thing @@ -86,7 +84,7 @@ class Net::Telnet elsif options.has_key?("Prompt") options["Prompt"] elsif options.has_key?("String") - Regexp.new( Regexp.quote(options["String"]) ) + Regexp.new(Regexp.quote(options["String"])) end time_out = options["Timeout"] if options.has_key?("Timeout") waittime = options["Waittime"] if options.has_key?("Waittime") @@ -102,9 +100,9 @@ class Net::Telnet line = '' buf = '' rest = '' - until(prompt === line and not IO::select([@sock], nil, nil, waittime)) + until prompt === line and not IO::select([@sock], nil, nil, waittime) unless IO::select([@sock], nil, nil, time_out) - raise TimeoutError, "timed out while waiting for more data" + raise Timeout::Error, "timed out while waiting for more data" end begin c = @sock.readpartial(1024 * 1024) @@ -114,30 +112,30 @@ class Net::Telnet c = rest + c if Integer(c.rindex(/#{IAC}#{SE}/no) || 0) < Integer(c.rindex(/#{IAC}#{SB}/no) || 0) - buf = preprocess(c[0 ... c.rindex(/#{IAC}#{SB}/no)]) - rest = c[c.rindex(/#{IAC}#{SB}/no) .. -1] + buf = preprocess(c[0...c.rindex(/#{IAC}#{SB}/no)]) + rest = c[c.rindex(/#{IAC}#{SB}/no)..-1] elsif pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) || c.rindex(/\r\z/no) - buf = preprocess(c[0 ... pt]) - rest = c[pt .. -1] + buf = preprocess(c[0...pt]) + rest = c[pt..-1] else buf = preprocess(c) rest = '' end - else - # Not Telnetmode. - # - # We cannot use preprocess() on this data, because that - # method makes some Telnetmode-specific assumptions. - buf = rest + c - rest = '' - unless @options["Binmode"] - if pt = buf.rindex(/\r\z/no) - buf = buf[0 ... pt] - rest = buf[pt .. -1] - end - buf.gsub!(/#{EOL}/no, "\n") - end + else + # Not Telnetmode. + # + # We cannot use preprocess() on this data, because that + # method makes some Telnetmode-specific assumptions. + buf = rest + c + rest = '' + unless @options["Binmode"] + if pt = buf.rindex(/\r\z/no) + buf = buf[0...pt] + rest = buf[pt..-1] + end + buf.gsub!(/#{EOL}/no, "\n") + end end @log.print(buf) if @options.has_key?("Output_log") line += buf diff --git a/lib/oxidized/jobs.rb b/lib/oxidized/jobs.rb index c566778..fdc1cbf 100644 --- a/lib/oxidized/jobs.rb +++ b/lib/oxidized/jobs.rb @@ -6,7 +6,7 @@ module Oxidized def initialize max, interval, nodes @max = max - # Set interval to 1 if interval is 0 (=disabled) so we don't break + # Set interval to 1 if interval is 0 (=disabled) so we don't break # the 'ceil' function @interval = interval == 0 ? 1 : interval @nodes = nodes @@ -28,7 +28,7 @@ module Oxidized @durations.fill AVERAGE_DURATION, @durations.size...@nodes.size end @durations.push(last).shift - @duration = @durations.inject(:+).to_f / @nodes.size #rolling average + @duration = @durations.inject(:+).to_f / @nodes.size # rolling average new_count end @@ -45,9 +45,8 @@ module Oxidized # and c) there is more than MAX_INTER_JOB_GAP since last one was started # then we want one more thread (rationale is to fix hanging thread causing HOLB) if @want <= size and @want < @nodes.size - @want +=1 if (Time.now.utc - @last) > MAX_INTER_JOB_GAP + @want += 1 if (Time.now.utc - @last) > MAX_INTER_JOB_GAP end end - end end diff --git a/lib/oxidized/manager.rb b/lib/oxidized/manager.rb index bf28ae7..c4523f3 100644 --- a/lib/oxidized/manager.rb +++ b/lib/oxidized/manager.rb @@ -7,11 +7,11 @@ module Oxidized class << self def load dir, file begin - require File.join dir, file+'.rb' + require File.join dir, file + '.rb' klass = nil [Oxidized, Object].each do |mod| klass = mod.constants.find { |const| const.to_s.downcase == file.downcase } - klass = mod.constants.find { |const| const.to_s.downcase == 'oxidized'+ file.downcase } unless klass + klass = mod.constants.find { |const| const.to_s.downcase == 'oxidized' + file.downcase } unless klass klass = mod.const_get klass if klass break if klass end @@ -31,16 +31,19 @@ module Oxidized @source = {} @hook = {} end + def add_input method method = Manager.load Config::InputDir, method return false if method.empty? @input.merge! method end + def add_output method method = Manager.load Config::OutputDir, method return false if method.empty? @output.merge! method end + def add_model _model name = _model _model = Manager.load File.join(Config::Root, 'model'), name @@ -48,14 +51,16 @@ module Oxidized return false if _model.empty? @model.merge! _model end + def add_source _source - return nil if @source.key? _source + return nil if @source.has_key? _source _source = Manager.load Config::SourceDir, _source return false if _source.empty? @source.merge! _source end + def add_hook _hook - return nil if @hook.key? _hook + return nil if @hook.has_key? _hook name = _hook _hook = Manager.load File.join(Config::Root, 'hook'), name _hook = Manager.load Config::HookDir, name if _hook.empty? diff --git a/lib/oxidized/model/acos.rb b/lib/oxidized/model/acos.rb index 47649a2..a2db89c 100644 --- a/lib/oxidized/model/acos.rb +++ b/lib/oxidized/model/acos.rb @@ -3,7 +3,7 @@ class ACOS < Oxidized::Model comment '! ' - ##ACOS prompt changes depending on the state of the device + # ACOS prompt changes depending on the state of the device prompt /^([-\w.\/:?\[\]\(\)]+[#>]\s?)$/ cmd :secret do |cfg| @@ -30,19 +30,19 @@ class ACOS < Oxidized::Model 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 + 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 + 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| @@ -50,7 +50,7 @@ class ACOS < Oxidized::Model end cmd 'show aflex all-partitions' do |cfg| - @partitions_aflex = cfg.lines.each_with_object({}) do |l,h| + @partitions_aflex = cfg.lines.each_with_object({}) do |l, h| h[$1] = [] if l.match /partition: (.+)/ # only consider scripts that have passed syntax check h[h.keys.last] << $1 if l.match /^([\w-]+) +Check/ @@ -66,7 +66,7 @@ class ACOS < Oxidized::Model pre do unless @partitions_aflex.empty? out = [] - @partitions_aflex.each do |partition,arules| + @partitions_aflex.each do |partition, arules| out << "! partition: #{partition}" arules.each do |name| cmd("show aflex #{name} partition #{partition}") do |cfg| @@ -85,7 +85,7 @@ class ACOS < Oxidized::Model username /login:/ password /^Password:/ end - + cfg :telnet, :ssh do # preferred way to handle additional passwords post_login do @@ -98,5 +98,4 @@ class ACOS < Oxidized::Model post_login 'terminal width 0' pre_logout "exit\nexit\nY\r\n" end - end diff --git a/lib/oxidized/model/acsw.rb b/lib/oxidized/model/acsw.rb new file mode 100644 index 0000000..c0857b3 --- /dev/null +++ b/lib/oxidized/model/acsw.rb @@ -0,0 +1,62 @@ +class ACSW < Oxidized::Model + prompt /([\w.@()\/\\-]+[#>]\s?)/ + comment '! ' + + cmd :all do |cfg| + cfg.gsub! /^% Invalid input detected at '\^' marker\.$|^\s+\^$/, '' + 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+).*/, '\\1 <secret hidden>' + cfg.gsub! /^(username \S+ password \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(username \S+ secret \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(enable (password|secret) \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(\s+(?:password|secret)) (?:\d )?\S+/, '\\1 <secret hidden>' + cfg.gsub! /^(.*wpa-psk ascii \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(.*key 7) (.*)/, '\\1 <secret hidden>' + cfg.gsub! /^(tacacs-server key \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(crypto isakmp key) (\S+) (.*)/, '\\1 <secret hidden> \\3' + cfg.gsub! /^(.*key 1 md5) (\d.+)/, '\\1 <secret hidden>' + cfg.gsub! /^(.*standby \d.+authentication).*/, '\\1 <secret hidden>' + cfg.gsub! /^(.*version 2c).*/, '\\1 <secret hidden>' + cfg + end + + cmd 'show version' do |cfg| + comment cfg + end + + cmd 'show inventory' do |cfg| + comment cfg + end + + cmd 'show running-config' do |cfg| + cfg = cfg.each_line.to_a[3..-1] + cfg = cfg.reject { |line| line.match /^ntp clock-period / }.join + cfg.gsub! /^Current configuration : [^\n]*\n/, '' + cfg.gsub! /^\ tunnel\ mpls\ traffic-eng\ bandwidth[^\n]*\n*( + (?:\ [^\n]*\n*)* + tunnel\ mpls\ traffic-eng\ auto-bw)/mx, '\1' + cfg.gsub! /^([\s\t\!]*Last configuration change ).*/, '' + cfg.gsub! /^([\s\t\!]*NVRAM config last ).*/, '' + cfg + end + + cfg :telnet do + username /.*login:/ + password /^Password:/ + end + + cfg :telnet, :ssh do + if vars :enable + post_login do + send "enable\n" + cmd vars(:enable) + end + end + post_login 'terminal length 0' + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/aen.rb b/lib/oxidized/model/aen.rb new file mode 100644 index 0000000..474e6d5 --- /dev/null +++ b/lib/oxidized/model/aen.rb @@ -0,0 +1,19 @@ +class AEN < Oxidized::Model + # Accedian + + comment '# ' + + prompt /^([-\w.\/:?\[\]\(\)]+:\s?)$/ + + cmd 'configuration generate-script module all' do |cfg| + cfg + end + + cmd :all do |cfg| + cfg.each_line.to_a[1..-2].join + end + + cfg :ssh do + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/aireos.rb b/lib/oxidized/model/aireos.rb index 7056e3f..a0378c7 100644 --- a/lib/oxidized/model/aireos.rb +++ b/lib/oxidized/model/aireos.rb @@ -1,17 +1,16 @@ class Aireos < Oxidized::Model - # AireOS (at least I think that is what it's called, hard to find data) # Used in Cisco WLC 5500 - comment '# ' ## this complains too, can't find real comment char + comment '# ' # this complains too, can't find real comment char prompt /^\([^\)]+\)\s>/ cmd :all do |cfg| cfg.each_line.to_a[1..-2].join end - ##show sysinfo? - ##show switchconfig? + # show sysinfo? + # show switchconfig? cmd 'show udi' do |cfg| cfg = comment clean cfg @@ -44,12 +43,11 @@ class Aireos < Oxidized::Model out = [] cfg.each_line do |line| next if line.match /^\s*$/ - next if line.match /rogue (adhoc|client) alert [\da-f]{2}:/ + next if line.match /rogue (adhoc|client) (alert|Unknown) [\da-f]{2}:/ line = line[1..-1] if line[0] == "\r" out << line.strip end out = out.join "\n" out << "\n" end - end diff --git a/lib/oxidized/model/airos.rb b/lib/oxidized/model/airos.rb index 775005f..7d82956 100644 --- a/lib/oxidized/model/airos.rb +++ b/lib/oxidized/model/airos.rb @@ -1,14 +1,19 @@ class Airos < Oxidized::Model # Ubiquiti AirOS circa 5.x - + prompt /^[^#]+# / - + comment '# ' + cmd 'cat /etc/board.info' do |cfg| cfg.split("\n").map { |line| "# #{line}" }.join("\n") + "\n" end - + + cmd 'cat /etc/version' do |cfg| + comment "airos version: #{cfg}" + end + cmd 'sort /tmp/system.cfg' - + cmd :secret do |cfg| cfg.gsub! /^(users\.\d+\.password|snmp\.community)=.+/, "# \\1=<hidden>" cfg diff --git a/lib/oxidized/model/alteonos.rb b/lib/oxidized/model/alteonos.rb new file mode 100644 index 0000000..dec4faf --- /dev/null +++ b/lib/oxidized/model/alteonos.rb @@ -0,0 +1,58 @@ +class ALTEONOS < Oxidized::Model + prompt /^\(?.+\)?\s?[#>]/ + + comment '! ' + + cmd :secret do |cfg| + cfg.gsub!(/^([\s\t]*admpw ).*/, '\1 <password removed>') + cfg.gsub!(/^([\s\t]*pswd ).*/, '\1 <password removed>') + cfg.gsub!(/^([\s\t]*esecret ).*/, '\1 <password removed>') + cfg + end + + ############################################################################################## + # Added to remove # + # # + # /* Configuration dump taken 14:10:20 Fri Jul 28, 2017 (DST) # + # /* Configuration last applied at 16:17:05 Fri Jul 14, 2017 # + # /* Configuration last save at 16:17:43 Fri Jul 14, 2017 # + # /* Version 29.0.3.12, vXXXXXXXX, Base MAC address XXXXXXXXXXX # + # /* To restore SSL Offloading configuration and management HTTPS access, # + # /* it is recommended to include the private keys in the dump. # + # OR # + # /* To restore SSL Offloading configuration and management HTTPS access,it is recommended # + # /* to include the private keys in the dump. # + # # + ############################################################################################## + + cmd 'cfg/dump' do |cfg| + cfg.gsub! /^([\s\t\/*]*Configuration).*/, '' + cfg.gsub! /^([\s\t\/*]*Version).*/, '' + cfg.gsub! /^([\s\t\/*]*To restore ).*/, '' + cfg.gsub! /^([\s\t\/*]*it is recommended to include).*/, '' + cfg.gsub! /^([\s\t\/*]*to include ).*/, '' + cfg + end + + # Answer for Dispay private keys + expect /^Display private keys\?\s?\[y\/n\]\: $/ do |data, re| + send "n\r" + data.sub re, '' + end + + # Answer for sync to peer on exit + expect /^Confirm Sync to Peer\s?\[y\/n\]\: $/ do |data, re| + send "n\r" + data.sub re, '' + end + + # Answer for Unsaved configuration + expect /^(WARNING: There are unsaved configuration changes).*/ do |data, re| + send "n\r" + data.sub re, '' + end + + cfg :ssh do + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/alvarion.rb b/lib/oxidized/model/alvarion.rb index 3c762de..8831f49 100644 --- a/lib/oxidized/model/alvarion.rb +++ b/lib/oxidized/model/alvarion.rb @@ -1,5 +1,4 @@ class Alvarion < Oxidized::Model - # Used in Alvarion wisp equipment # Run this command as an instance of Model so we can access node @@ -7,7 +6,6 @@ class Alvarion < Oxidized::Model cmd "#{node.auth[:password]}.cfg" end - - cfg :tftp {} - + cfg :tftp do + end end diff --git a/lib/oxidized/model/aos.rb b/lib/oxidized/model/aos.rb index ec73b92..fed78c8 100644 --- a/lib/oxidized/model/aos.rb +++ b/lib/oxidized/model/aos.rb @@ -1,8 +1,7 @@ class AOS < Oxidized::Model - # Alcatel-Lucent Operating System # used in OmniSwitch - + comment '! ' cmd :all do |cfg| @@ -10,7 +9,7 @@ class AOS < Oxidized::Model end cmd 'show system' do |cfg| - cfg = cfg.each_line.find{|line|line.match 'Description'} + cfg = cfg.each_line.find { |line| line.match 'Description' } comment cfg.to_s.strip end @@ -34,5 +33,4 @@ class AOS < Oxidized::Model cfg :telnet, :ssh do pre_logout 'exit' end - end diff --git a/lib/oxidized/model/aos7.rb b/lib/oxidized/model/aos7.rb index 8d11066..00bee54 100644 --- a/lib/oxidized/model/aos7.rb +++ b/lib/oxidized/model/aos7.rb @@ -1,8 +1,7 @@ class AOS7 < Oxidized::Model - # Alcatel-Lucent Operating System Version 7 (Linux based) # used in OmniSwitch 6900/10k - + comment '! ' cmd :all do |cfg, cmdstring| @@ -11,7 +10,7 @@ class AOS7 < Oxidized::Model end cmd 'show system' do |cfg| - cfg = cfg.each_line.find{|line|line.match 'Description'} + cfg = cfg.each_line.find { |line| line.match 'Description' } comment cfg.to_s.strip + "\n" end diff --git a/lib/oxidized/model/aosw.rb b/lib/oxidized/model/aosw.rb index adf481b..7543353 100644 --- a/lib/oxidized/model/aosw.rb +++ b/lib/oxidized/model/aosw.rb @@ -1,47 +1,60 @@ class AOSW < Oxidized::Model - - # AOSW Aruba Wireless + # AOSW Aruba Wireless, IAP, Instant Controller and Mobility Access Switches # Used in Alcatel OAW-4750 WLAN controller # Also Dell controllers + # HPE Aruba Switches should use a different model as the software is based on the HP Procurve line. + + # Support for IAP & Instant Controller tested with 115, 205, 215 & 325 running 6.4.4.8-4.2.4.5_57965 + # Support for Mobility Access Switches tested with S2500-48P & S2500-24P running 7.4.1.4_54199 and S2500-24P running 7.4.1.7_57823 + # All IAPs connected to a Instant Controller will have the same config output. Only the controller needs to be monitored. + comment '# ' - prompt /^\([^)]+\) [#>]/ + prompt /^\(?.+\)?\s[#>]/ cmd :all do |cfg| cfg.each_line.to_a[1..-2].join end cmd :secret do |cfg| + cfg.gsub!(/secret (\S+)$/, 'secret <secret removed>') + cfg.gsub!(/enable secret (\S+)$/, 'enable secret <secret removed>') cfg.gsub!(/PRE-SHARE (\S+)$/, 'PRE-SHARE <secret removed>') cfg.gsub!(/ipsec (\S+)$/, 'ipsec <secret removed>') cfg.gsub!(/community (\S+)$/, 'community <secret removed>') cfg.gsub!(/ sha (\S+)/, ' sha <secret removed>') cfg.gsub!(/ des (\S+)/, ' des <secret removed>') cfg.gsub!(/mobility-manager (\S+) user (\S+) (\S+)/, 'mobility-manager \1 user \2 <secret removed>') - cfg.gsub!(/mgmt-user (\S+) (\S+) (\S+)$/, 'mgmt-user \1 \2 <secret removed>') + cfg.gsub!(/mgmt-user (\S+) (root|guest\-provisioning|network\-operations|read\-only|location\-api\-mgmt) (\S+)$/, 'mgmt-user \1 \2 <secret removed>') # MAS & Wireless Controler + cfg.gsub!(/mgmt-user (\S+) (\S+)( (read\-only|guest\-mgmt))?$/, 'mgmt-user \1 <secret removed> \3') # IAP + # MAS format: mgmt-user <username> <accesslevel> <password hash> + # IAP format (root user): mgmt-user <username> <password hash> + # IAP format: mgmt-user <username> <password hash> <access level> cfg.gsub!(/key (\S+)$/, 'key <secret removed>') - cfg.gsub!(/secret (\S+)$/, 'secret <secret removed>') cfg.gsub!(/wpa-passphrase (\S+)$/, 'wpa-passphrase <secret removed>') cfg.gsub!(/bkup-passwords (\S+)$/, 'bkup-passwords <secret removed>') + cfg.gsub!(/user (\S+) (\S+) (\S+)$/, 'user \1 <secret removed> \3') + cfg.gsub!(/virtual-controller-key (\S+)$/, 'virtual-controller-key <secret removed>') cfg end cmd 'show version' do |cfg| - cfg = cfg.each_line.select { |line| not line.match /Switch uptime/i } + cfg = cfg.each_line.reject { |line| line.match /(Switch|AP) uptime/i } rstrip_cfg comment cfg.join end cmd 'show inventory' do |cfg| + cfg = "" if cfg.match /(Invalid input detected at '\^' marker|Parse error)/ # Don't show for unsupported devices (IAP and MAS) rstrip_cfg clean cfg end cmd 'show slots' do |cfg| - cfg = "" if cfg.match /Invalid input detected at '\^' marker/ #Don't show for unsupported devices + cfg = "" if cfg.match /(Invalid input detected at '\^' marker|Parse error)/ # Don't show for unsupported devices (IAP and MAS) rstrip_cfg comment cfg end cmd 'show license' do |cfg| - cfg = "" if cfg.match /Invalid input detected at '\^' marker/ #Don't show for unsupported devices + cfg = "" if cfg.match /(Invalid input detected at '\^' marker|Parse error)/ # Don't show for unsupported devices (IAP and MAS) rstrip_cfg comment cfg end @@ -98,5 +111,4 @@ class AOSW < Oxidized::Model out = comment out.join "\n" out << "\n" end - end diff --git a/lib/oxidized/model/apc_aos.rb b/lib/oxidized/model/apc_aos.rb index 530d436..5a4d232 100644 --- a/lib/oxidized/model/apc_aos.rb +++ b/lib/oxidized/model/apc_aos.rb @@ -1,11 +1,8 @@ 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/arbos.rb b/lib/oxidized/model/arbos.rb new file mode 100644 index 0000000..51b269d --- /dev/null +++ b/lib/oxidized/model/arbos.rb @@ -0,0 +1,26 @@ +class ARBOS < Oxidized::Model + # Arbor OS model # + + prompt /^[\S\s]+\n([\w.@-]+[:\/#>]+)\s?$/ + comment '# ' + + cmd 'system hardware' do |cfg| + cfg.gsub! /^Boot\ time\:\s.+/, '' # Remove boot timer + cfg.gsub! /^Load\ averages\:\s.+/, '' # Remove CPU load info + cfg = cfg.each_line.to_a[2..-1].join + comment cfg + end + + cmd 'system version' do |cfg| + comment cfg + end + + cmd 'config show' do |cfg| + cfg + end + + cfg :ssh do + exec true + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/aricentiss.rb b/lib/oxidized/model/aricentiss.rb new file mode 100644 index 0000000..77b78f4 --- /dev/null +++ b/lib/oxidized/model/aricentiss.rb @@ -0,0 +1,51 @@ +# Developed against: +# #show version +# Switch ID Hardware Version Firmware Version +# 0 SSE-G48-TG4 (P2-01) 1.0.16-9 + +class AricentISS < Oxidized::Model + prompt (/^(\e\[27m)?[ \r]*[\w-]+# ?$/) + + cfg :ssh do + # "pagination" was misspelled in some (earlier) versions (at least 1.0.16-9) + # 1.0.18-15 is known to include the corrected spelling + post_login 'no cli pagination' + post_login 'no cli pagignation' + pre_logout 'exit' + end + + cmd :all do |cfg| + # * Drop first line that contains the command, and the last line that + # contains a prompt + # * Strip carriage returns + cfg.delete("\r").each_line.to_a[1..-2].join + end + + cmd :secret do |cfg| + cfg.gsub(/^(snmp community) .*/, '\1 <hidden>') + end + + cmd 'show system information' do |cfg| + cfg.sub! /^Device Up Time.*\n/, '' + cfg.delete! "\r" + comment(cfg).gsub(/ +$/, '') + end + + cmd 'show running-config' do |cfg| + comment_next = 0 + cfg.each_line.map { |l| + next '' if l.match /^Building configuration/ + + if l.match /^Switch ID.*Hardware Version.*Firmware Version/ then + comment_next = 2 + end + + if comment_next > 0 then + comment_next -= 1 + next comment(l) + end + + l + }.join.gsub(/ +$/, '') + end +end diff --git a/lib/oxidized/model/asa.rb b/lib/oxidized/model/asa.rb index df30059..dfd94b1 100644 --- a/lib/oxidized/model/asa.rb +++ b/lib/oxidized/model/asa.rb @@ -1,5 +1,4 @@ class ASA < Oxidized::Model - # Cisco ASA model # # Only SSH supported for the sake of security @@ -13,15 +12,21 @@ class ASA < Oxidized::Model cmd :secret do |cfg| cfg.gsub! /enable password (\S+) (.*)/, 'enable password <secret hidden> \2' 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! /(ikev[12] ((remote|local)-authentication )?pre-shared-key) (\S+)/, '\1 <secret hidden>' cfg.gsub! /^(aaa-server TACACS\+? \(\S+\) host.*\n\skey) \S+$/mi, '\1 <secret hidden>' + cfg.gsub! /ldap-login-password (\S+)/, 'ldap-login-password <secret hidden>' + cfg.gsub! /^snmp-server host (.*) community (\S+)/, 'snmp-server host \1 community <secret hidden>' cfg end + # check for multiple contexts + cmd 'show mode' do |cfg| + @is_multiple_context = cfg.include? 'multiple' + end + cmd 'show version' do |cfg| # avoid commits due to uptime / ixo-router01 up 2 mins 28 secs / ixo-router01 up 1 days 2 hours - cfg = cfg.each_line.select { |line| not line.match /(\s+up\s+\d+\s+)|(.*days.*)/ } + cfg = cfg.each_line.reject { |line| line.match /(\s+up\s+\d+\s+)|(.*days.*)/ } cfg = cfg.join comment cfg end @@ -30,25 +35,12 @@ class ASA < Oxidized::Model comment cfg end - cmd 'more system:running-config' do |cfg| - cfg = cfg.each_line.to_a[3..-1].join - cfg.gsub! /^: [^\n]*\n/, '' - # backup any xml referenced in the configuration. - anyconnect_profiles = cfg.scan(Regexp.new('(\sdisk0:/.+\.xml)')).flatten - anyconnect_profiles.each do |profile| - cfg << (comment profile + "\n" ) - cmd ("more" + profile) do |xml| - cfg << (comment xml) - end + post do + if @is_multiple_context + multiple_context + else + single_context end - # if DAP is enabled, also backup dap.xml - if cfg.rindex(/dynamic-access-policy-record\s(?!DfltAccessPolicy)/) - cfg << (comment "disk0:/dap.xml\n") - cmd "more disk0:/dap.xml" do |xml| - cfg << (comment xml) - end - end - cfg end cfg :ssh do @@ -62,4 +54,46 @@ class ASA < Oxidized::Model pre_logout 'exit' end + def single_context + # Single context mode + cmd 'more system:running-config' do |cfg| + cfg = cfg.each_line.to_a[3..-1].join + cfg.gsub! /^: [^\n]*\n/, '' + # backup any xml referenced in the configuration. + anyconnect_profiles = cfg.scan(Regexp.new('(\sdisk0:/.+\.xml)')).flatten + anyconnect_profiles.each do |profile| + cfg << (comment profile + "\n") + cmd ("more" + profile) do |xml| + cfg << (comment xml) + end + end + # if DAP is enabled, also backup dap.xml + if cfg.rindex(/dynamic-access-policy-record\s(?!DfltAccessPolicy)/) + cfg << (comment "disk0:/dap.xml\n") + cmd "more disk0:/dap.xml" do |xml| + cfg << (comment xml) + end + end + cfg + end + end + + def multiple_context + # Multiple context mode + cmd 'changeto system' do |cfg| + cmd 'show running-config' do |systemcfg| + allcfg = "\n\n" + systemcfg + "\n\n" + contexts = systemcfg.scan(/^context (\S+)$/) + files = systemcfg.scan(/config-url (\S+)$/) + contexts.each_with_index do |cont, i| + allcfg = allcfg + "\n\n----------========== [ CONTEXT " + cont.join(" ") + " FILE " + files[i].join(" ") + " ] ==========----------\n\n" + cmd "more " + files[i].join(" ") do |cfgcontext| + allcfg = allcfg + "\n\n" + cfgcontext + end + end + cfg = allcfg + end + cfg + end + end end diff --git a/lib/oxidized/model/asyncos.rb b/lib/oxidized/model/asyncos.rb new file mode 100644 index 0000000..2857ef8 --- /dev/null +++ b/lib/oxidized/model/asyncos.rb @@ -0,0 +1,46 @@ +class AsyncOS < Oxidized::Model + # ESA prompt "(mail.example.com)> " + prompt /^\r*([(][\w. ]+[)][#>]\s+)$/ + comment '! ' + + # Select passphrase display option + expect /\[\S+\]>\s/ do |data, re| + send "3\n" + data.sub re, '' + end + + # handle paging + expect /-Press Any Key For More-+.*$/ do |data, re| + send " " + data.sub re, '' + end + + cmd 'version' do |cfg| + comment cfg + end + + cmd 'showconfig' do |cfg| + # Delete hour and date which change each run + # cfg.gsub! /\sCurrent Time: \S+\s\S+\s+\S+\s\S+\s\S+/, ' Current Time:' + # Delete select passphrase display option + cfg.gsub! /Choose the passphrase option:/, '' + cfg.gsub! /1. Mask passphrases \(Files with masked passphrases cannot be loaded using/, '' + cfg.gsub! /loadconfig command\)/, '' + cfg.gsub! /2. Encrypt passphrases/, '' + cfg.gsub! /3. Plain passphrases/, '' + cfg.gsub! /^3$/, '' + # Delete space + cfg.gsub! /\n\s{25,26}/, '' + # Delete after line + cfg.gsub! /([-\\\/,.\w><@]+)(\s{25,27})/, "\\1" + # Add a carriage return + cfg.gsub! /([-\\\/,.\w><@]+)(\s{6})([-\\\/,.\w><@]+)/, "\\1\n\\2\\3" + # Delete prompt + cfg.gsub! /^\r*([(][\w. ]+[)][#>]\s+)$/, '' + cfg + end + + cfg :ssh do + pre_logout "exit" + end +end diff --git a/lib/oxidized/model/audiocodes.rb b/lib/oxidized/model/audiocodes.rb new file mode 100644 index 0000000..2c77abb --- /dev/null +++ b/lib/oxidized/model/audiocodes.rb @@ -0,0 +1,28 @@ +class AudioCodes < Oxidized::Model + # Pull config from AudioCodes Mediant devices from version > 7.0 + + prompt /^\r?([\w.@() -]+[#>]\s?)$/ + comment '## ' + + expect /\s*--MORE--$/ do |data, re| + send ' ' + + data.sub re, '' + end + + cmd 'show running-config' do |cfg| + cfg + end + + cfg :ssh do + username /^login as:\s$/ + password /^.+password:\s$/ + pre_logout 'exit' + end + + cfg :telnet do + username /^Username:\s$/ + password /^Password:\s$/ + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/awplus.rb b/lib/oxidized/model/awplus.rb new file mode 100644 index 0000000..7c88d60 --- /dev/null +++ b/lib/oxidized/model/awplus.rb @@ -0,0 +1,84 @@ +class AWPlus < Oxidized::Model + # Allied Telesis Alliedware Plus Model# + # https://www.alliedtelesis.com/products/software/AlliedWare-Plus + + prompt /^(\r?[\w.@:\/-]+[#>]\s?)$/ + comment '! ' + + # Avoids needing "term length 0" to display full config file. + expect /--More--/ do |data, re| + send ' ' + data.sub re, '' + end + + # Removes gibberish pager output e.g. VT100 escape codes + cmd :all do |cfg| + cfg.gsub! /\e\[K/, '' # example how to handle pager - cleareol EL0 + cfg.gsub! /\e\[7m\e\[m/, '' # example how to handle pager - Reverse SGR7 + cfg.gsub! /\r/, '' # Filters rogue ^M - see issue #415 + cfg.each_line.to_a[1..-2].join + end + + # Remove passwords from config file. + # Add vars "remove_secret: true" to global oxidized config file to enable. + + cmd :secret do |cfg| + cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' + cfg.gsub! /^(username \S+ privilege \d+) (\S+).*/, '\\1 <secret hidden>' + cfg.gsub! /^(username \S+ password \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(username \S+ secret \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(enable (password|secret) \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(\s+(?:password|secret)) (?:\d )?\S+/, '\\1 <secret hidden>' + cfg.gsub! /^(tacacs-server key \d) (\S+)/, '\\1 <secret hidden>' + cfg + end + + # Adds "Show system" output to start of config. + + cmd 'Show System' do |cfg| + comment cfg.insert(0, "--------------------------------------------------------------------------------! \n") + # Unhash below to write a comment in the config file. + cfg.insert(0, "Starting: Show system cmd \n") + cfg << "\n \nEnding: show system cmd" + comment cfg << "\n--------------------------------------------------------------------------------! \n \n" + # Removes the following lines from "show system" in output file. This ensures oxidized diffs are meaningful. + comment cfg.each_line.reject { |line| + line.match /^$\n/ or # Remove blank lines in "sh sys" + line.match /System Status\s*.*/ or + line.match /RAM\s*:.*/ or + line.match /Uptime\s*:.*/ or + line.match /Flash\s*:.*/ or + line.match /Current software\s*:.*/ or + line.match /Software version\s*:.*/ or + line.match /Build date\s*:.*/ + } .join + end + + # Actually get the devices running config# + cmd 'show running-config' do |cfg| + cfg + end + + # Config required for telnet to detect username prompt + cfg :telnet do + username /login:\s/ + end + + # Main login config + cfg :telnet, :ssh do + post_login do + if vars :enable + send "enable\n" + expect /^Password:\s/ + cmd vars(:enable) + "\r\n" + else + cmd 'enable' # Required for Priv-Exec users without enable PW to be put into "enable mode". + end + # cmd 'terminal length 0' #set so the entire config is output without intervention. + end + pre_logout do + # cmd 'terminal no length' #Sets term length back to default on exit. + send "exit\r\n" + end + end +end diff --git a/lib/oxidized/model/boss.rb b/lib/oxidized/model/boss.rb new file mode 100644 index 0000000..cf762ee --- /dev/null +++ b/lib/oxidized/model/boss.rb @@ -0,0 +1,75 @@ +class Boss < Oxidized::Model + # Avaya Baystack Operating System Software(BOSS) + # Created by danielcoxman@gmail.com + # May 15, 2017 + # This was tested on ers3510, ers5530, ers4850, ers5952 + # ssh and telnet were tested with banner and without + + comment '! ' + + prompt /^[^\s#>]+[#>]$/ + + # Handle the banner + # to disable the banner on BOSS the configuration parameter is "banner disabled" + expect /Enter Ctrl-Y to begin\./ do |data, re| + send "\cY" + data.sub re, '' + end + + # Handle the Failed retries since last login + # no known way to disable other than to implement radius authentication + expect /Press ENTER to continue/ do |data, re| + send "\n" + data.sub re, '' + end + + # Handle the menu on the older BOSS example ers55xx, ers56xx + # to disable them menu on BOSS the configuration parameter is "cmd-interface cli" + expect /ommand Line Interface\.\.\./ do |data, re| + send "c" + data.sub re, '' + end + + # needed for proper formatting + cmd('') { |cfg| comment "#{cfg}\n" } + + # Do a sys-info and check and see if it supports stack + cmd 'show sys-info' do |cfg| + @stack = true if cfg.match /Stack/ + cfg.gsub! /(^((.*)sysUpTime(.*))$)/, 'removed sysUpTime' + cfg.gsub! /(^((.*)sysNtpTime(.*))$)/, 'removed sysNtpTime' + cfg.gsub! /(^((.*)sysRtcTime(.*))$)/, 'removed sysNtpTime' + # remove timestamp + cfg.gsub! /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} .*/, '' + comment "#{cfg}\n" + end + + # if a stack then collect the stacking information + cmd 'show stack-info' do |cfg| + if @stack + # remove timestamp + cfg.gsub! /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} .*/, '' + comment "#{cfg}\n" + end + end + + cmd 'show running-config' do |cfg| + cfg.gsub! /^show running-config/, '! show running-config' + # remove timestamp + cfg.gsub! /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} .*/, '' + cfg.gsub! /^[^\s#>]+[#>]$/, '' + cfg.gsub! /^! clock set.*/, '! removed clock set' + cfg + end + + cfg :telnet do + username /Username: / + password /Password: / + end + + cfg :telnet, :ssh do + pre_logout 'logout' + post_login 'terminal length 0' + post_login 'terminal width 132' + end +end diff --git a/lib/oxidized/model/br6910.rb b/lib/oxidized/model/br6910.rb index b5c9bcf..1e79da3 100644 --- a/lib/oxidized/model/br6910.rb +++ b/lib/oxidized/model/br6910.rb @@ -1,45 +1,43 @@ -
-class BR6910 < Oxidized::Model
-
- prompt /^Vty-[0-9]\#$/
- comment '! '
-
- # not possible to disable paging prior to show running-config
- expect /^((.*)Others to exit ---(.*))$/ do |data, re|
- send 'a'
- data.sub re, ''
- end
-
- cmd :all do |cfg|
- # sometimes br6910s inserts arbitrary whitespace after commands are
- # issued on the CLI, from run to run. this normalises the output.
- cfg.each_line.to_a[1..-2].drop_while { |e| e.match /^\s+$/ }.join
- end
-
- cmd 'show version' do |cfg|
- comment cfg
- end
-
- # show flash is not possible on a brocade 6910, do dir instead
- # to see flash contents (includes config file names)
- cmd 'dir' do |cfg|
- comment cfg
- end
-
- cmd 'show running-config' do |cfg|
- arr = cfg.each_line.to_a
- arr[2..-1].join unless arr.length < 2
- end
-
- cfg :telnet do
- username /^Username:/
- password /^Password:/
- end
-
- # post login and post logout
- cfg :telnet, :ssh do
- post_login ''
- pre_logout 'exit'
- end
-
-end
+ +class BR6910 < Oxidized::Model + prompt /^([\w.@()-]+[#>]\s?)$/ + comment '! ' + + # not possible to disable paging prior to show running-config + expect /^((.*)Others to exit ---(.*))$/ do |data, re| + send 'a' + data.sub re, '' + end + + cmd :all do |cfg| + # sometimes br6910s inserts arbitrary whitespace after commands are + # issued on the CLI, from run to run. this normalises the output. + cfg.each_line.to_a[1..-2].drop_while { |e| e.match /^\s+$/ }.join + end + + cmd 'show version' do |cfg| + comment cfg + end + + # show flash is not possible on a brocade 6910, do dir instead + # to see flash contents (includes config file names) + cmd 'dir' do |cfg| + comment cfg + end + + cmd 'show running-config' do |cfg| + arr = cfg.each_line.to_a + arr[2..-1].join unless arr.length < 2 + end + + cfg :telnet do + username /^Username:/ + password /^Password:/ + end + + # post login and post logout + cfg :telnet, :ssh do + post_login '' + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/c4cmts.rb b/lib/oxidized/model/c4cmts.rb index 150029c..8ea27c6 100644 --- a/lib/oxidized/model/c4cmts.rb +++ b/lib/oxidized/model/c4cmts.rb @@ -1,15 +1,14 @@ class C4CMTS < Oxidized::Model - # Arris C4 CMTS prompt /^([\w.@:\/-]+[#>]\s?)$/ comment '! ' cmd :all do |cfg| - cfg.each_line.to_a[1..-2].map{|line|line.delete("\r").rstrip}.join("\n") + "\n" + cfg.each_line.to_a[1..-2].map { |line| line.delete("\r").rstrip }.join("\n") + "\n" end - cmd :secret do |cfg| + cmd :secret do |cfg| cfg.gsub! /(.+)\s+encrypted-password\s+\w+\s+(.*)/, '\\1 <secret hidden> \\2' cfg.gsub! /(snmp-server community)\s+".*"\s+(.*)/, '\\1 <secret hidden> \\2' cfg.gsub! /(tacacs.*\s+key)\s+".*"\s+(.*)/, '\\1 <secret hidden> \\2' @@ -25,7 +24,7 @@ class C4CMTS < Oxidized::Model cmd 'show version' do |cfg| # remove uptime readings at char 55 and beyond - cfg = cfg.each_line.map{|line|line.rstrip.slice(0..54)}.join("\n") + "\n" + cfg = cfg.each_line.map { |line| line.rstrip.slice(0..54) }.join("\n") + "\n" comment cfg end @@ -48,5 +47,4 @@ class C4CMTS < Oxidized::Model end pre_logout 'exit' end - end diff --git a/lib/oxidized/model/catos.rb b/lib/oxidized/model/catos.rb index bac9eec..e97a0b6 100644 --- a/lib/oxidized/model/catos.rb +++ b/lib/oxidized/model/catos.rb @@ -1,5 +1,4 @@ class Catos < Oxidized::Model - prompt /^[\w.@-]+>\s?(\(enable\) )?$/ comment '# ' @@ -39,5 +38,4 @@ class Catos < Oxidized::Model end pre_logout 'exit' end - end diff --git a/lib/oxidized/model/cisconga.rb b/lib/oxidized/model/cisconga.rb new file mode 100644 index 0000000..9764351 --- /dev/null +++ b/lib/oxidized/model/cisconga.rb @@ -0,0 +1,17 @@ +class CiscoNGA < Oxidized::Model + comment '# ' + prompt /([\w.@-]+[#>]\s?)$/ + + cmd 'show version' do |cfg| + comment cfg + end + + cmd 'show configuration' do |cfg| + cfg + end + + cfg :ssh do + post_login 'terminal length 0' + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/ciscosma.rb b/lib/oxidized/model/ciscosma.rb new file mode 100644 index 0000000..5a34f86 --- /dev/null +++ b/lib/oxidized/model/ciscosma.rb @@ -0,0 +1,42 @@ +class CiscoSMA < Oxidized::Model + # SMA prompt "mail.example.com> " + prompt /^\r*([-\w. ]+\.[-\w. ]+\.[-\w. ]+[#>]\s+)$/ + comment '! ' + + # Select passphrase display option + expect /using loadconfig command\. \[Y\]\>/ do |data, re| + send "y\n" + data.sub re, '' + end + + # handle paging + expect /-Press Any Key For More-+.*$/ do |data, re| + send " " + data.sub re, '' + end + + cmd 'version' do |cfg| + comment cfg + end + + cmd 'showconfig' do |cfg| + # Delete hour and date which change each run + # cfg.gsub! /\sCurrent Time: \S+\s\S+\s+\S+\s\S+\s\S+/, ' Current Time:' + # Delete select passphrase display option + cfg.gsub! /Do you want to mask the password\? Files with masked passwords cannot be loaded/, '' + cfg.gsub! /^\s+y/, '' + # Delete space + cfg.gsub! /\n\s{25}/, '' + # Delete after line + cfg.gsub! /([\/\-,.\w><@]+)(\s{27})/, "\\1" + # Add a carriage return + cfg.gsub! /([\/\-,.\w><@]+)(\s{6,8})([\/\-,.\w><@]+)/, "\\1\n\\2\\3" + # Delete prompt + cfg.gsub! /^\r*([-\w. ]+\.[-\w. ]+\.[-\w. ]+[#>]\s+)$/, '' + cfg + end + + cfg :ssh do + pre_logout "exit" + end +end diff --git a/lib/oxidized/model/ciscosmb.rb b/lib/oxidized/model/ciscosmb.rb index e5501d5..ef6f4e6 100644 --- a/lib/oxidized/model/ciscosmb.rb +++ b/lib/oxidized/model/ciscosmb.rb @@ -1,6 +1,5 @@ class CiscoSMB < Oxidized::Model - - # Cisco Small Business 200, 300, 500, and ESW2 series switches + # Cisco Small Business 300, 500, and ESW2 series switches # http://www.cisco.com/c/en/us/support/switches/small-business-300-series-managed-switches/products-release-notes-list.html prompt /^\r?([\w.@()-]+[#>]\s?)$/ @@ -16,6 +15,8 @@ class CiscoSMB < Oxidized::Model cmd :secret do |cfg| cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' cfg.gsub! /username (\S+) privilege (\d+) (\S+).*/, '<secret hidden>' + cfg.gsub! /^(encrypted radius-server key).*/, '\\1 <configuration removed>' + cfg.gsub! /System Up Time.*/, '' cfg end @@ -23,6 +24,14 @@ class CiscoSMB < Oxidized::Model comment cfg end + cmd 'show system' do |cfg| + comment cfg + end + + cmd 'show bootvar' do |cfg| + comment cfg + end + cmd 'show running-config' do |cfg| cfg = cfg.each_line.to_a[0..-1].join cfg.gsub! /^Current configuration : [^\n]*\n/, '' @@ -39,8 +48,7 @@ class CiscoSMB < Oxidized::Model 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' # exit returns to previous priv level, no way to quit from exec(#) pre_logout 'exit' end - end diff --git a/lib/oxidized/model/comware.rb b/lib/oxidized/model/comware.rb index 27b70ae..f20cd4d 100644 --- a/lib/oxidized/model/comware.rb +++ b/lib/oxidized/model/comware.rb @@ -1,23 +1,29 @@ class Comware < Oxidized::Model # HP (A-series)/H3C/3Com Comware - - # sometimes the prompt might have a leading nul - prompt /^\0*(<[\w.-]+>)$/ + + # sometimes the prompt might have a leading nul or trailing ASCII Bell (^G) + prompt /^\0*(<[\w.-]+>).?$/ comment '# ' # example how to handle pager - #expect /^\s*---- More ----$/ do |data, re| + # expect /^\s*---- More ----$/ do |data, re| # send ' ' # data.sub re, '' - #end + # end cmd :all do |cfg| - #cfg.gsub! /^.*\e\[42D/, '' # example how to handle pager - #skip rogue ^M + # cfg.gsub! /^.*\e\[42D/, '' # example how to handle pager + # skip rogue ^M cfg = cfg.gsub /\r/, '' cfg.each_line.to_a[1..-2].join end - + + cmd :secret do |cfg| + cfg.gsub! /^( snmp-agent community).*/, '\\1 <configuration removed>' + cfg.gsub! /^( password hash).*/, '\\1 <configuration removed>' + cfg + end + cfg :telnet do username /^Username:$/ password /^Password:$/ @@ -41,7 +47,7 @@ class Comware < Oxidized::Model end cmd 'display version' do |cfg| - cfg = cfg.each_line.select {|l| not l.match /uptime/i }.join + cfg = cfg.each_line.reject { |l| l.match /uptime/i }.join comment cfg end diff --git a/lib/oxidized/model/coriant8600.rb b/lib/oxidized/model/coriant8600.rb index a48ffa5..2e8bb76 100644 --- a/lib/oxidized/model/coriant8600.rb +++ b/lib/oxidized/model/coriant8600.rb @@ -1,7 +1,6 @@ class Coriant8600 < Oxidized::Model - comment '# ' - + prompt /^[^\s#>]+[#>]$/ cmd 'show hw-inventory' do |cfg| @@ -11,7 +10,7 @@ class Coriant8600 < Oxidized::Model cmd 'show flash' do |cfg| comment cfg end - + cmd 'show run' do |cfg| cfg end @@ -20,11 +19,10 @@ class Coriant8600 < Oxidized::Model username /^user name:$/ password /^password:$/ end - + cfg :telnet, :ssh do pre_logout 'exit' post_login 'enable' post_login 'terminal more off' end - end diff --git a/lib/oxidized/model/coriantgroove.rb b/lib/oxidized/model/coriantgroove.rb new file mode 100644 index 0000000..9d81e82 --- /dev/null +++ b/lib/oxidized/model/coriantgroove.rb @@ -0,0 +1,28 @@ +class CoriantGroove < Oxidized::Model + comment '# ' + + prompt /^(\w+@.*>\s*)$/ + + cmd :all do |cfg| + cfg.each_line.to_a[1..-3].map { |line| line.delete("\r").rstrip }.join("\n") + "\n" + end + + cmd 'show inventory' do |cfg| + cfg = cfg.each_line.to_a[0..-2].join + comment cfg + end + + cmd 'show softwareload' do |cfg| + cfg = cfg.each_line.to_a[0..-2].join + comment cfg + end + + cmd 'show config | display commands' do |cfg| + cfg.each_line.to_a[1..-1].join + end + + cfg :ssh do + post_login 'set -f cli-config cli-columns 65535' + pre_logout 'quit -f' + end +end diff --git a/lib/oxidized/model/corianttmos.rb b/lib/oxidized/model/corianttmos.rb index 76603f6..bc9618e 100644 --- a/lib/oxidized/model/corianttmos.rb +++ b/lib/oxidized/model/corianttmos.rb @@ -1,7 +1,6 @@ class CoriantTmos < Oxidized::Model - comment '# ' - + prompt /^[^\s#]+#\s$/ cmd 'show node extensive' do |cfg| @@ -21,5 +20,4 @@ class CoriantTmos < Oxidized::Model pre_logout 'exit' post_login 'enable config terminal length 0' end - end diff --git a/lib/oxidized/model/cumulus.rb b/lib/oxidized/model/cumulus.rb index dc6792a..334e1e4 100644 --- a/lib/oxidized/model/cumulus.rb +++ b/lib/oxidized/model/cumulus.rb @@ -1,73 +1,81 @@ class Cumulus < Oxidized::Model - prompt /^((\w*)@(.*)):/ - comment '# ' - - - #add a comment in the final conf + comment '# ' + + # add a comment in the final conf def add_comment comment - "\n###### #{comment} ######\n" + "\n###### #{comment} ######\n" end cmd :all do |cfg| cfg.each_line.to_a[1..-2].join end - - #show the persistent configuration + + # show the persistent configuration pre do cfg = add_comment 'THE HOSTNAME' cfg += cmd 'cat /etc/hostname' - + cfg += add_comment 'THE HOSTS' cfg += cmd 'cat /etc/hosts' - + cfg += add_comment 'THE INTERFACES' cfg += cmd 'grep -r "" /etc/network/interface* | cut -d "/" -f 4-' - + cfg += add_comment 'RESOLV.CONF' cfg += cmd 'cat /etc/resolv.conf' - + cfg += add_comment 'NTP.CONF' cfg += cmd 'cat /etc/ntp.conf' - + cfg += add_comment 'IP Routes' cfg += cmd 'netstat -rn' + cfg += add_comment 'SNMP settings' + cfg += cmd 'cat /etc/snmp/snmpd.conf' + cfg += add_comment 'QUAGGA DAEMONS' cfg += cmd 'cat /etc/quagga/daemons' - + cfg += add_comment 'QUAGGA ZEBRA' cfg += cmd 'cat /etc/quagga/zebra.conf' - + cfg += add_comment 'QUAGGA BGP' cfg += cmd 'cat /etc/quagga/bgpd.conf' - + cfg += add_comment 'QUAGGA OSPF' cfg += cmd 'cat /etc/quagga/ospfd.conf' - + cfg += add_comment 'QUAGGA OSPF6' cfg += cmd 'cat /etc/quagga/ospf6d.conf' - + + cfg += add_comment 'QUAGGA CONF' + cfg += cmd 'cat /etc/quagga/Quagga.conf' + cfg += add_comment 'MOTD' cfg += cmd 'cat /etc/motd' - + cfg += add_comment 'PASSWD' cfg += cmd 'cat /etc/passwd' - - cfg += add_comment ' SWITCHD' + + cfg += add_comment 'SWITCHD' cfg += cmd 'cat /etc/cumulus/switchd.conf' - + + cfg += add_comment 'PORTS' + cfg += cmd 'cat /etc/cumulus/ports.conf' + + cfg += add_comment 'TRAFFIC' + cfg += cmd 'cat /etc/cumulus/datapath/traffic.conf' + cfg += add_comment 'ACL' cfg += cmd 'iptables -L -n' - + cfg += add_comment 'VERSION' cfg += cmd 'cat /etc/cumulus/etc.replace/os-release' cfg += add_comment 'License' cfg += cmd 'cl-license' - end - cfg :telnet do username /^Username:/ @@ -77,6 +85,4 @@ class Cumulus < Oxidized::Model cfg :telnet, :ssh do pre_logout 'exit' end - - end diff --git a/lib/oxidized/model/datacom.rb b/lib/oxidized/model/datacom.rb index 5dbc080..5d5d220 100644 --- a/lib/oxidized/model/datacom.rb +++ b/lib/oxidized/model/datacom.rb @@ -1,5 +1,4 @@ class DataCom < Oxidized::Model - comment '! ' expect /^--More--\s+$/ do |data, re| @@ -34,5 +33,4 @@ class DataCom < Oxidized::Model password /^Password:\s$/ pre_logout 'exit' end - end diff --git a/lib/oxidized/model/dcnos.rb b/lib/oxidized/model/dcnos.rb new file mode 100644 index 0000000..59e6744 --- /dev/null +++ b/lib/oxidized/model/dcnos.rb @@ -0,0 +1,46 @@ +# DCNOS is a ZebOS derivative by DCN (http://www.dcnglobal.com/) +# In addition to products by DCN (now Yunke China), this OS type +# powers a number of re-branded OEM devices. + +# Developed against SNR S2950-24G 7.0.3.5 + +class DCNOS < Oxidized::Model + comment '! ' + + cmd :all do |cfg| + cfg.each_line.to_a[1..-1].join + end + + cmd 'show version' do |cfg| + cfg.gsub! /\s(Uptime is).*/, '' + comment cfg + end + + cmd 'show boot-files' do |cfg| + comment cfg + end + + cmd 'show flash' do |cfg| + comment cfg + end + + cmd 'show running-config' do |cfg| + cfg + end + + cfg :telnet do + username /^login:/i + password /^password:/i + end + + cfg :telnet, :ssh do + if vars :enable + post_login do + send "enable\n" + cmd vars(:enable) + end + end + post_login 'terminal length 0' + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/dlink.rb b/lib/oxidized/model/dlink.rb index 5756bad..6c604fb 100644 --- a/lib/oxidized/model/dlink.rb +++ b/lib/oxidized/model/dlink.rb @@ -11,10 +11,11 @@ class Dlink < Oxidized::Model end cmd :all do |cfg| - cfg.each_line.to_a[2..-2].map{|line|line.delete("\r").rstrip}.join("\n") + "\n" + cfg.each_line.to_a[2..-2].map { |line| line.delete("\r").rstrip }.join("\n") + "\n" end cmd 'show switch' do |cfg| + cfg.gsub! /^System\ Uptime\s.+/, '' # Omit constantly changing uptime info comment cfg end diff --git a/lib/oxidized/model/dnos.rb b/lib/oxidized/model/dnos.rb index a44630e..d8b1d96 100644 --- a/lib/oxidized/model/dnos.rb +++ b/lib/oxidized/model/dnos.rb @@ -1,14 +1,15 @@ -class DNOS < Oxidized::Model - +class DNOS < Oxidized::Model # Force10 DNOS model # comment '! ' cmd :all do |cfg| + cfg.gsub! /^% Invalid input detected at '\^' marker\.$|^\s+\^$/, '' + cfg.gsub! /^Dell Networking OS uptime is\s.+/, '' # Omit constantly changing uptime info cfg.each_line.to_a[2..-2].join end - cmd :secret do |cfg| + cmd :secret do |cfg| cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' cfg.gsub! /secret (\d+) (\S+).*/, '<secret hidden>' cfg @@ -22,6 +23,10 @@ class DNOS < Oxidized::Model comment cfg end + cmd 'show version' do |cfg| + comment cfg + end + cmd 'show running-config' do |cfg| cfg = cfg.each_line.to_a[3..-1].join cfg @@ -42,7 +47,6 @@ class DNOS < Oxidized::Model post_login 'terminal length 0' post_login 'terminal width 0' pre_logout 'exit' - pre_logout 'exit' + pre_logout 'exit' end - end diff --git a/lib/oxidized/model/edgecos.rb b/lib/oxidized/model/edgecos.rb new file mode 100644 index 0000000..3668769 --- /dev/null +++ b/lib/oxidized/model/edgecos.rb @@ -0,0 +1,45 @@ +class EdgeCOS < Oxidized::Model + comment '! ' + + cmd :secret do |cfg| + cfg.gsub!(/password \d+ (\S+).*/, '<secret removed>') + cfg.gsub!(/community (\S+)/, 'community <hidden>') + cfg + end + + cmd :all do |cfg| + cfg.each_line.to_a[2..-2].join + end + + cmd 'show running-config' + + cmd 'show access-list tcam-utilization' do |cfg| + comment cfg + end + + cmd 'show memory' do |cfg| + comment cfg + end + + cmd 'show system' do |cfg| + comment cfg + end + + cmd 'show version' do |cfg| + comment cfg + end + + cmd 'show watchdog' do |cfg| + comment cfg + end + + cfg :telnet do + username /^Username:/ + password /^Password:/ + end + + cfg :telnet, :ssh do + post_login 'terminal length 0' + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/edgeos.rb b/lib/oxidized/model/edgeos.rb index 2a8d663..a723904 100644 --- a/lib/oxidized/model/edgeos.rb +++ b/lib/oxidized/model/edgeos.rb @@ -1,5 +1,4 @@ class Edgeos < Oxidized::Model - # EdgeOS # prompt /\@.*?\:~\$\s/ @@ -9,11 +8,15 @@ class Edgeos < Oxidized::Model end cmd :secret do |cfg| + cfg.gsub! /encrypted-password (\S+).*/, 'encrypted-password <secret removed>' + cfg.gsub! /plaintext-password (\S+).*/, 'plaintext-password <secret removed>' + cfg.gsub! /password (\S+).*/, 'password <secret removed>' + cfg.gsub! /pre-shared-secret (\S+).*/, 'pre-shared-secret <secret removed>' cfg.gsub! /community (\S+) {/, 'community <hidden> {' cfg end - cmd 'show configuration | no-more' + cmd 'show configuration commands | no-more' cfg :telnet do username /login:\s/ @@ -23,5 +26,4 @@ class Edgeos < Oxidized::Model cfg :telnet, :ssh do pre_logout 'exit' end - end diff --git a/lib/oxidized/model/edgeswitch.rb b/lib/oxidized/model/edgeswitch.rb index 7f5b1ea..36c0f9f 100644 --- a/lib/oxidized/model/edgeswitch.rb +++ b/lib/oxidized/model/edgeswitch.rb @@ -1,6 +1,5 @@ class EdgeSwitch < Oxidized::Model - -# Ubiquiti EdgeSwitch # + # Ubiquiti EdgeSwitch # comment '!' @@ -28,5 +27,4 @@ class EdgeSwitch < Oxidized::Model pre_logout 'quit' pre_logout 'n' end - end diff --git a/lib/oxidized/model/enterasys.rb b/lib/oxidized/model/enterasys.rb new file mode 100644 index 0000000..40f676d --- /dev/null +++ b/lib/oxidized/model/enterasys.rb @@ -0,0 +1,28 @@ +class Enterasys < Oxidized::Model + # Enterasys B3/C3 models # + + prompt /^.+\w\(su\)->\s?$/ + + comment '!' + + cmd :all do |cfg| + cfg.each_line.to_a[2..-3].map { |line| line.delete("\r").rstrip }.join("\n") + "\n" + end + + cmd 'show system hardware' do |cfg| + comment cfg + end + + cmd 'show config' do |cfg| + cfg.gsub! /^This command shows non-default configurations only./, '' + cfg.gsub! /^Use 'show config all' to show both default and non-default configurations./, '' + cfg.gsub! /^!|#.*/, '' + cfg.gsub! /^$\n/, '' + + cfg + end + + cfg :ssh do + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/eos.rb b/lib/oxidized/model/eos.rb index a9f3ff3..f29910c 100644 --- a/lib/oxidized/model/eos.rb +++ b/lib/oxidized/model/eos.rb @@ -1,5 +1,4 @@ class EOS < Oxidized::Model - # Arista EOS model # prompt /^.+[#>]\s?$/ @@ -7,15 +6,15 @@ class EOS < Oxidized::Model comment '! ' cmd :all do |cfg| - cfg.each_line.to_a[1..-2].join + 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! /^(enable secret).*/, '\\1 <configuration removed>' - cfg.gsub! /^(tacacs-server key \d+).*/, '\\1 <configuration removed>' - cfg + cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' + cfg.gsub! /username (\S+) privilege (\d+) (\S+).*/, '<secret hidden>' + cfg.gsub! /^(enable secret).*/, '\\1 <configuration removed>' + cfg.gsub! /^(tacacs-server key \d+).*/, '\\1 <configuration removed>' + cfg end cmd 'show inventory | no-more' do |cfg| @@ -41,5 +40,4 @@ class EOS < Oxidized::Model end pre_logout 'exit' end - end diff --git a/lib/oxidized/model/fabricos.rb b/lib/oxidized/model/fabricos.rb index e6d921e..b150c51 100644 --- a/lib/oxidized/model/fabricos.rb +++ b/lib/oxidized/model/fabricos.rb @@ -1,22 +1,20 @@ class FabricOS < Oxidized::Model - # Brocade Fabric OS model # ## FIXME: Only ssh exec mode support, no telnet, no ssh screenscraping prompt /^([\w]+:+[\w]+[>]\s)$/ - comment '# ' + comment '# ' cmd 'chassisShow' do |cfg| comment cfg.each_line.reject { |line| line.match /Time Awake:/ or line.match /Power Usage \(Watts\):/ or line.match /Time Alive:/ or line.match /Update:/ }.join end cmd 'configShow -all' do |cfg| - cfg = cfg.each_line.reject { |line| line.match /date = /}.join + cfg = cfg.each_line.reject { |line| line.match /date = / }.join cfg end cfg :ssh do - exec true # don't run shell, run each command in exec channel + exec true # don't run shell, run each command in exec channel end - end diff --git a/lib/oxidized/model/fiberdriver.rb b/lib/oxidized/model/fiberdriver.rb index abe8f68..7cfa847 100644 --- a/lib/oxidized/model/fiberdriver.rb +++ b/lib/oxidized/model/fiberdriver.rb @@ -13,7 +13,7 @@ class FiberDriver < Oxidized::Model cfg.each_line.to_a[3..-1].join cfg.gsub! /^Building configuration.*$/, '' cfg.gsub! /^Current configuration:.*$$/, '' - cfg.gsub! /^! Configuration saved on .*$/, '' + cfg.gsub! /^! Configuration (saved|generated) on .*$/, '' cfg end diff --git a/lib/oxidized/model/firewareos.rb b/lib/oxidized/model/firewareos.rb index f2bef4c..bd0008d 100644 --- a/lib/oxidized/model/firewareos.rb +++ b/lib/oxidized/model/firewareos.rb @@ -1,15 +1,20 @@ class FirewareOS < Oxidized::Model - - prompt /^\[?\w*\]?\w*<?\w*>?#\s*$/ + prompt /^\[?\w*\]?\w*?(<\w*>)?(#|>)\s*$/ comment '-- ' cmd :all do |cfg| cfg.each_line.to_a[1..-2].join end + # Handle Logon Disclaimer added in XTM 11.9.3 + expect /^I have read and accept the Logon Disclaimer message. \(yes or no\)\? $/ do |data, re| + send "yes\n" + data.sub re, '' + end + cmd 'show sysinfo' do |cfg| # avoid commits due to uptime - cfg = cfg.each_line.select { |line| not line.match /(.*time.*)|(.*memory.*)|(.*cpu.*)/ } + cfg = cfg.each_line.reject { |line| line.match /(.*time.*)|(.*memory.*)|(.*cpu.*)/ } cfg = cfg.join comment cfg end @@ -19,6 +24,4 @@ class FirewareOS < Oxidized::Model cfg :ssh do pre_logout 'exit' end - end - diff --git a/lib/oxidized/model/fortios.rb b/lib/oxidized/model/fortios.rb index cd379b7..bbbab89 100644 --- a/lib/oxidized/model/fortios.rb +++ b/lib/oxidized/model/fortios.rb @@ -1,6 +1,5 @@ class FortiOS < Oxidized::Model - - comment '# ' + comment '# ' prompt /^([-\w\.\~]+(\s[\(\w\-\.\)]+)?\~?\s?[#>$]\s?)$/ @@ -14,6 +13,16 @@ class FortiOS < Oxidized::Model new_cfg << cfg.each_line.to_a[1..-2].map { |line| line.gsub(/(conf_file_ver=)(.*)/, '\1<stripped>\3') }.join end + cmd :secret do |cfg| + cfg.gsub! /(set (?:passwd|password|psksecret|secret|key|group-password|secondary-secret|tertiary-secret|auth-password-l1|auth-password-l2|rsso|history0|history1|inter-controller-key ENC|passphrase ENC|login-passwd ENC)).*/, '\\1 <configuration removed>' + cfg.gsub! /(set private-key).*-+END ENCRYPTED PRIVATE KEY-*"$/m, '\\1 <configuration removed>' + cfg.gsub! /(set ca ).*-+END CERTIFICATE-*"$/m, '\\1 <configuration removed>' + cfg.gsub! /(set csr ).*-+END CERTIFICATE REQUEST-*"$/m, '\\1 <configuration removed>' + cfg.gsub! /(Virus-DB|Extended DB|IPS-DB|IPS-ETDB|APP-DB|INDUSTRIAL-DB|Botnet DB|IPS Malicious URL Database).*/, '\\1 <configuration removed>' + cfg.gsub! /(Cluster uptime:).*/, '\\1 <configuration removed>' + cfg + end + cmd 'get system status' do |cfg| @vdom_enabled = cfg.include? 'Virtual domain configuration: enable' cfg.gsub!(/(System time: )(.*)/, '\1<stripped>\3') @@ -28,13 +37,18 @@ class FortiOS < Oxidized::Model comment cfg end - cfg << cmd('diagnose autoupdate version') do |cfg| - comment cfg + # default behaviour: include autoupdate output (backwards compatibility) + # do not include if variable "show_autoupdate" is set to false + if defined?(vars(:fortios_autoupdate)).nil? || vars(:fortios_autoupdate) + cfg << cmd('diagnose autoupdate version') do |cfg| + cfg.gsub! /(FDS Address\n---------\n).*/, '\\1IP Address removed' + comment cfg.each_line.reject { |line| line.match /Last Update|Result/ }.join + end end cfg << cmd('end') if @vdom_enabled - cfg << cmd('show') + cfg << cmd('show full-configuration | grep .') cfg.join "\n" end @@ -46,5 +60,4 @@ class FortiOS < Oxidized::Model cfg :telnet, :ssh do pre_logout "exit\n" end - end diff --git a/lib/oxidized/model/ftos.rb b/lib/oxidized/model/ftos.rb index b465271..3ef4de6 100644 --- a/lib/oxidized/model/ftos.rb +++ b/lib/oxidized/model/ftos.rb @@ -1,5 +1,4 @@ -class FTOS < Oxidized::Model - +class FTOS < Oxidized::Model # Force10 FTOS model # comment '! ' @@ -8,13 +7,15 @@ class FTOS < Oxidized::Model cfg.each_line.to_a[2..-2].join end - cmd :secret do |cfg| + cmd :secret do |cfg| cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' cfg.gsub! /secret (\d+) (\S+).*/, '<secret hidden>' cfg end cmd 'show inventory' do |cfg| + # Old versions of FTOS can occasionally return data that triggers encoding errors. + cfg.encode!("UTF-8", :invalid => :replace, :undef => :replace, :replace => "") comment cfg end @@ -43,5 +44,4 @@ class FTOS < Oxidized::Model end pre_logout 'exit' end - end diff --git a/lib/oxidized/model/fujitsupy.rb b/lib/oxidized/model/fujitsupy.rb index 20a78dd..a2db909 100644 --- a/lib/oxidized/model/fujitsupy.rb +++ b/lib/oxidized/model/fujitsupy.rb @@ -1,5 +1,4 @@ class FujitsuPY < Oxidized::Model - prompt /^(\([\w.-]*\)\s#|^\S+\#\s)$/ comment '! ' @@ -7,13 +6,13 @@ class FujitsuPY < Oxidized::Model cfg.each_line.to_a[1..-2].join end -# 1Gbe switch + # 1Gbe switch cmd 'show version' do |cfg| cfg.gsub! /^(<ERROR> : 2 : format error)$/, '' comment cfg end -# 10Gbe switch + # 10Gbe switch cmd 'show system information' do |cfg| cfg.gsub! /^Current-time : [\w\s:]*$/, '' cfg.gsub! /^(\s{33}\^)$/, '' @@ -38,5 +37,4 @@ class FujitsuPY < Oxidized::Model send "n\n" end end - end diff --git a/lib/oxidized/model/gaiaos.rb b/lib/oxidized/model/gaiaos.rb index 434e774..8f13bcb 100644 --- a/lib/oxidized/model/gaiaos.rb +++ b/lib/oxidized/model/gaiaos.rb @@ -1,25 +1,23 @@ class GaiaOS < Oxidized::Model - # CheckPoint - Gaia OS Model - + # Gaia Prompt prompt /^([\[\]\w.@:-]+[#>]\s?)$/ # Comment tag comment '# ' - cmd :all do |cfg| cfg = cfg.each_line.to_a[1..-2].join end cmd :secret do |cfg| cfg.gsub! /^(set expert-password-hash ).*/, '\1<EXPERT PASSWORD REMOVED>' - cfg.gsub! /^(set user \S+ password-hash ).*/,'\1<USER PASSWORD REMOVED>' - cfg.gsub! /^(set ospf .* secret ).*/,'\1<OSPF KEY REMOVED>' - cfg.gsub! /^(set snmp community )(.*)( read-only.*)/,'\1<SNMP COMMUNITY REMOVED>\3' - cfg.gsub! /^(add snmp .* community )(.*)(\S?.*)/,'\1<SNMP COMMUNITY REMOVED>\3' - cfg.gsub! /(auth|privacy)(-pass-phrase-hashed )(\S*)/,'\1-pass-phrase-hashed <SNMP PASS-PHRASE REMOVED>' + cfg.gsub! /^(set user \S+ password-hash ).*/, '\1<USER PASSWORD REMOVED>' + cfg.gsub! /^(set ospf .* secret ).*/, '\1<OSPF KEY REMOVED>' + cfg.gsub! /^(set snmp community )(.*)( read-only.*)/, '\1<SNMP COMMUNITY REMOVED>\3' + cfg.gsub! /^(add snmp .* community )(.*)(\S?.*)/, '\1<SNMP COMMUNITY REMOVED>\3' + cfg.gsub! /(auth|privacy)(-pass-phrase-hashed )(\S*)/, '\1-pass-phrase-hashed <SNMP PASS-PHRASE REMOVED>' cfg end @@ -36,11 +34,9 @@ class GaiaOS < Oxidized::Model cfg end - cfg :ssh do # User shell must be /etc/cli.sh post_login 'set clienv rows 0' pre_logout 'exit' end - end diff --git a/lib/oxidized/model/gcombnps.rb b/lib/oxidized/model/gcombnps.rb new file mode 100644 index 0000000..e92c7ba --- /dev/null +++ b/lib/oxidized/model/gcombnps.rb @@ -0,0 +1,82 @@ +class GcomBNPS < Oxidized::Model + # For switches from GCOM Technologies Co.,Ltd. running the "Broadband Network Platform Software" + # Author: Frederik Kriewitz <frederik@kriewitz.eu> + # + # tested with: + # - S5330 (aka Fiberstore S3800) + + prompt /^\r?([\w.@()-]+?(\(1-16 chars\))?[#>:]\s?)$/ # also match SSH password promt (post_login commands are sent after the first prompt) + comment '! ' + + # alternative to handle the SSH login, but this breaks telnet + # expect /^Password\(1-16 chars\):/ do |data| + # send @node.auth[:password] + "\n" + # '' + # end + + # handle pager (can't be disabled?) + expect /^\.\.\.\.press ENTER to next line, CTRL_C to quit, other key to next page\.\.\.\.$/ do |data, re| + send ' ' + data.sub re, '' + end + + cmd :all do |cfg| + cfg = cfg.gsub " \e[73D\e[K", '' # remove garbage remaining from the pager + cfg.each_line.to_a[1..-2].join + end + + cmd :secret do |cfg| + cfg.gsub! /^(snmp-server community)\s+[^\s]+\s+(.*)/, '\\1 <community hidden> \\2' + cfg + end + + cmd 'show running-config' do |cfg| + cfg + end + + cmd 'show interface sfp' do |cfg| + out = [] + cfg.each_line do |line| + next if line.match /^ Temperature/ + next if line.match /^ Voltage\(V\)/ + next if line.match /^ Bias Current\(mA\)/ + next if line.match /^ RX Power\(dBM\)/ + next if line.match /^ TX Power\(dBM\)/ + out << line + end + + comment out.join + end + + cmd 'show version' do |cfg| + comment cfg + end + + cmd 'show system' do |cfg| + out = [] + cfg.each_line do |line| + next if line.match /^system run time :/ + next if line.match /^switch temperature :/ + out << line + end + + comment out.join + end + + cfg :telnet do + username /^Username\(1-32 chars\):/ + password /^Password\(1-16 chars\):/ + end + + cfg :ssh do + # the switch blindy accepts the SSH connection without password validation and then spawns a telnet login prompt + # first thing we've to send is the password + post_login do + send @node.auth[:password] + "\n" + end + end + + cfg :telnet, :ssh do + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/hatteras.rb b/lib/oxidized/model/hatteras.rb index 4192cbc..7341cc4 100644 --- a/lib/oxidized/model/hatteras.rb +++ b/lib/oxidized/model/hatteras.rb @@ -9,7 +9,6 @@ class Hatteras < Oxidized::Model data.sub re, '' end - cmd :secret do |cfg| cfg.gsub! /^(community) \S+/, '\\1 "<configuration removed>"' cfg.gsub! /^(communityString) "\S+"/, '\\1 "<configuration removed>"' @@ -22,14 +21,18 @@ class Hatteras < Oxidized::Model 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 + 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 + 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 diff --git a/lib/oxidized/model/hirschmann.rb b/lib/oxidized/model/hirschmann.rb new file mode 100644 index 0000000..858b198 --- /dev/null +++ b/lib/oxidized/model/hirschmann.rb @@ -0,0 +1,39 @@ +class Hirschmann < Oxidized::Model + prompt /^[\(\w\s\w\)]+\s[>|#]+?$/ + + comment '## ' + + # Handle pager + expect /^--More--.*$/ do |data, re| + send 'a' + data.sub re, '' + end + + cmd :all do |cfg| + cfg.each_line.to_a[1..-2].join + end + + cmd 'show sysinfo' do |cfg| + cfg.gsub! /^System Up Time.*\n/, "" + cfg.gsub! /^System Date and Time.*\n/, "" + cfg.gsub! /^CPU Utilization.*\n/, "" + cfg.gsub! /^Memory.*\n/, "" + cfg.gsub! /^Average CPU Utilization.*\n/, "" + comment cfg + end + + cmd 'show running-config' do |cfg| + cfg.gsub! /^users.*\n/, "" + cfg + end + + cfg :telnet do + username /^User:/ + password /^Password:/ + end + + cfg :telnet, :ssh do + post_login 'enable' + pre_logout 'logout' + end +end diff --git a/lib/oxidized/model/hpebladesystem.rb b/lib/oxidized/model/hpebladesystem.rb index 5e34de8..75d2dbf 100644 --- a/lib/oxidized/model/hpebladesystem.rb +++ b/lib/oxidized/model/hpebladesystem.rb @@ -4,13 +4,13 @@ class HPEBladeSystem < Oxidized::Model prompt /.*> / comment '# ' - expect /^\s*--More--\s+.*$/ do |data, re| - send ' ' - data.sub re, '' - end + # 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 = 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 @@ -22,7 +22,7 @@ class HPEBladeSystem < Oxidized::Model cmd 'show oa info' do |cfg| comment cfg end - + cmd 'show oa network' do |cfg| comment cfg end @@ -30,16 +30,17 @@ class HPEBladeSystem < Oxidized::Model 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| + cfg.gsub! /Last Update:.*$/i, '' comment cfg end @@ -54,15 +55,15 @@ class HPEBladeSystem < Oxidized::Model 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 @@ -71,13 +72,14 @@ class HPEBladeSystem < Oxidized::Model 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 + + cfg :telnet, :ssh do + post_login "set script mode on" + pre_logout "exit" + end end diff --git a/lib/oxidized/model/hpemsa.rb b/lib/oxidized/model/hpemsa.rb new file mode 100644 index 0000000..68485a1 --- /dev/null +++ b/lib/oxidized/model/hpemsa.rb @@ -0,0 +1,10 @@ +class HpeMsa < Oxidized::Model + prompt /^#\s?$/ + + cmd 'show configuration' + + cfg :ssh do + post_login 'set cli-parameters pager disabled' + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/ios.rb b/lib/oxidized/model/ios.rb index c8b0ef3..8069f83 100644 --- a/lib/oxidized/model/ios.rb +++ b/lib/oxidized/model/ios.rb @@ -1,41 +1,114 @@ class IOS < Oxidized::Model - prompt /^([\w.@()-]+[#>]\s?)$/ comment '! ' # example how to handle pager - #expect /^\s--More--\s+.*$/ do |data, re| + # expect /^\s--More--\s+.*$/ do |data, re| # send ' ' # data.sub re, '' - #end + # end # non-preferred way to handle additional PW prompt - #expect /^[\w.]+>$/ do |data| + # expect /^[\w.]+>$/ do |data| # send "enable\n" # send vars(:enable) + "\n" # data - #end + # end cmd :all do |cfg| - #cfg.gsub! /\cH+\s{8}/, '' # example how to handle pager - #cfg.gsub! /\cH+/, '' # example how to handle pager + # cfg.gsub! /\cH+\s{8}/, '' # example how to handle pager + # cfg.gsub! /\cH+/, '' # example how to handle pager + # get rid of errors for commands that don't work on some devices + cfg.gsub! /^% Invalid input detected at '\^' marker\.$|^\s+\^$/, '' 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! /^username \S+ secret \d \S+/, '<secret hidden>' - cfg.gsub! /^enable (password|secret) \d \S+/, '<secret hidden>' + cfg.gsub! /^(snmp-server host \S+( vrf \S+)?( version (1|2c|3))?)\s+\S+((\s+\S*)*)\s*/, '\\1 <secret hidden> \\5' + cfg.gsub! /^(username \S+ privilege \d+) (\S+).*/, '\\1 <secret hidden>' + cfg.gsub! /^(username \S+ password \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(username \S+ secret \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(enable (password|secret) \d) (\S+)/, '\\1 <secret hidden>' cfg.gsub! /^(\s+(?:password|secret)) (?:\d )?\S+/, '\\1 <secret hidden>' - cfg.gsub! /wpa-psk ascii \d \S+/, '<secret hidden>' - cfg.gsub! /^tacacs-server key \d \S+/, '<secret hidden>' + cfg.gsub! /^(.*wpa-psk ascii \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(.*key 7) (\d.+)/, '\\1 <secret hidden>' + cfg.gsub! /^(tacacs-server key \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(crypto isakmp key) (\S+) (.*)/, '\\1 <secret hidden> \\3' cfg end cmd 'show version' do |cfg| - comment cfg.lines.first + comments = [] + comments << cfg.lines.first + lines = cfg.lines + lines.each_with_index do |line, i| + slave = '' + slaveslot = '' + + if line.match /^Slave in slot (\d+) is running/ + slave = " Slave:"; + slaveslot = ", slot #{$1}"; + end + + if line.match /^Compiled (.*)$/ + comments << "Image:#{slave} Compiled: #{$1}" + end + + if line.match /^(?:Cisco )?IOS .* Software,? \(([A-Za-z0-9_-]*)\), .*Version\s+(.*)$/ + comments << "Image:#{slave} Software: #{$1}, #{$2}" + end + + if line.match /^ROM: (IOS \S+ )?(System )?Bootstrap.*(Version.*)$/ + comments << "ROM Bootstrap: #{$3}" + end + + if line.match /^BOOTFLASH: .*(Version.*)$/ + comments << "BOOTFLASH: #{$1}" + end + + if line.match /^(\d+[kK]) bytes of (non-volatile|NVRAM)/ + comments << "Memory: nvram #{$1}" + end + + if line.match /^(\d+[kK]) bytes of (flash memory|flash internal|processor board System flash|ATA CompactFlash)/i + comments << "Memory: flash #{$1}" + end + + if line.match (/^(\d+[kK]) bytes of (Flash|ATA)?.*PCMCIA .*(slot|disk) ?(\d)/i) + comments << "Memory: pcmcia #{$2} #{$3}#{$4} #{$1}"; + end + + if line.match /(\S+(?:\sseries)?)\s+(?:\((\S+)\)\s+processor|\(revision[^)]+\)).*\s+with (\S+k) bytes/i + sproc = $1 + cpu = $2 + mem = $3 + cpuxtra = '' + comments << "Chassis type:#{slave} #{sproc}"; + comments << "Memory:#{slave} main #{mem}"; + # check the next two lines for more CPU info + if cfg.lines[i + 1].match /processor board id (\S+)/i + comments << "Processor ID: #{$1}"; + end + if cfg.lines[i + 2].match /(cpu at |processor: |#{cpu} processor,)/i + # change implementation to impl and prepend comma + cpuxtra = cfg.lines[i + 2].gsub(/implementation/, 'impl').gsub(/^/, ', ').chomp; + end + comments << "CPU:#{slave} #{cpu}#{cpuxtra}#{slaveslot}"; + end + + if line.match /^System image file is "([^\"]*)"$/ + comments << "Image: #{$1}" + end + end + comments << "\n" + comment comments.join "\n" + end + + cmd 'show vtp status' do |cfg| + cfg.gsub! /^$\n/, '' + cfg.gsub! /^/, 'VTP: ' if (!cfg.empty?) + comment "#{cfg}\n" end cmd 'show inventory' do |cfg| @@ -53,8 +126,8 @@ class IOS < Oxidized::Model end cfg :telnet do - username /^Username:/ - password /^Password:/ + username /^Username:/i + password /^Password:/i end cfg :telnet, :ssh do @@ -69,5 +142,4 @@ class IOS < Oxidized::Model post_login 'terminal width 0' pre_logout 'exit' end - end diff --git a/lib/oxidized/model/iosxe.rb b/lib/oxidized/model/iosxe.rb new file mode 100644 index 0000000..1945e11 --- /dev/null +++ b/lib/oxidized/model/iosxe.rb @@ -0,0 +1,5 @@ +# IOS parser should work here + +require_relative 'ios.rb' + +IOSXE = IOS diff --git a/lib/oxidized/model/iosxr.rb b/lib/oxidized/model/iosxr.rb index bf01140..1635e57 100644 --- a/lib/oxidized/model/iosxr.rb +++ b/lib/oxidized/model/iosxr.rb @@ -1,5 +1,4 @@ class IOSXR < Oxidized::Model - # IOS XR model # prompt /^(\r?[\w.@:\/-]+[#>]\s?)$/ @@ -9,7 +8,7 @@ class IOSXR < Oxidized::Model cfg.each_line.to_a[2..-2].join end - cmd :secret do |cfg| + cmd :secret do |cfg| cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' cfg.gsub! /secret (\d+) (\S+).*/, '<secret hidden>' cfg @@ -36,6 +35,7 @@ class IOSXR < Oxidized::Model cfg :telnet, :ssh do post_login 'terminal length 0' post_login 'terminal width 0' + post_login 'terminal exec prompt no-timestamp' if vars :enable post_login do send "enable\n" @@ -44,5 +44,4 @@ class IOSXR < Oxidized::Model end pre_logout 'exit' end - end diff --git a/lib/oxidized/model/ipos.rb b/lib/oxidized/model/ipos.rb index 5efd831..753c6b1 100644 --- a/lib/oxidized/model/ipos.rb +++ b/lib/oxidized/model/ipos.rb @@ -1,9 +1,8 @@ class IPOS < Oxidized::Model - # Ericsson SSR (IPOS) # Redback SE (SEOS) - prompt /^([\[\]\w.@-]+[#>]\s?)$/ + prompt /^([\[\]\w.@-]+[#:>]\s?)$/ comment '! ' cmd 'show chassis' do |cfg| @@ -52,10 +51,15 @@ class IPOS < Oxidized::Model cfg :telnet, :ssh do post_login 'terminal length 0' + if vars :enable + post_login do + cmd "enable" + cmd vars(:enable) + end + end pre_logout do send "exit\n" send "n\n" end end - end diff --git a/lib/oxidized/model/ironware.rb b/lib/oxidized/model/ironware.rb index 5b58f22..32dbf47 100644 --- a/lib/oxidized/model/ironware.rb +++ b/lib/oxidized/model/ironware.rb @@ -1,19 +1,17 @@ class IronWare < Oxidized::Model - prompt /^.*(telnet|ssh)\@.+[>#]\s?$/i comment '! ' - #to handle pager without enable - #expect /^((.*)--More--(.*))$/ do |data, re| + # to handle pager without enable + # expect /^((.*)--More--(.*))$/ do |data, re| # send ' ' # data.sub re, '' - #end - + # end - #to remove backspace (if handle pager without enable) - #expect /^((.*)[\b](.*))$/ do |data, re| + # to remove backspace (if handle pager without enable) + # expect /^((.*)[\b](.*))$/ do |data, re| # data.sub re, '' - #end + # end cmd :all do |cfg| # sometimes ironware inserts arbitrary whitespace after commands are @@ -22,18 +20,20 @@ class IronWare < Oxidized::Model end cmd 'show version' do |cfg| - cfg.gsub! /(^((.*)[Ss]ystem uptime(.*))$)/, '' #remove unwanted line system uptime + cfg.gsub! /(^((.*)[Ss]ystem uptime(.*))$)/, '' # remove unwanted line system uptime cfg.gsub! /(^((.*)[Tt]he system started at(.*))$)/, '' - cfg.gsub! /[Uu]p\s?[Tt]ime is .*/,'' + cfg.gsub! /[Uu]p\s?[Tt]ime is .*/, '' comment cfg end cmd 'show chassis' do |cfg| - cfg.encode!("UTF-8", :invalid => :replace, :undef => :replace) #sometimes ironware returns broken encoding - cfg.gsub! /(^((.*)Current temp(.*))$)/, '' #remove unwanted lines current temperature - cfg.gsub! /Speed = [A-Z-]{2,6} \(\d{2,3}\%\)/, '' #remove unwanted lines Speed Fans + cfg.encode!("UTF-8", :invalid => :replace, :undef => :replace) # sometimes ironware returns broken encoding + cfg.gsub! /(^((.*)Current temp(.*))$)/, '' # remove unwanted lines current temperature + cfg.gsub! /Speed = [A-Z-]{2,6} \(\d{2,3}\%\)/, '' # remove unwanted lines Speed Fans cfg.gsub! /current speed is [A-Z]{2,6} \(\d{2,3}\%\)/, '' + cfg.gsub! /Fan \d* - STATUS: OK \D*\d*./, '' # Fix for ADX Fan speed reporting + cfg.gsub! /\d* deg C/, '' # Fix for ADX temperature reporting cfg.gsub! /([\[]*)1([\]]*)<->([\[]*)2([\]]*)(<->([\[]*)3([\]]*))*/, '' cfg.gsub! /\d{2}\.\d deg-C/, 'XX.X deg-C' if cfg.include? "TEMPERATURE" @@ -50,6 +50,7 @@ class IronWare < Oxidized::Model end cmd 'show flash' do |cfg| + cfg.gsub! /(\d+) bytes/, '' # Fix for ADX flash size comment cfg end @@ -67,23 +68,20 @@ class IronWare < Oxidized::Model # match expected prompts on both older and newer # versions of IronWare username /^(Please Enter Login Name|Username):/ - password /^(Please Enter )Password:/ + password /^(Please Enter Password ?|Password):/ end - #handle pager with enable + # handle pager with enable cfg :telnet, :ssh do if vars :enable post_login do - send "enable\r\n" + send "enable\n" cmd vars(:enable) end end post_login '' post_login 'skip-page-display' post_login 'terminal length 0' - pre_logout 'logout' - pre_logout 'exit' - pre_logout 'exit' + pre_logout "logout\nexit\nexit\n" end - end diff --git a/lib/oxidized/model/isam.rb b/lib/oxidized/model/isam.rb index 1709500..8b8b5d5 100644 --- a/lib/oxidized/model/isam.rb +++ b/lib/oxidized/model/isam.rb @@ -1,6 +1,6 @@ class ISAM < Oxidized::Model - #Alcatel ISAM 7302/7330 FTTN - + # Alcatel ISAM 7302/7330 FTTN + prompt /^([\w.:@-]+>#\s)$/ comment '# ' @@ -8,7 +8,7 @@ class ISAM < Oxidized::Model cfg.each_line.to_a[1..-2].join end - cfg :telnet do + cfg :telnet do username /^login:\s*/ password /^password:\s*/ end @@ -19,7 +19,7 @@ class ISAM < Oxidized::Model post_login 'environment inhibit-alarms print no-more' pre_logout 'logout' end - + cmd 'show software-mngt oswp detail' do |cfg| comment cfg end @@ -31,5 +31,4 @@ class ISAM < Oxidized::Model cmd 'info configure flat' do |cfg| cfg end - end diff --git a/lib/oxidized/model/junos.rb b/lib/oxidized/model/junos.rb index 058e3cf..737a000 100644 --- a/lib/oxidized/model/junos.rb +++ b/lib/oxidized/model/junos.rb @@ -1,6 +1,5 @@ class JunOS < Oxidized::Model - - comment '# ' + comment '# ' def telnet @input.class.to_s.match(/Telnet/) @@ -8,11 +7,15 @@ class JunOS < Oxidized::Model cmd :all do |cfg| cfg = cfg.lines.to_a[1..-2].join if screenscrape + cfg.gsub!(/ scale-subscriber (\s+)(\d+)/, ' scale-subscriber <count>') cfg.lines.map { |line| line.rstrip }.join("\n") + "\n" end cmd :secret do |cfg| - cfg.gsub!(/encrypted-password (\S+).*/, '<secret removed>') + cfg.gsub!(/encrypted-password (\S+).*/, 'encrypted-password <secret removed>') + cfg.gsub!(/pre-shared-key ascii-text (\S+).*/, 'pre-shared-key ascii-text <secret removed>') + cfg.gsub!(/pre-shared-key hexadecimal (\S+).*/, 'pre-shared-key hexadecimal <secret removed>') + cfg.gsub!(/authentication-key (\S+).*/, 'authentication-key <secret removed>') cfg.gsub!(/community (\S+) {/, 'community <hidden> {') cfg end @@ -28,7 +31,7 @@ class JunOS < Oxidized::Model out = '' case @model when 'mx960' - out << cmd('show chassis fabric reachability') { |cfg| comment cfg } + out << cmd('show chassis fabric reachability') { |cfg| comment cfg } when /^(ex22|ex33|ex4|ex8|qfx)/ out << cmd('show virtual-chassis') { |cfg| comment cfg } end @@ -36,6 +39,8 @@ class JunOS < Oxidized::Model end cmd('show chassis hardware') { |cfg| comment cfg } + cmd('show system license') { |cfg| comment cfg } + cmd('show system license keys') { |cfg| comment cfg } cfg :telnet do username(/^login:/) @@ -43,7 +48,7 @@ class JunOS < Oxidized::Model end cfg :ssh do - exec true # don't run shell, run each command in exec channel + exec true # don't run shell, run each command in exec channel end cfg :telnet, :ssh do @@ -51,5 +56,4 @@ class JunOS < Oxidized::Model post_login 'set cli screen-width 0' pre_logout 'exit' end - end diff --git a/lib/oxidized/model/masteros.rb b/lib/oxidized/model/masteros.rb index 587fdc4..c03beb1 100644 --- a/lib/oxidized/model/masteros.rb +++ b/lib/oxidized/model/masteros.rb @@ -1,8 +1,7 @@ class MasterOS < Oxidized::Model - # MRV MasterOS model # -comment '!' + comment '!' cmd :secret do |cfg| cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' @@ -43,5 +42,4 @@ comment '!' end pre_logout 'exit' end - end diff --git a/lib/oxidized/model/mlnxos.rb b/lib/oxidized/model/mlnxos.rb index 9542c88..990417d 100644 --- a/lib/oxidized/model/mlnxos.rb +++ b/lib/oxidized/model/mlnxos.rb @@ -1,8 +1,7 @@ class MLNXOS < Oxidized::Model - prompt /([\w.@()-\[:\s\]]+[#>]\s)$/ - comment '## ' - + comment '## ' + # Pager Handling expect /.+lines\s\d+\-\d+([\s]|\/\d+\s\(END\)\s).+$/ do |data, re| send ' ' @@ -11,8 +10,12 @@ class MLNXOS < Oxidized::Model cmd :all do |cfg| cfg.gsub! /\[\?1h=\r/, '' # Pager Handling - cfg.gsub! /\r\[K/,'' # Pager Handling + cfg.gsub! /\r\[K/, '' # Pager Handling cfg.gsub! /\s/, '' # Linebreak Handling + cfg.gsub! /^CPU\ load\ averages\:\s.+/, '' # Omit constantly changing CPU info + cfg.gsub! /^System\ memory\:\s.+/, '' # Omit constantly changing memory info + cfg.gsub! /^Uptime\:\s.+/, '' # Omit constantly changing uptime info + cfg.gsub! /.+Generated\ at\s\d+.+/, '' # Omit constantly changing generation time info cfg = cfg.lines.to_a[2..-3].join end @@ -38,6 +41,6 @@ class MLNXOS < Oxidized::Model cfg :ssh do password /^Password:\s*/ - pre_logout 'exit' + pre_logout "\nexit" end end diff --git a/lib/oxidized/model/model.rb b/lib/oxidized/model/model.rb index a2a71cb..438357f 100644 --- a/lib/oxidized/model/model.rb +++ b/lib/oxidized/model/model.rb @@ -7,29 +7,34 @@ module Oxidized class << self def inherited klass - klass.instance_variable_set '@cmd', Hash.new { |h,k| h[k] = [] } - klass.instance_variable_set '@cfg', Hash.new { |h,k| h[k] = [] } - klass.instance_variable_set '@procs', Hash.new { |h,k| h[k] = [] } + klass.instance_variable_set '@cmd', Hash.new { |h, k| h[k] = [] } + klass.instance_variable_set '@cfg', Hash.new { |h, k| h[k] = [] } + klass.instance_variable_set '@procs', Hash.new { |h, k| h[k] = [] } klass.instance_variable_set '@expect', [] klass.instance_variable_set '@comment', nil klass.instance_variable_set '@prompt', nil end - def comment _comment='# ' + + def comment _comment = '# ' return @comment if @comment @comment = block_given? ? yield : _comment end - def prompt _prompt=nil + + def prompt _prompt = nil @prompt or @prompt = _prompt end + def cfg *methods, &block [methods].flatten.each do |method| @cfg[method.to_s] << block end end + def cfgs @cfg end - def cmd _cmd=nil, &block + + def cmd _cmd = nil, &block if _cmd.class == Symbol @cmd[_cmd] << block else @@ -37,12 +42,15 @@ module Oxidized end Oxidized.logger.debug "lib/oxidized/model/model.rb Added #{_cmd} to the commands list" end + def cmds @cmd end + def expect re, &block @expect << [re, block] end + def expects @expect end @@ -83,6 +91,7 @@ module Oxidized Oxidized.logger.debug "lib/oxidized/model/model.rb Executing #{string}" out = @input.cmd(string) return false unless out + out = out.b unless Oxidized.config.input.utf8_encoded? self.class.cmds[:all].each do |all_block| out = instance_exec Oxidized::String.new(out), string, &all_block end @@ -166,6 +175,5 @@ module Oxidized output.set_cmd(name) output end - end end diff --git a/lib/oxidized/model/mtrlrfs.rb b/lib/oxidized/model/mtrlrfs.rb index 8baa4e9..31b4f22 100644 --- a/lib/oxidized/model/mtrlrfs.rb +++ b/lib/oxidized/model/mtrlrfs.rb @@ -1,5 +1,4 @@ class Mtrlrfs < Oxidized::Model - # Motorola RFS/Extreme WM comment '# ' @@ -7,7 +6,7 @@ class Mtrlrfs < Oxidized::Model cmd :all do |cfg| # xos inserts leading \r characters and other trailing white space. # this deletes extraneous \r and trailing white space. - cfg.each_line.to_a[1..-2].map{|line|line.delete("\r").rstrip}.join("\n") + "\n" + cfg.each_line.to_a[1..-2].map { |line| line.delete("\r").rstrip }.join("\n") + "\n" end cmd 'show version' do |cfg| @@ -32,6 +31,4 @@ class Mtrlrfs < Oxidized::Model send "n\n" end end - end - diff --git a/lib/oxidized/model/ndms.rb b/lib/oxidized/model/ndms.rb new file mode 100644 index 0000000..1947f91 --- /dev/null +++ b/lib/oxidized/model/ndms.rb @@ -0,0 +1,24 @@ +class NDMS < Oxidized::Model + # Pull config from Zyxel Keenetic devices from version NDMS >= 2.0 + + comment '! ' + + prompt /^([\w.@()-]+[#>]\s?)/m + + cmd 'show version' do |cfg| + cfg = cfg.each_line.to_a[1..-3].join + comment cfg + end + + cmd 'show running-config' do |cfg| + cfg = cfg.each_line.to_a[1..-2] + cfg = cfg.reject { |line| line.match /(clock date|checksum)/ }.join + cfg + end + + cfg :telnet do + username /^Login:/ + password /^Password:/ + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/netgear.rb b/lib/oxidized/model/netgear.rb new file mode 100644 index 0000000..40b0924 --- /dev/null +++ b/lib/oxidized/model/netgear.rb @@ -0,0 +1,39 @@ +class Netgear < Oxidized::Model + comment '!' + prompt /^(\([\w\s\-.]+\)\s[#>])$/ + + cmd :secret do |cfg| + cfg.gsub!(/password (\S+)/, 'password <hidden>') + cfg.gsub!(/encrypted (\S+)/, 'encrypted <hidden>') + cfg + end + + cfg :telnet do + username /^(User:|Applying Interface configuration, please wait ...)/ + end + + cfg :telnet, :ssh do + if vars :enable + post_login do + send "enable\n" + # Interpret enable: true as meaning we won't be prompted for a password + unless vars(:enable).is_a? TrueClass + expect /[pP]assword:\s?$/ + send vars(:enable) + "\n" + end + expect /^.+[#]$/ + end + end + post_login 'terminal length 0' + # quit / logout will sometimes prompt the user: + # + # The system has unsaved changes. + # Would you like to save them now? (y/n) + # + # So it is safer simply to disconnect and not issue a pre_logout command + end + + cmd 'show running-config' do |cfg| + cfg.gsub! /^(!.*Time).*$/, '\1' + end +end diff --git a/lib/oxidized/model/netscaler.rb b/lib/oxidized/model/netscaler.rb index 9ca66b6..00d1b71 100644 --- a/lib/oxidized/model/netscaler.rb +++ b/lib/oxidized/model/netscaler.rb @@ -1,5 +1,4 @@ class NetScaler < Oxidized::Model - prompt /^\>\s*$/ comment '# ' @@ -20,5 +19,4 @@ class NetScaler < Oxidized::Model cfg :ssh do pre_logout 'exit' end - end diff --git a/lib/oxidized/model/nos.rb b/lib/oxidized/model/nos.rb index ec7c818..4049aa5 100644 --- a/lib/oxidized/model/nos.rb +++ b/lib/oxidized/model/nos.rb @@ -1,5 +1,4 @@ class NOS < Oxidized::Model - # Brocade Network Operating System prompt /^(?:\e\[..h)?[\w.-]+# $/ @@ -38,8 +37,7 @@ class NOS < Oxidized::Model cfg :telnet, :ssh do post_login 'terminal length 0' - #post_login 'terminal width 0' + # post_login 'terminal width 0' pre_logout 'exit' end - end diff --git a/lib/oxidized/model/nxos.rb b/lib/oxidized/model/nxos.rb index fbe772d..d1449dd 100644 --- a/lib/oxidized/model/nxos.rb +++ b/lib/oxidized/model/nxos.rb @@ -1,8 +1,15 @@ class NXOS < Oxidized::Model - prompt /^(\r?[\w.@_()-]+[#]\s?)$/ comment '! ' + cmd :secret do |cfg| + cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' + cfg.gsub! /^(snmp-server user (\S+) (\S+) auth (\S+)) (\S+) (priv) (\S+)/, '\\1 <configuration removed> ' + cfg.gsub! /^(username \S+ password \d) (\S+)/, '\\1 <secret hidden>' + cfg.gsub! /^(radius-server key).*/, '\\1 <secret hidden>' + cfg + end + cmd 'show version' do |cfg| cfg = cfg.each_line.take_while { |line| not line.match(/uptime/i) } comment cfg.join "" @@ -10,10 +17,11 @@ class NXOS < Oxidized::Model cmd 'show inventory' do |cfg| comment cfg - end + end cmd 'show running-config' do |cfg| cfg.gsub! /^!Time:[^\n]*\n/, '' + cfg end cfg :ssh, :telnet do diff --git a/lib/oxidized/model/oneos.rb b/lib/oxidized/model/oneos.rb new file mode 100644 index 0000000..35332c8 --- /dev/null +++ b/lib/oxidized/model/oneos.rb @@ -0,0 +1,56 @@ +class OneOS < Oxidized::Model + prompt /^([\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 set-read-community ").*+?(".*)$/, '\\1<secret hidden>\\2' + cfg + end + + cmd 'show version' do |cfg| + comment cfg + end + + cmd 'show running-config' do |cfg| + cfg = cfg.each_line.to_a[0..-1].join + cfg.gsub! /^Building configuration...\s*[^\n]*\n/, '' + cfg.gsub! /^Current configuration :\s*[^\n]*\n/, '' + cfg + end + + cfg :telnet do + username /^Username:/ + password /^Password:/ + end + + cfg :telnet, :ssh do + # preferred way to handle additional passwords + if vars :enable + post_login do + send "enable\n" + cmd vars(:enable) + end + end + post_login 'term len 0' + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/opengear.rb b/lib/oxidized/model/opengear.rb index 7f801f8..e0d4c0a 100644 --- a/lib/oxidized/model/opengear.rb +++ b/lib/oxidized/model/opengear.rb @@ -1,6 +1,7 @@ class OpenGear < Oxidized::Model + comment '# ' - comment '# ' + prompt /^(\$\s)?$/ cmd :secret do |cfg| cfg.gsub!(/password (\S+)/, 'password <secret removed>') @@ -13,7 +14,6 @@ class OpenGear < Oxidized::Model cmd 'config -g config' cfg :ssh do - exec true # don't run shell, run each command in exec channel + exec true # don't run shell, run each command in exec channel end - end diff --git a/lib/oxidized/model/opnsense.rb b/lib/oxidized/model/opnsense.rb new file mode 100644 index 0000000..a8f7a47 --- /dev/null +++ b/lib/oxidized/model/opnsense.rb @@ -0,0 +1,19 @@ +class OpnSense < Oxidized::Model + # minimum required permissions: "System: Shell account access" + # must enable SSH and password-based SSH access + + cmd :all do |cfg| + cfg.each_line.to_a[1..-1].join + end + + cmd 'cat /conf/config.xml' do |cfg| + cfg.gsub! /\s<revision>\s*<time>\d*<\/time>\s*.*\s*.*\s*<\/revision>/, '' + cfg.gsub! /\s<last_rule_upd_time>\d*<\/last_rule_upd_time>/, '' + cfg + end + + cfg :ssh do + exec true + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/outputs.rb b/lib/oxidized/model/outputs.rb index a668e9d..23a37f5 100644 --- a/lib/oxidized/model/outputs.rb +++ b/lib/oxidized/model/outputs.rb @@ -1,7 +1,6 @@ module Oxidized class Model class Outputs - def to_cfg type_to_str(nil) end @@ -23,7 +22,7 @@ module Oxidized end def type type - @outputs.select { |out| out.type==type } + @outputs.select { |out| out.type == type } end def types @@ -35,7 +34,6 @@ module Oxidized def initialize @outputs = [] end - end end end diff --git a/lib/oxidized/model/panos.rb b/lib/oxidized/model/panos.rb index 68d80c3..422a9fe 100644 --- a/lib/oxidized/model/panos.rb +++ b/lib/oxidized/model/panos.rb @@ -1,8 +1,7 @@ class PanOS < Oxidized::Model - # PaloAlto PAN-OS model # - comment '! ' + comment '! ' prompt /^[\w.\@:\(\)-]+>\s?$/ @@ -28,6 +27,6 @@ class PanOS < Oxidized::Model cfg :ssh do post_login 'set cli pager off' - pre_logout 'exit' + pre_logout 'quit' end end diff --git a/lib/oxidized/model/pfsense.rb b/lib/oxidized/model/pfsense.rb index c02c0d0..278a126 100644 --- a/lib/oxidized/model/pfsense.rb +++ b/lib/oxidized/model/pfsense.rb @@ -1,19 +1,18 @@ class PfSense < Oxidized::Model - # 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 + cfg.each_line.to_a[1..-1].join end - + cmd 'cat /cf/conf/config.xml' do |cfg| - cfg.gsub! /\s<revision>\s*.*\s*<time>\d*<\/time>\s*.*\s*<\/revision>/, '' + cfg.gsub! /\s<revision>\s*<time>\d*<\/time>\s*.*\s*.*\s*<\/revision>/, '' + cfg.gsub! /\s<last_rule_upd_time>\d*<\/last_rule_upd_time>/, '' cfg end - + cfg :ssh do exec true pre_logout 'exit' end - end diff --git a/lib/oxidized/model/planet.rb b/lib/oxidized/model/planet.rb index 05a369a..56e688c 100644 --- a/lib/oxidized/model/planet.rb +++ b/lib/oxidized/model/planet.rb @@ -1,24 +1,23 @@ class Planet < Oxidized::Model - prompt /^\r?([\w.@()-]+[#>]\s?)$/ comment '! ' # example how to handle pager - #expect /^\s--More--\s+.*$/ do |data, re| + # expect /^\s--More--\s+.*$/ do |data, re| # send ' ' # data.sub re, '' - #end + # end # non-preferred way to handle additional PW prompt - #expect /^[\w.]+>$/ do |data| + # expect /^[\w.]+>$/ do |data| # send "enable\n" # send vars(:enable) + "\n" # data - #end + # end cmd :all do |cfg| - #cfg.gsub! /\cH+\s{8}/, '' # example how to handle pager - #cfg.gsub! /\cH+/, '' # example how to handle pager + # 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 @@ -39,20 +38,20 @@ class Planet < Oxidized::Model cfg = cfg.each_line.to_a[0...-2] - # Strip system time and system uptime from planet gs switches + # Strip system (up)time and temperature cfg = cfg.reject { |line| line.match /System Time\s*:.*/ } cfg = cfg.reject { |line| line.match /System Uptime\s*:.*/ } + cfg = cfg.reject { |line| line.match /Temperature\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 @@ -61,7 +60,6 @@ class Planet < Oxidized::Model cfg.join end - cfg :telnet do username /^Username:/ @@ -79,5 +77,4 @@ class Planet < Oxidized::Model end pre_logout 'exit' end - end diff --git a/lib/oxidized/model/powerconnect.rb b/lib/oxidized/model/powerconnect.rb index ac36c26..bf36f65 100644 --- a/lib/oxidized/model/powerconnect.rb +++ b/lib/oxidized/model/powerconnect.rb @@ -1,23 +1,27 @@ class PowerConnect < Oxidized::Model - prompt /^([\w\s.@-]+[#>]\s?)$/ # allow spaces in hostname..dell does not limit it.. # - comment '! ' + comment '! ' expect /^\s*--More--\s+.*$/ do |data, re| - send ' ' - data.sub re, '' + send ' ' + data.sub re, '' end cmd :all do |cfg| cfg.each_line.to_a[1..-3].join end + cmd :secret do |cfg| + cfg.gsub! /^(username \S+ password (?:encrypted )?)\S+(.*)/, '\1<hidden>\2' + cfg + end + cmd 'show version' do |cfg| if (@stackable.nil?) @stackable = true if cfg.match /(U|u)nit\s/ end - cfg = cfg.split("\n").select { |line| not line[/Up\sTime/] } + cfg = cfg.split("\n").reject { |line| line[/Up\sTime/] } comment cfg.join("\n") + "\n" end @@ -47,7 +51,6 @@ class PowerConnect < Oxidized::Model post_login "terminal length 0" pre_logout "logout" pre_logout "exit" - end def clean cfg @@ -67,8 +70,8 @@ class PowerConnect < Oxidized::Model end out << line.strip end + out = out.reject { |line| line[/Up\sTime/] } out = comment out.join "\n" out << "\n" end - end diff --git a/lib/oxidized/model/procurve.rb b/lib/oxidized/model/procurve.rb index c117df3..930dc53 100644 --- a/lib/oxidized/model/procurve.rb +++ b/lib/oxidized/model/procurve.rb @@ -1,10 +1,14 @@ class Procurve < Oxidized::Model - - # some models start lines with \r + # some models start lines with \r # previous command is repeated followed by "\eE", which sometimes ends up on last line - prompt /^\r?([\w -]+\eE)?([\w.-]+# )$/ + prompt /^\r?([\w.-]+# )$/ + + comment '! ' - comment '! ' + # replace next line control sequence with a new line + expect /(\e\[1M\e\[\??\d+(;\d+)*[A-Za-z]\e\[1L)|(\eE)/ do |data, re| + data.gsub re, "\n" + end # replace all used vt100 control sequences expect /\e\[\??\d+(;\d+)*[A-Za-z]/ do |data, re| @@ -16,8 +20,13 @@ class Procurve < Oxidized::Model "" end + expect /Enter switch number/ do + send "\n" + "" + end + cmd :all do |cfg| - cfg = cfg.each_line.to_a[1..-3].join + cfg = cfg.each_line.to_a[1..-2].join cfg = cfg.gsub /^\r/, '' end @@ -25,6 +34,7 @@ class Procurve < Oxidized::Model cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' cfg.gsub! /^(snmp-server host).*/, '\\1 <configuration removed>' cfg.gsub! /^(radius-server host).*/, '\\1 <configuration removed>' + cfg.gsub! /^(radius-server key).*/, '\\1 <configuration removed>' cfg end @@ -32,6 +42,22 @@ class Procurve < Oxidized::Model comment cfg end + cmd 'show modules' do |cfg| + comment cfg + end + + cmd 'show system power-supply' do |cfg| + comment cfg + end + + cmd 'show interfaces transceiver' do |cfg| + comment cfg + end + + cmd 'show flash' do |cfg| + comment cfg + end + # not supported on all models cmd 'show system-information' do |cfg| cfg = cfg.split("\n")[0..-8].join("\n") @@ -40,7 +66,7 @@ class Procurve < Oxidized::Model # not supported on all models cmd 'show system information' do |cfg| - cfg = cfg.each_line.select { |line| not line.match /(.*CPU.*)|(.*Up Time.*)|(.*Total.*)|(.*Free.*)|(.*Lowest.*)|(.*Missed.*)/ } + cfg = cfg.each_line.reject { |line| line.match /(.*CPU.*)|(.*Up Time.*)|(.*Total.*)|(.*Free.*)|(.*Lowest.*)|(.*Missed.*)/ } cfg = cfg.join comment cfg end @@ -60,5 +86,4 @@ class Procurve < Oxidized::Model cfg :ssh do pty_options({ chars_wide: 1000 }) end - end diff --git a/lib/oxidized/model/quantaos.rb b/lib/oxidized/model/quantaos.rb index 274440d..8dbdf3b 100644 --- a/lib/oxidized/model/quantaos.rb +++ b/lib/oxidized/model/quantaos.rb @@ -1,18 +1,17 @@ class QuantaOS < Oxidized::Model - prompt /^\((\w|\S)+\) (>|#)$/ comment '! ' - + cmd 'show run' do |cfg| cfg.each_line.select do |line| not line.match /^!.*$/ and - not line.match /^\((\w|\S)+\) (>|#)$/ and - not line.match /^show run$/ + not line.match /^\((\w|\S)+\) (>|#)$/ and + not line.match /^show run$/ end.join end cfg :telnet do - username /^Username:/ + username /^User(name)?:/ password /^Password:/ end @@ -31,5 +30,4 @@ class QuantaOS < Oxidized::Model send "n\n" end end - end diff --git a/lib/oxidized/model/routeros.rb b/lib/oxidized/model/routeros.rb index a92ad5e..c729126 100644 --- a/lib/oxidized/model/routeros.rb +++ b/lib/oxidized/model/routeros.rb @@ -6,11 +6,23 @@ class RouterOS < Oxidized::Model comment cfg end - 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" + cmd '/system package update print' do |cfg| + comment cfg + end + + cmd '/system history print' do |cfg| + comment cfg + end + + post do + run_cmd = vars(:remove_secret) ? '/export hide-sensitive' : '/export' + cmd run_cmd 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.gsub! /# inactive time\r\n/, '' # Remove time based system comment + cfg = cfg.split("\n").reject { |line| line[/^\#\s\w{3}\/\d{2}\/\d{4}.*$/] } + cfg.join("\n") + "\n" + end end cfg :telnet do @@ -18,6 +30,10 @@ class RouterOS < Oxidized::Model password /^Password:/ end + cfg :telnet, :ssh do + pre_logout 'quit' + end + cfg :ssh do exec true end diff --git a/lib/oxidized/model/saos.rb b/lib/oxidized/model/saos.rb index 5d460cf..97547b5 100644 --- a/lib/oxidized/model/saos.rb +++ b/lib/oxidized/model/saos.rb @@ -1,8 +1,7 @@ class SAOS < Oxidized::Model - # Ciena SAOS switch # used for 6.x devices - + comment '! ' cmd :all do |cfg| @@ -10,6 +9,8 @@ class SAOS < Oxidized::Model end cmd 'configuration show' do |cfg| + cfg.gsub! /^! Created: [^\n]*\n/, '' + cfg.gsub! /^! On terminal: [^\n]*\n/, '' cfg end diff --git a/lib/oxidized/model/screenos.rb b/lib/oxidized/model/screenos.rb index 0258898..9b52f9f 100644 --- a/lib/oxidized/model/screenos.rb +++ b/lib/oxidized/model/screenos.rb @@ -1,8 +1,7 @@ -class ScreenOS < Oxidized::Model - +class ScreenOS < Oxidized::Model # Netscreen ScreenOS model # - comment '! ' + comment '! ' prompt /^[\w.:\(\)-]+->\s?$/ @@ -10,7 +9,7 @@ class ScreenOS < Oxidized::Model cfg.each_line.to_a[2..-2].join end - cmd :secret do |cfg| + cmd :secret do |cfg| cfg.gsub! /^(set admin name) .*|^(set admin password) .*/, '\\1 <removed>' cfg.gsub! /^(set admin user .* password) .* (.*)/, '\\1 <removed> \\2' cfg.gsub! /(secret|password|preshare) .*/, '\\1 <secret hidden>' @@ -41,5 +40,4 @@ class ScreenOS < Oxidized::Model send "n" end end - end diff --git a/lib/oxidized/model/sgos.rb b/lib/oxidized/model/sgos.rb new file mode 100644 index 0000000..894e042 --- /dev/null +++ b/lib/oxidized/model/sgos.rb @@ -0,0 +1,45 @@ +class SGOS < Oxidized::Model + comment '!- ' + prompt /\w+>|#/ + + expect /--More--/ do |data, re| + send ' ' + data.sub re, '' + end + + cmd :all do |cfg| + cfg.each_line.to_a[1..-3].join + end + + cmd 'show licenses' do |cfg| + comment cfg + end + + cmd 'show general' do |cfg| + comment cfg + end + + cmd :secret do |cfg| + cfg.gsub! /^(security hashed-enable-password).*/, '\\1 <secret hidden>' + cfg.gsub! /^(security hashed-password).*/, '\\1 <secret hidden>' + cfg + end + + cmd 'show configuration expanded noprompts with-keyrings unencrypted' do |cfg| + cfg.gsub! /^(!- Local time).*/, "" + cfg.gsub! /^(archive-configuration encrypted-password).*/, "" + cfg.gsub! /^(download encrypted-password).*/, "" + cfg + end + + cfg :telnet, :ssh do + # 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/siklu.rb b/lib/oxidized/model/siklu.rb new file mode 100644 index 0000000..2203bda --- /dev/null +++ b/lib/oxidized/model/siklu.rb @@ -0,0 +1,17 @@ +class Siklu < Oxidized::Model + # Siklu EtherHaul # + + prompt /^[\w-]+>$/ + + cmd 'copy startup-configuration display' do |cfg| + cfg.each_line.to_a[2..2].join + end + + cmd 'copy running-configuration display' do |cfg| + cfg.each_line.to_a[3..-2].join + end + + cfg :ssh do + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/slxos.rb b/lib/oxidized/model/slxos.rb new file mode 100644 index 0000000..1afc8b1 --- /dev/null +++ b/lib/oxidized/model/slxos.rb @@ -0,0 +1,59 @@ +class SLXOS < Oxidized::Model + prompt /^.*[>#]\s?$/i + comment '! ' + + cmd 'show version' do |cfg| + cfg.gsub! /(^((.*)[Ss]ystem [Uu]ptime(.*))$)/, '' # remove unwanted line system uptime + cfg.gsub! /[Uu]p\s?[Tt]ime is .*/, '' + + comment cfg + end + + cmd 'show chassis' do |cfg| + cfg.encode!("UTF-8", :invalid => :replace, :undef => :replace) # sometimes ironware returns broken encoding + cfg.gsub! /.*Power Usage.*/, '' # remove unwanted lines power usage + cfg.gsub! /Time A(live|wake).*/, '' # remove unwanted lines time alive/awake + cfg.gsub! /([\[]*)1([\]]*)<->([\[]*)2([\]]*)(<->([\[]*)3([\]]*))*/, '' + + comment cfg + end + + cmd 'show system' do |cfg| + cfg.gsub! /Up Time.*/, '' # removes uptime line + cfg.gsub! /Current Time.*/, '' # remove current time line + cfg.gsub! /.*speed is.*/, '' # removes fan speed lines + + comment cfg + end + + cmd 'show slots' do |cfg| + cfg.gsub! /^-*^$/, '' # some slx devices are fixed config + cfg.gsub! /syntax error: element does not exist/, '' # same as above + + comment cfg + end + + cmd 'show running-config' do |cfg| + arr = cfg.each_line.to_a + arr[2..-1].join unless arr.length < 2 + end + + cfg :telnet do + # match expected prompts + username /^(Please Enter Login Name|Username):/ + password /^(Please Enter Password ?|Password):/ + end + + # handle pager with enable + cfg :telnet, :ssh do + if vars :enable + post_login do + send "enable\n" + cmd vars(:enable) + end + end + post_login '' + post_login 'terminal length 0' + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/sros.rb b/lib/oxidized/model/sros.rb new file mode 100644 index 0000000..d809147 --- /dev/null +++ b/lib/oxidized/model/sros.rb @@ -0,0 +1,117 @@ +class SROS < Oxidized::Model + # + # Nokia SR OS (TiMOS) (formerly TiMetra, Alcatel, Alcatel-Lucent). + # Used in 7705 SAR, 7210 SAS, 7450 ESS, 7750 SR, 7950 XRS, and NSP. + # + + comment '# ' + + prompt /^([-\w\.:>\*]+\s?[#>]\s?)$/ + + cmd :all do |cfg, cmdstring| + new_cfg = comment "COMMAND: #{cmdstring}\n" + new_cfg << cfg.each_line.to_a[1..-2].join + end + + # + # Show the boot options file. + # + cmd 'show bof' do |cfg| + cfg.gsub! /# Finished .*/, '' + cfg.gsub! /# Generated .*/, '' + comment cfg + end + + # + # Show the system information. + # + cmd 'show system information' do |cfg| + # + # Strip uptime. + # + cfg.sub! /^System Up Time.*\n/, '' + cfg.gsub! /# Finished .*/, '' + cfg.gsub! /# Generated .*/, '' + comment cfg + end + + # + # Show the card state. + # + cmd 'show card state' do |cfg| + cfg.gsub! /# Finished .*/, '' + cfg.gsub! /# Generated .*/, '' + comment cfg + end + + # + # Show the boot log. + # + cmd 'file type bootlog.txt' do |cfg| + # + # Strip carriage returns and backspaces. + # + cfg.gsub! /\r/, '' + cfg.gsub! /[\b][\b][\b]/, "\n" + cfg.gsub! /# Finished .*/, '' + cfg.gsub! /# Generated .*/, '' + comment cfg + end + + # + # Show the running debug configuration. + # + cmd 'show debug' do |cfg| + cfg.gsub! /# Finished .*/, '' + cfg.gsub! /# Generated .*/, '' + comment cfg + end + + # + # Show the saved debug configuration (admin debug-save). + # + cmd 'file type config.dbg' do |cfg| + # + # Strip carriage returns. + # + cfg.gsub! /\r/, '' + cfg.gsub! /# Finished .*/, '' + cfg.gsub! /# Generated .*/, '' + comment cfg + end + + # + # Show the running persistent indices. + # + cmd 'admin display-config index' do |cfg| + # + # Strip carriage returns. + # + cfg.gsub! /\r/, '' + cfg.gsub! /# Finished .*/, '' + cfg.gsub! /# Generated .*/, '' + comment cfg + end + + # + # Show the running configuration. + # + cmd 'admin display-config' do |cfg| + # + # Strip carriage returns. + # + cfg.gsub! /\r/, '' + cfg.gsub! /# Finished .*/, '' + cfg.gsub! /# Generated .*/, '' + end + + cfg :telnet do + username /^Login: / + password /^Password: / + end + + cfg :telnet, :ssh do + post_login 'environment no more' + pre_logout 'logout' + end +end diff --git a/lib/oxidized/model/supermicro.rb b/lib/oxidized/model/supermicro.rb index 361244c..56d5ef6 100644 --- a/lib/oxidized/model/supermicro.rb +++ b/lib/oxidized/model/supermicro.rb @@ -1,45 +1,10 @@ -class Supermicro < Oxidized::Model - comment '! ' +# Backward compatibility shim for deprecated model `supermicro`. +# Migrate your source from `supermicro` to `edgecos`. - cmd :secret do |cfg| - cfg.gsub!(/password \d+ (\S+).*/, '<secret removed>') - cfg.gsub!(/community (\S+)/, 'community <hidden>') - cfg - end +require_relative 'edgecos.rb' - cmd :all do |cfg| - cfg.each_line.to_a[2..-2].join - end +Supermicro = EdgeCOS - cmd 'show running-config' +Oxidized.logger.warn "Using deprecated model supermicro, use edgecos instead." - cmd 'show access-list tcam-utilization' do |cfg| - comment cfg - end - - cmd 'show memory' do |cfg| - comment cfg - end - - cmd 'show system' do |cfg| - comment cfg - end - - cmd 'show version' do |cfg| - comment cfg - end - - cmd 'show watchdog' do |cfg| - comment cfg - end - - cfg :telnet do - username /^Username:/ - password /^Password:/ - end - - cfg :telnet, :ssh do - post_login 'terminal length 0' - pre_logout 'exit' - end -end
\ No newline at end of file +# Deprecated diff --git a/lib/oxidized/model/timos.rb b/lib/oxidized/model/timos.rb index d40e845..e454630 100644 --- a/lib/oxidized/model/timos.rb +++ b/lib/oxidized/model/timos.rb @@ -1,102 +1,10 @@ -class TiMOS < Oxidized::Model +# Backward compatibility shim for deprecated model `timos`. +# Migrate your source from `timos` to `sros`. - # - # Nokia SR OS (TiMOS) (formerly TiMetra, Alcatel, Alcatel-Lucent). - # Used in 7705 SAR, 7210 SAS, 7450 ESS, 7750 SR, 7950 XRS, and NSP. - # +require_relative 'sros.rb' - comment '# ' +TiMOS = SROS - prompt /^([-\w\.:>\*]+\s?[#>]\s?)$/ +Oxidized.logger.warn "Using deprecated model timos, use sros instead." - cmd :all do |cfg, cmdstring| - new_cfg = comment "COMMAND: #{cmdstring}\n" - new_cfg << cfg.each_line.to_a[1..-2].join - end - - # - # Show the boot options file. - # - cmd 'show bof' do |cfg| - comment cfg - end - - # - # Show the system information. - # - cmd 'show system information' do |cfg| - # - # Strip uptime. - # - cfg.sub! /^System Up Time.*\n/, '' - comment cfg - end - - # - # Show the card state. - # - cmd 'show card state' do |cfg| - comment cfg - end - - # - # Show the boot log. - # - cmd 'file type bootlog.txt' do |cfg| - # - # Strip carriage returns and backspaces. - # - cfg.gsub! /\r/, '' - cfg.gsub! /[\b][\b][\b]/, "\n" - comment cfg - end - - # - # Show the running debug configuration. - # - cmd 'show debug' do |cfg| - comment cfg - end - - # - # Show the saved debug configuration (admin debug-save). - # - cmd 'file type config.dbg' do |cfg| - # - # Strip carriage returns. - # - cfg.gsub! /\r/, '' - comment cfg - end - - # - # Show the running persistent indices. - # - cmd 'admin display-config index' do |cfg| - # - # Strip carriage returns. - # - cfg.gsub! /\r/, '' - comment cfg - end - - # - # Show the running configuration. - # - cmd 'admin display-config' do |cfg| - # - # Strip carriage returns. - # - cfg.gsub! /\r/, '' - end - - cfg :telnet do - username /^Login: / - password /^Password: / - end - - cfg :telnet, :ssh do - post_login 'environment no more' - pre_logout 'logout' - end -end +# Deprecated diff --git a/lib/oxidized/model/tmos.rb b/lib/oxidized/model/tmos.rb index 390046d..69b0e1b 100644 --- a/lib/oxidized/model/tmos.rb +++ b/lib/oxidized/model/tmos.rb @@ -1,5 +1,4 @@ class TMOS < Oxidized::Model - comment '# ' cmd :secret do |cfg| @@ -46,7 +45,6 @@ class TMOS < Oxidized::Model cmd('cat /config/partitions/*/bigip.conf') { |cfg| comment cfg } cfg :ssh do - exec true # don't run shell, run each command in exec channel + exec true # don't run shell, run each command in exec channel end - end diff --git a/lib/oxidized/model/tplink.rb b/lib/oxidized/model/tplink.rb new file mode 100644 index 0000000..2a61fa5 --- /dev/null +++ b/lib/oxidized/model/tplink.rb @@ -0,0 +1,61 @@ +class TPLink < Oxidized::Model + # tp-link prompt + prompt /^\r?([\w.@()-]+[#>]\s?)$/ + comment '! ' + + # handle paging + # workaround for sometimes missing whitespaces with "\s?" + expect /Press\s?any\s?key\s?to\s?continue\s?\(Q\s?to\s?quit\)/ do |data, re| + send ' ' + data.sub re, '' + end + + # send carriage return because \n with the command is not enough + # checks if line ends with prompt >,# or \r,\nm otherwise send \r + expect /[^>#\r\n]$/ do |data, re| + send "\r" + data.sub re, '' + end + + cmd :all do |cfg| + # normalize linefeeds + cfg.gsub! /(\r|\r\n|\n\r)/, "\n" + # remove empty lines + cfg.each_line.reject { |line| line.match /^[\r\n\s\u0000#]+$/ }.join + end + + cmd :secret do |cfg| + cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' + cfg.gsub! /secret (\d+) (\S+).*/, '<secret hidden>' + cfg + end + + cmd 'show system-info' do |cfg| + comment cfg.each_line.to_a[3..-3].join + end + + cmd 'show running-config' do |cfg| + lines = cfg.each_line.to_a[1..-1] + # cut config after "end" + lines[0..lines.index("end\n")].join + end + + cfg :telnet, :ssh do + username /^User ?[nN]ame:/ + password /^\r?Password:/ + end + + cfg :telnet, :ssh do + if vars :enable + post_login do + send "enable\r" + cmd vars(:enable) + end + end + + pre_logout do + send "exit\r" + send "logout\r" + end + end +end diff --git a/lib/oxidized/model/trango.rb b/lib/oxidized/model/trango.rb index b2aa1e7..b55b277 100644 --- a/lib/oxidized/model/trango.rb +++ b/lib/oxidized/model/trango.rb @@ -1,8 +1,8 @@ class Trango < Oxidized::Model # take a Trangolink sysinfo output and turn it into a configuration file - + prompt /^#>\s?/ - comment '# ' + comment '# ' cmd 'sysinfo' do |cfg| out = [] @@ -47,16 +47,15 @@ class Trango < Oxidized::Model 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] + Regexp.last_match[2] + ' ' + + Regexp.last_match[3] end - end + end comments.push(*out).join "\n" - end + end cfg :telnet do password /Password:/ pre_logout 'exit' end - end diff --git a/lib/oxidized/model/ucs.rb b/lib/oxidized/model/ucs.rb new file mode 100644 index 0000000..4418b68 --- /dev/null +++ b/lib/oxidized/model/ucs.rb @@ -0,0 +1,30 @@ +class UCS < Oxidized::Model + prompt /^(\r?[\w.@_()-]+[#]\s?)$/ + comment '! ' + + cmd 'show version brief' do |cfg| + comment cfg + end + + cmd 'show chassis detail' do |cfg| + comment cfg + end + + cmd 'show fabric-interconnect detail' do |cfg| + comment cfg + end + + cmd 'show configuration all | no-more' do |cfg| + cfg + end + + cfg :ssh, :telnet do + post_login 'terminal length 0' + pre_logout 'exit' + end + + cfg :telnet do + username /^login:/ + password /^Password:/ + end +end diff --git a/lib/oxidized/model/voltaire.rb b/lib/oxidized/model/voltaire.rb new file mode 100644 index 0000000..62a62e5 --- /dev/null +++ b/lib/oxidized/model/voltaire.rb @@ -0,0 +1,53 @@ +class VOLTAIRE < Oxidized::Model + prompt /([\w.@()-\[:\s\]]+[#>]\s|(One or more tests have failed.*))$/ + comment '## ' + + # Pager Handling + expect /.+lines\s\d+\-\d+([\s]|\/\d+\s\(END\)\s).+$/ do |data, re| + send ' ' + data.sub re, '' + end + + cmd :all do |cfg| + cfg.gsub! /\[\?1h=\r/, '' # Pager Handling + cfg.gsub! /\r\[K/, '' # Pager Handling + cfg.gsub! /\s/, '' # Linebreak Handling + cfg.gsub! /^CPU\ load\ averages\:\s.+/, '' # Omit constantly changing CPU info + cfg.gsub! /^System\ memory\:\s.+/, '' # Omit constantly changing memory info + cfg.gsub! /^Uptime\:\s.+/, '' # Omit constantly changing uptime info + cfg.gsub! /.+Generated\ at\s\d+.+/, '' # Omit constantly changing generation time info + cfg = cfg.lines.to_a[2..-3].join + end + + cmd :secret do |cfg| + cfg.gsub! /(snmp-server community).*/, ' <snmp-server community configuration removed>' + cfg.gsub! /username (\S+) password (\d+) (\S+).*/, '<secret hidden>' + cfg + end + + cmd 'version show' do |cfg| + comment cfg + end + + cmd 'firmware-version show' do |cfg| + comment cfg + end + + cmd 'remote show' do |cfg| + cfg + end + + cmd 'sm-info show' do |cfg| + cfg + end + + cmd ' show' do |cfg| + cfg + end + + cfg :ssh do + post_login "no\n" + password /^Password:\s*/ + pre_logout 'exit' + end +end diff --git a/lib/oxidized/model/voss.rb b/lib/oxidized/model/voss.rb new file mode 100644 index 0000000..32958ce --- /dev/null +++ b/lib/oxidized/model/voss.rb @@ -0,0 +1,41 @@ +class Voss < Oxidized::Model + # Avaya VSP Operating System Software(VOSS) + # Created by danielcoxman@gmail.com + # May 25, 2017 + # This was tested on vsp4k and vsp8k + + comment '# ' + + prompt /^[^\s#>]+[#>]$/ + + # needed for proper formatting after post_login + cmd('') { |cfg| comment "#{cfg}\n" } + + # Get sys-info and remove information that changes such has temperature and power + cmd 'show sys-info' do |cfg| + cfg.gsub! /(^((.*)SysUpTime(.*))$)/, 'removed SysUpTime' + cfg.gsub! /^((.*)Temperature Info \:(.*\r?\n){4})/, 'removed Temperature Info and 3 more lines' + cfg.gsub! /(^((.*)AmbientTemperature(.*)\:(.*))$)/, 'removed AmbientTemperature' + cfg.gsub! /(^((.*)Temperature(.*)\:(.*))$)/, 'removed Temperature' + cfg.gsub! /(^((.*)Total Power Usage(.*)\:(.*))$)/, 'removed Total Power Usage' + comment "#{cfg}\n" + end + + # more the config rather than doing a show run + cmd 'more config.cfg' do |cfg| + cfg + cfg.gsub! /^[^\s#>]+[#>]$/, '' + cfg.gsub! /^more config.cfg/, '# more config.cfg' + end + + cfg :telnet do + username /Login: $/ + password /Password: $/ + end + + cfg :telnet, :ssh do + pre_logout 'exit' + post_login 'enable' + post_login 'terminal more disable' + end +end diff --git a/lib/oxidized/model/vrp.rb b/lib/oxidized/model/vrp.rb index 12a9ca3..42762c2 100644 --- a/lib/oxidized/model/vrp.rb +++ b/lib/oxidized/model/vrp.rb @@ -1,25 +1,31 @@ class VRP < Oxidized::Model # Huawei VRP - + prompt /^(<[\w.-]+>)$/ comment '# ' + cmd :secret do |cfg| + cfg.gsub! /(pin verify (?:auto|)).*/, '\\1 <PIN hidden>' + cfg.gsub! /(%\^%#.*%\^%#)/, '<secret hidden>' + cfg + end + cmd :all do |cfg| cfg.each_line.to_a[1..-2].join end - + cfg :telnet do username /^Username:$/ password /^Password:$/ end - cfg :telnet, :ssh do + cfg :telnet, :ssh do post_login 'screen-length 0 temporary' pre_logout 'quit' end cmd 'display version' do |cfg| - cfg = cfg.each_line.select {|l| not l.match /uptime/ }.join + cfg = cfg.each_line.reject { |l| l.match /uptime/ }.join comment cfg end @@ -30,5 +36,4 @@ class VRP < Oxidized::Model cmd 'display current-configuration all' do |cfg| cfg end - end diff --git a/lib/oxidized/model/vyatta.rb b/lib/oxidized/model/vyatta.rb index 8d977aa..fb6b0d1 100644 --- a/lib/oxidized/model/vyatta.rb +++ b/lib/oxidized/model/vyatta.rb @@ -1,7 +1,6 @@ class Vyatta < Oxidized::Model - # Brocade Vyatta / VyOS model # - + prompt /\@.*?\:~\$\s/ cmd :all do |cfg| @@ -9,11 +8,15 @@ class Vyatta < Oxidized::Model end cmd :secret do |cfg| + cfg.gsub! /encrypted-password (\S+).*/, 'encrypted-password <secret removed>' + cfg.gsub! /plaintext-password (\S+).*/, 'plaintext-password <secret removed>' + cfg.gsub! /password (\S+).*/, 'password <secret removed>' + cfg.gsub! /pre-shared-secret (\S+).*/, 'pre-shared-secret <secret removed>' cfg.gsub! /community (\S+) {/, 'community <hidden> {' cfg end - cmd 'show configuration | no-more' + cmd 'show configuration commands | no-more' cfg :telnet do username /login:\s/ @@ -23,5 +26,4 @@ class Vyatta < Oxidized::Model cfg :telnet, :ssh do pre_logout 'exit' end - end diff --git a/lib/oxidized/model/weos.rb b/lib/oxidized/model/weos.rb new file mode 100644 index 0000000..2856666 --- /dev/null +++ b/lib/oxidized/model/weos.rb @@ -0,0 +1,20 @@ +class WEOS < Oxidized::Model + # Westell WEOS, works with Westell 8178G, Westell 8266G + + prompt /^(\s[\w.@-]+[#>]\s?)$/ + + cmd :all do |cfg| + cfg.each_line.to_a[1..-2].join + end + + cmd 'show running-config' do |cfg| + cfg + end + + cfg :telnet do + username /login:/ + password /assword:/ + post_login 'cli more disable' + pre_logout 'logout' + end +end diff --git a/lib/oxidized/model/xos.rb b/lib/oxidized/model/xos.rb index 6f1323f..5ce8017 100644 --- a/lib/oxidized/model/xos.rb +++ b/lib/oxidized/model/xos.rb @@ -1,5 +1,4 @@ class XOS < Oxidized::Model - # Extreme Networks XOS prompt /^*?[\w .-]+# $/ @@ -8,7 +7,7 @@ class XOS < Oxidized::Model cmd :all do |cfg| # xos inserts leading \r characters and other trailing white space. # this deletes extraneous \r and trailing white space. - cfg.each_line.to_a[1..-2].map{|line|line.delete("\r").rstrip}.join("\n") + "\n" + cfg.each_line.to_a[1..-2].map { |line| line.delete("\r").rstrip }.join("\n") + "\n" end cmd 'show version' do |cfg| @@ -23,11 +22,18 @@ class XOS < Oxidized::Model comment cfg end - cmd 'show switch'do |cfg| + cmd 'show switch' do |cfg| comment cfg.each_line.reject { |line| line.match /Time:/ or line.match /boot/i }.join end - cmd 'show configuration' + cmd 'show configuration' do |cfg| + cfg = cfg.each_line.reject { |line| line.match /^#(\s[\w]+\s)(Configuration generated)/ }.join + cfg + end + + cmd 'show policy detail' do |cfg| + comment cfg + end cfg :telnet do username /^login:/ @@ -41,5 +47,4 @@ class XOS < Oxidized::Model send "n\n" end end - end diff --git a/lib/oxidized/model/zhoneolt.rb b/lib/oxidized/model/zhoneolt.rb index b60edb2..ce6df29 100644 --- a/lib/oxidized/model/zhoneolt.rb +++ b/lib/oxidized/model/zhoneolt.rb @@ -18,7 +18,7 @@ class ZhoneOLT < Oxidized::Model end cmd :all do |cfg| - cfg.each_line.to_a[1..-2].map{|line|line.delete("\r").rstrip}.join("\n") + "\n" + cfg.each_line.to_a[1..-2].map { |line| line.delete("\r").rstrip }.join("\n") + "\n" end cmd 'swversion' do |cfg| @@ -39,7 +39,7 @@ class ZhoneOLT < Oxidized::Model end cmd 'dump console' do |cfg| - cfg = cfg.each_line.select { |line| not line.match /To Abort the operation enter Ctrl-C/ }.join + cfg = cfg.each_line.reject { |line| line.match /To Abort the operation enter Ctrl-C/ }.join end # zhone technically supports ssh, but it locks up a ton. Especially when diff --git a/lib/oxidized/model/zynos.rb b/lib/oxidized/model/zynos.rb index 89be8af..581d405 100644 --- a/lib/oxidized/model/zynos.rb +++ b/lib/oxidized/model/zynos.rb @@ -1,12 +1,10 @@ class ZyNOS < Oxidized::Model - # Used in Zyxel DSLAMs, such as SAM1316 - comment '! ' + comment '! ' cmd 'config-0' cfg :ftp do end - end diff --git a/lib/oxidized/model/zynoscli.rb b/lib/oxidized/model/zynoscli.rb new file mode 100644 index 0000000..ae64b04 --- /dev/null +++ b/lib/oxidized/model/zynoscli.rb @@ -0,0 +1,36 @@ +class ZyNOSCLI < Oxidized::Model + # Used in Zyxel DSLAMs, such as SAM1316 + + # Typical prompt "XGS4600#" + prompt /^([\w.@()-]+[#>]\s\e7)$/ + comment ';; ' + + cmd :all do |cfg| + cfg.gsub! /^.*\e7/, '' + end + cmd 'show stacking' + + cmd 'show version' + + cmd 'show running-config' + + cfg :telnet do + username /^User name:/i + password /^Password:/i + end + + cfg :telnet, :ssh do + if vars :enable + post_login do + send "enable\n" + # Interpret enable: true as meaning we won't be prompted for a password + unless vars(:enable).is_a? TrueClass + expect /[pP]assword:\s?$/ + send vars(:enable) + "\n" + end + expect /^.+[#]$/ + end + end + pre_logout 'exit' + end +end diff --git a/lib/oxidized/node.rb b/lib/oxidized/node.rb index 7201a73..4105da0 100644 --- a/lib/oxidized/node.rb +++ b/lib/oxidized/node.rb @@ -6,8 +6,9 @@ module Oxidized class ModelNotFound < OxidizedError; end class Node attr_reader :name, :ip, :model, :input, :output, :group, :auth, :prompt, :vars, :last, :repo - attr_accessor :running, :user, :msg, :from, :stats, :retry + attr_accessor :running, :user, :email, :msg, :from, :stats, :retry alias :running? :running + def initialize opt Oxidized.logger.debug 'resolving DNS for %s...' % opt[:name] # remove the prefix if an IP Address is provided with one as IPAddr converts it to a network address. @@ -39,7 +40,7 @@ module Oxidized cfg_name = input.to_s.split('::').last.downcase next unless @model.cfg[cfg_name] and not @model.cfg[cfg_name].empty? @model.input = input = input.new - if config=run_input(input) + if config = run_input(input) Oxidized.logger.debug "lib/oxidized/node.rb: #{input.class.name} ran for #{name} successfully" status = :success break @@ -55,7 +56,7 @@ module Oxidized def run_input input rescue_fail = {} [input.class::RescueFail, input.class.superclass::RescueFail].each do |hash| - hash.each do |level,errors| + hash.each do |level, errors| errors.each do |err| rescue_fail[err] = level end @@ -64,9 +65,9 @@ module Oxidized begin input.connect(self) and input.get rescue *rescue_fail.keys => err - resc = '' + resc = '' if not level = rescue_fail[err.class] - resc = err.class.ancestors.find{|e|rescue_fail.keys.include? e} + resc = err.class.ancestors.find { |e| rescue_fail.keys.include? e } level = rescue_fail[resc] resc = " (rescued #{resc})" end @@ -121,7 +122,7 @@ module Oxidized end def reset - @user = @msg = @from = nil + @user = @email = @msg = @from = nil @retry = 0 end @@ -167,35 +168,49 @@ module Oxidized end def resolve_repo opt - return unless is_git? opt - - remote_repo = Oxidized.config.output.git.repo - - if remote_repo.is_a?(::String) - if Oxidized.config.output.git.single_repo? || @group.nil? - remote_repo + if is_git? opt + remote_repo = Oxidized.config.output.git.repo + + if remote_repo.is_a?(::String) + if Oxidized.config.output.git.single_repo? || @group.nil? + remote_repo + else + File.join(File.dirname(remote_repo), @group + '.git') + end else - File.join(File.dirname(remote_repo), @group + '.git') + remote_repo[@group] + end + elsif is_gitcrypt? opt + remote_repo = Oxidized.config.output.gitcrypt.repo + + if remote_repo.is_a?(::String) + if Oxidized.config.output.gitcrypt.single_repo? || @group.nil? + remote_repo + else + File.join(File.dirname(remote_repo), @group + '.git') + end + else + remote_repo[@group] end else - remote_repo[@group] + return end end - def resolve_key key, opt, global=nil + 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 + # 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 + # group if Oxidized.config.groups.has_key?(@group) if Oxidized.config.groups[@group].has_key?(key_str) value = Oxidized.config.groups[@group][key_str] @@ -203,7 +218,16 @@ module Oxidized end end - #node + # model + # FIXME: warning: instance variable @model not initialized + if Oxidized.config.models.has_key?(@model.class.name.to_s.downcase) + if Oxidized.config.models[@model.class.name.to_s.downcase].has_key?(key_str) + value = Oxidized.config.models[@model.class.name.to_s.downcase][key_str] + Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from model" + end + end + + # node value = opt[key_sym] || value Oxidized.logger.debug "node.rb: returning node key '#{key}' with value '#{value}'" value @@ -213,5 +237,8 @@ module Oxidized (opt[:output] || Oxidized.config.output.default) == 'git' end + def is_gitcrypt? opt + (opt[:output] || Oxidized.config.output.default) == 'gitcrypt' + end end end diff --git a/lib/oxidized/node/stats.rb b/lib/oxidized/node/stats.rb index 6b5719c..2c04a60 100644 --- a/lib/oxidized/node/stats.rb +++ b/lib/oxidized/node/stats.rb @@ -18,7 +18,7 @@ module Oxidized # @param [Symbol] status stats for specific status # @return [Hash,Array] Hash of stats for every status or Array of stats for specific status - def get status=nil + def get status = nil status ? @stats[status] : @stats end @@ -27,7 +27,6 @@ module Oxidized def initialize @stats = {} end - end end end diff --git a/lib/oxidized/nodes.rb b/lib/oxidized/nodes.rb index f5a1ad0..a159b48 100644 --- a/lib/oxidized/nodes.rb +++ b/lib/oxidized/nodes.rb @@ -4,15 +4,16 @@ module Oxidized class Oxidized::NotSupported < OxidizedError; end class Oxidized::NodeNotFound < OxidizedError; end class Nodes < Array - attr_accessor :source + attr_accessor :source, :jobs alias :put :unshift - def load node_want=nil + def load node_want = nil with_lock do new = [] @source = Oxidized.config.source.default Oxidized.mgr.add_source @source Oxidized.logger.info "lib/oxidized/nodes.rb: Loading nodes" - Oxidized.mgr.source[@source].new.load.each do |node| + nodes = Oxidized.mgr.source[@source].new.load node_want + nodes.each do |node| # we want to load specific node(s), not all of them next unless node_want? node_want, node begin @@ -42,7 +43,6 @@ module Oxidized end end - def list with_lock do map { |e| e.serialize } @@ -63,16 +63,18 @@ module Oxidized end # @param node [String] name of the node moved into the head of array - def next node, opt={} + def next node, opt = {} if waiting.find_node_index(node) with_lock do n = del node n.user = opt['user'] + n.email = opt['email'] n.msg = opt['msg'] n.from = opt['from'] # set last job to nil so that the node is picked for immediate update n.last = nil put n + jobs.want += 1 if Oxidized.config.next_adds_job? end end end @@ -111,10 +113,10 @@ module Oxidized private - def initialize opts={} + def initialize opts = {} super() node = opts.delete :node - @mutex= Mutex.new # we compete for the nodes with webapi thread + @mutex = Mutex.new # we compete for the nodes with webapi thread if nodes = opts.delete(:nodes) replace nodes else @@ -127,7 +129,7 @@ module Oxidized end def find_index node - index { |e| e.name == node or e.ip == node} + index { |e| e.name == node or e.ip == node } end # @param node node which is removed from nodes list @@ -160,7 +162,7 @@ module Oxidized node.stats = old[i].stats node.last = old[i].last end - rescue Oxidized::NodeNotFound + rescue Oxidized::NodeNotFound end end sort_by! { |x| x.last.nil? ? Time.new(0) : x.last.end } diff --git a/lib/oxidized/output/file.rb b/lib/oxidized/output/file.rb index 45f72e1..bad1b6a 100644 --- a/lib/oxidized/output/file.rb +++ b/lib/oxidized/output/file.rb @@ -1,59 +1,58 @@ module Oxidized -class OxidizedFile < Output - require 'fileutils' + class OxidizedFile < Output + require 'fileutils' - attr_reader :commitref + attr_reader :commitref - def initialize - @cfg = Oxidized.config.output.file - end + def initialize + @cfg = Oxidized.config.output.file + end - def setup - if @cfg.empty? - Oxidized.asetus.user.output.file.directory = File.join(Config::Root, 'configs') - Oxidized.asetus.save :user - raise NoConfig, 'no output file config, edit ~/.config/oxidized/config' + def setup + if @cfg.empty? + Oxidized.asetus.user.output.file.directory = File.join(Config::Root, 'configs') + Oxidized.asetus.save :user + raise NoConfig, 'no output file config, edit ~/.config/oxidized/config' + end end - end - def store node, outputs, opt={} - file = File.expand_path @cfg.directory - if opt[:group] - file = File.join File.dirname(file), opt[:group] + def store node, outputs, opt = {} + file = File.expand_path @cfg.directory + if opt[:group] + file = File.join File.dirname(file), opt[:group] + end + FileUtils.mkdir_p file + file = File.join file, node + open(file, 'w') { |fh| fh.write outputs.to_cfg } + @commitref = file end - FileUtils.mkdir_p file - file = File.join file, node - open(file, 'w') { |fh| fh.write outputs.to_cfg } - @commitref = file - end - def fetch node, group - cfg_dir = File.expand_path @cfg.directory - node_name = node.name + def fetch node, group + cfg_dir = File.expand_path @cfg.directory + node_name = node.name - if group # group is explicitly defined by user - 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_name) # node configuration file is stored on base directory + if group # group is explicitly defined by user + cfg_dir = File.join File.dirname(cfg_dir), group File.read File.join(cfg_dir, node_name) else - path = Dir.glob(File.join(File.dirname(cfg_dir), '**', node_name)).first # fetch node in all groups - File.read path + 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(File.dirname(cfg_dir), '**', node_name)).first # fetch node in all groups + File.read path + end end + rescue Errno::ENOENT + return nil end - rescue Errno::ENOENT - return nil - end - def version node, group - # not supported - [] - end + def version node, group + # not supported + [] + end - def get_version node, group, oid - 'not supported' + def get_version node, group, oid + 'not supported' + end end - -end end diff --git a/lib/oxidized/output/git.rb b/lib/oxidized/output/git.rb index fee0ab6..8acfd46 100644 --- a/lib/oxidized/output/git.rb +++ b/lib/oxidized/output/git.rb @@ -1,74 +1,73 @@ module Oxidized -class Git < Output - class GitError < OxidizedError; end - begin - require 'rugged' - rescue LoadError - raise OxidizedError, 'rugged not found: sudo gem install rugged' - end - - attr_reader :commitref + class Git < Output + class GitError < OxidizedError; end + begin + require 'rugged' + rescue LoadError + raise OxidizedError, 'rugged not found: sudo gem install rugged' + end - def initialize - @cfg = Oxidized.config.output.git - end + attr_reader :commitref - def setup - if @cfg.empty? - Oxidized.asetus.user.output.git.user = 'Oxidized' - Oxidized.asetus.user.output.git.email = 'o@example.com' - Oxidized.asetus.user.output.git.repo = File.join(Config::Root, 'oxidized.git') - Oxidized.asetus.save :user - raise NoConfig, 'no output git config, edit ~/.config/oxidized/config' + def initialize + @cfg = Oxidized.config.output.git end - if @cfg.repo.respond_to?(:each) - @cfg.repo.each do |group, repo| - @cfg.repo["#{group}="] = File.expand_path repo + def setup + if @cfg.empty? + Oxidized.asetus.user.output.git.user = 'Oxidized' + Oxidized.asetus.user.output.git.email = 'o@example.com' + Oxidized.asetus.user.output.git.repo = File.join(Config::Root, 'oxidized.git') + Oxidized.asetus.save :user + raise NoConfig, 'no output git config, edit ~/.config/oxidized/config' end - else - @cfg.repo = File.expand_path @cfg.repo - end - end - def store file, outputs, opt={} - @msg = opt[:msg] - @user = (opt[:user] or @cfg.user) - @email = (opt[:email] or @cfg.email) - @opt = opt - @commitref = nil - repo = @cfg.repo - - outputs.types.each do |type| - type_cfg = '' - type_repo = File.join(File.dirname(repo), type + '.git') - outputs.type(type).each do |output| - (type_cfg << output; next) if not output.name - type_file = file + '--' + output.name - if @cfg.type_as_directory? - type_file = type + '/' + type_file - type_repo = repo + if @cfg.repo.respond_to?(:each) + @cfg.repo.each do |group, repo| + @cfg.repo["#{group}="] = File.expand_path repo end - update type_repo, type_file, output + else + @cfg.repo = File.expand_path @cfg.repo end - update type_repo, file, type_cfg end - update repo, file, outputs.to_cfg - end + def store file, outputs, opt = {} + @msg = opt[:msg] + @user = (opt[:user] or @cfg.user) + @email = (opt[:email] or @cfg.email) + @opt = opt + @commitref = nil + repo = @cfg.repo + + outputs.types.each do |type| + type_cfg = '' + type_repo = File.join(File.dirname(repo), type + '.git') + outputs.type(type).each do |output| + (type_cfg << output; next) if not output.name + type_file = file + '--' + output.name + if @cfg.type_as_directory? + type_file = type + '/' + type_file + type_repo = repo + end + update type_repo, type_file, output + end + update type_repo, file, type_cfg + end + update repo, file, outputs.to_cfg + end - def fetch node, group - begin - repo, path = yield_repo_and_path(node, group) - repo = Rugged::Repository.new repo - index = repo.index - index.read_tree repo.head.target.tree unless repo.empty? - repo.read(index.get(path)[:oid]).data - rescue - 'node not found' + def fetch node, group + begin + repo, path = yield_repo_and_path(node, group) + repo = Rugged::Repository.new repo + index = repo.index + index.read_tree repo.head.target.tree unless repo.empty? + repo.read(index.get(path)[:oid]).data + rescue + 'node not found' + end end - end # give a hash of all oid revision for the given node, and the date of the commit def version node, group @@ -80,7 +79,7 @@ class Git < Output walker.sorting(Rugged::SORT_DATE) walker.push(repo.head.target) i = -1 - tab = [] + tab = [] walker.each do |commit| if commit.diff(paths: [path]).size > 0 hash = {} @@ -98,18 +97,18 @@ class Git < Output end end - #give the blob of a specific revision + # give the blob of a specific revision def get_version node, group, oid begin repo, path = yield_repo_and_path(node, group) repo = Rugged::Repository.new repo - repo.blob_at(oid,path).content + repo.blob_at(oid, path).content rescue 'version not found' end end - #give a hash with the patch of a diff between 2 revision and the stats (added and deleted lines) + # give a hash with the patch of a diff between 2 revision and the stats (added and deleted lines) def get_diff node, group, oid1, oid2 begin diff_commits = nil @@ -122,15 +121,15 @@ class Git < Output diff = repo.diff(commit_old, commit) diff.each do |patch| if /#{node.name}\s+/.match(patch.to_s.lines.first) - diff_commits = {:patch => patch.to_s, :stat => patch.stat} + diff_commits = { :patch => patch.to_s, :stat => patch.stat } break end end else stat = commit.parents[0].diff(commit).stat - stat = [stat[1],stat[2]] + stat = [stat[1], stat[2]] patch = commit.parents[0].diff(commit).patch - diff_commits = {:patch => patch, :stat => stat} + diff_commits = { :patch => patch, :stat => stat } end diff_commits @@ -139,68 +138,67 @@ class Git < Output end end - private + private - def yield_repo_and_path(node, group) - repo, path = node.repo, node.name + def yield_repo_and_path(node, group) + repo, path = node.repo, node.name - if group and @cfg.single_repo? - path = "#{group}/#{node.name}" - end + if group and @cfg.single_repo? + path = "#{group}/#{node.name}" + end - [repo, path] - end + [repo, path] + end - def update repo, file, data - return if data.empty? + def update repo, file, data + return if data.empty? - if @opt[:group] - if @cfg.single_repo? - file = File.join @opt[:group], file - else - repo = if repo.is_a?(::String) - File.join File.dirname(repo), @opt[:group] + '.git' - else - repo[@opt[:group]] - end + if @opt[:group] + if @cfg.single_repo? + file = File.join @opt[:group], file + else + repo = if repo.is_a?(::String) + File.join File.dirname(repo), @opt[:group] + '.git' + else + repo[@opt[:group]] + end + end end - end - begin - repo = Rugged::Repository.new repo - update_repo repo, file, data, @msg, @user, @email - rescue Rugged::OSError, Rugged::RepositoryError => open_error begin - Rugged::Repository.init_at repo, :bare - rescue => create_error - raise GitError, "first '#{open_error.message}' was raised while opening git repo, then '#{create_error.message}' was while trying to create git repo" + repo = Rugged::Repository.new repo + update_repo repo, file, data, @msg, @user, @email + rescue Rugged::OSError, Rugged::RepositoryError => open_error + begin + Rugged::Repository.init_at repo, :bare + rescue => create_error + raise GitError, "first '#{open_error.message}' was raised while opening git repo, then '#{create_error.message}' was while trying to create git repo" + end + retry end - retry end - end - def update_repo repo, file, data, msg, user, email - oid = repo.write data, :blob - index = repo.index - index.read_tree repo.head.target.tree unless repo.empty? - - tree_old = index.write_tree repo - index.add :path=>file, :oid=>oid, :mode=>0100644 - tree_new = index.write_tree repo - - if tree_old != tree_new - repo.config['user.name'] = user - repo.config['user.email'] = email - @commitref = Rugged::Commit.create(repo, - :tree => index.write_tree(repo), - :message => msg, - :parents => repo.empty? ? [] : [repo.head.target].compact, - :update_ref => 'HEAD', - ) - - index.write - true + def update_repo repo, file, data, msg, user, email + oid = repo.write data, :blob + index = repo.index + index.read_tree repo.head.target.tree unless repo.empty? + + tree_old = index.write_tree repo + index.add :path => file, :oid => oid, :mode => 0100644 + tree_new = index.write_tree repo + + if tree_old != tree_new + repo.config['user.name'] = user + repo.config['user.email'] = email + @commitref = Rugged::Commit.create(repo, + :tree => index.write_tree(repo), + :message => msg, + :parents => repo.empty? ? [] : [repo.head.target].compact, + :update_ref => 'HEAD',) + + index.write + true + end end end end -end diff --git a/lib/oxidized/output/gitcrypt.rb b/lib/oxidized/output/gitcrypt.rb new file mode 100644 index 0000000..2da0179 --- /dev/null +++ b/lib/oxidized/output/gitcrypt.rb @@ -0,0 +1,243 @@ +module Oxidized + class GitCrypt < Output + class GitCryptError < OxidizedError; end + begin + require 'git' + rescue LoadError + raise OxidizedError, 'git not found: sudo gem install ruby-git' + end + + attr_reader :commitref + + def initialize + @cfg = Oxidized.config.output.gitcrypt + @gitcrypt_cmd = "/usr/bin/git-crypt" + @gitcrypt_init = @gitcrypt_cmd + " init" + @gitcrypt_unlock = @gitcrypt_cmd + " unlock" + @gitcrypt_lock = @gitcrypt_cmd + " lock" + @gitcrypt_adduser = @gitcrypt_cmd + " add-gpg-user --trusted " + end + + def setup + if @cfg.empty? + Oxidized.asetus.user.output.gitcrypt.user = 'Oxidized' + Oxidized.asetus.user.output.gitcrypt.email = 'o@example.com' + Oxidized.asetus.user.output.gitcrypt.repo = File.join(Config::Root, 'oxidized.git') + Oxidized.asetus.save :user + raise NoConfig, 'no output git config, edit ~/.config/oxidized/config' + end + + if @cfg.repo.respond_to?(:each) + @cfg.repo.each do |group, repo| + @cfg.repo["#{group}="] = File.expand_path repo + end + else + @cfg.repo = File.expand_path @cfg.repo + end + end + + def crypt_init repo + repo.chdir do + system(@gitcrypt_init) + @cfg.users.each do |user| + system("#{@gitcrypt_adduser} #{user}") + end + File.write(".gitattributes", "* filter=git-crypt diff=git-crypt\n.gitattributes !filter !diff") + repo.add(".gitattributes") + repo.commit("Initial commit: crypt all config files") + end + end + + def lock repo + repo.chdir do + system(@gitcrypt_lock) + end + end + + def unlock repo + repo.chdir do + system(@gitcrypt_unlock) + end + end + + def store file, outputs, opt = {} + @msg = opt[:msg] + @user = (opt[:user] or @cfg.user) + @email = (opt[:email] or @cfg.email) + @opt = opt + @commitref = nil + repo = @cfg.repo + + outputs.types.each do |type| + type_cfg = '' + type_repo = File.join(File.dirname(repo), type + '.git') + outputs.type(type).each do |output| + (type_cfg << output; next) if not output.name + type_file = file + '--' + output.name + if @cfg.type_as_directory? + type_file = type + '/' + type_file + type_repo = repo + end + update type_repo, type_file, output + end + update type_repo, file, type_cfg + end + + update repo, file, outputs.to_cfg + end + + def fetch node, group + begin + repo, path = yield_repo_and_path(node, group) + repo = Git.open repo + unlock repo + index = repo.index + # Empty repo ? + empty = File.exists? index.path + if empty + raise 'Empty git repo' + else + File.read path + end + lock repo + rescue + 'node not found' + end + end + + # give a hash of all oid revision for the given node, and the date of the commit + def version node, group + begin + repo, path = yield_repo_and_path(node, group) + + repo = Git.open repo + unlock repo + walker = repo.log.path(path) + i = -1 + tab = [] + walker.each do |commit| + hash = {} + hash[:date] = commit.date.to_s + hash[:oid] = commit.objectish + hash[:author] = commit.author + hash[:message] = commit.message + tab[i += 1] = hash + end + walker.reset + tab + rescue + 'node not found' + end + end + + # give the blob of a specific revision + def get_version node, group, oid + begin + repo, path = yield_repo_and_path(node, group) + repo = Git.open repo + unlock repo + repo.gtree(oid).files[path].contents + rescue + 'version not found' + ensure + lock repo + end + end + + # give a hash with the patch of a diff between 2 revision and the stats (added and deleted lines) + def get_diff node, group, oid1, oid2 + begin + diff_commits = nil + repo, path = yield_repo_and_path(node, group) + repo = Git.open repo + unlock repo + commit = repo.gcommit(oid1) + + if oid2 + commit_old = repo.gcommit(oid2) + diff = repo.diff(commit_old, commit) + stats = [diff.stats[:files][node.name][:insertions], diff.stats[:files][node.name][:deletions]] + diff.each do |patch| + if /#{node.name}\s+/.match(patch.patch.to_s.lines.first) + diff_commits = { :patch => patch.patch.to_s, :stat => stats } + break + end + end + else + stat = commit.parents[0].diff(commit).stats + stat = [stat[:files][node.name][:insertions], stat[:files][node.name][:deletions]] + patch = commit.parents[0].diff(commit).patch + diff_commits = { :patch => patch, :stat => stat } + end + lock repo + diff_commits + rescue + 'no diffs' + ensure + lock repo + end + end + + private + + def yield_repo_and_path(node, group) + repo, path = node.repo, node.name + + if group and @cfg.single_repo? + path = "#{group}/#{node.name}" + end + + [repo, path] + end + + def update repo, file, data + return if data.empty? + + if @opt[:group] + if @cfg.single_repo? + file = File.join @opt[:group], file + else + repo = if repo.is_a?(::String) + File.join File.dirname(repo), @opt[:group] + '.git' + else + repo[@opt[:group]] + end + end + end + + begin + update_repo repo, file, data, @msg, @user, @email + rescue Git::GitExecuteError, ArgumentError => open_error + Oxidized.logger.debug "open_error #{open_error} #{file}" + begin + grepo = Git.init repo + crypt_init grepo + rescue => create_error + raise GitCryptError, "first '#{open_error.message}' was raised while opening git repo, then '#{create_error.message}' was while trying to create git repo" + end + retry + end + end + + def update_repo repo, file, data, msg, user, email + grepo = Git.open repo + grepo.config('user.name', user) + grepo.config('user.email', email) + grepo.chdir do + unlock grepo + File.write(file, data) + grepo.add(file) + if grepo.status[file].nil? + grepo.commit(msg) + @commitref = grepo.log(1).first.objectish + true + elsif !grepo.status[file].type.nil? + grepo.commit(msg) + @commitref = grepo.log(1).first.objectish + true + end + lock grepo + end + end + end +end diff --git a/lib/oxidized/output/http.rb b/lib/oxidized/output/http.rb index 13ba300..0467261 100644 --- a/lib/oxidized/output/http.rb +++ b/lib/oxidized/output/http.rb @@ -7,9 +7,9 @@ module Oxidized def setup if @cfg.empty? - CFGS.user.output.http.user = 'Oxidized' + CFGS.user.output.http.user = 'Oxidized' CFGS.user.output.http.pasword = 'secret' - CFGS.user.output.http.url = 'http://localhost/web-api/oxidized' + CFGS.user.output.http.url = 'http://localhost/web-api/oxidized' CFGS.save :user raise NoConfig, 'no output http config, edit ~/.config/oxidized/config' end @@ -17,42 +17,38 @@ module Oxidized require "net/http" require "uri" require "json" - def store node, outputs, opt={} + def store node, outputs, opt = {} @commitref = nil json = JSON.pretty_generate( - { - 'msg' => opt[:msg], - 'user' => opt[:user], - 'email' => opt[:email], - 'group' => opt[:group], - 'node' => node, - 'config' => outputs.to_cfg, - # actually we need to also iterate outputs, for other types like in gitlab. But most people don't use 'type' functionality. - } + { + 'msg' => opt[:msg], + 'user' => opt[:user], + 'email' => opt[:email], + 'group' => opt[:group], + 'node' => node, + 'config' => outputs.to_cfg, + # actually we need to also iterate outputs, for other types like in gitlab. But most people don't use 'type' functionality. + } ) uri = URI.parse @cfg.url http = Net::HTTP.new uri.host, uri.port - #http.use_ssl = true if uri.scheme = 'https' - req = Net::HTTP::Post.new(uri.request_uri, initheader = { 'Content-Type' => 'application/json'}) + # http.use_ssl = true if uri.scheme = 'https' + req = Net::HTTP::Post.new(uri.request_uri, initheader = { 'Content-Type' => 'application/json' }) req.basic_auth @cfg.user, @cfg.password req.body = json response = http.request req case response.code.to_i - when 200 || 201 - Oxidized.logger.info "Configuration http backup complete for #{node}" - p [:success] - when (400..499) - Oxidized.logger.info "Configuration http backup for #{node} failed status: #{response.body}" - p [:bad_request] - when (500..599) - p [:server_problems] - Oxidized.logger.info "Configuration http backup for #{node} failed status: #{response.body}" + when 200 || 201 + Oxidized.logger.info "Configuration http backup complete for #{node}" + p [:success] + when (400..499) + Oxidized.logger.info "Configuration http backup for #{node} failed status: #{response.body}" + p [:bad_request] + when (500..599) + p [:server_problems] + Oxidized.logger.info "Configuration http backup for #{node} failed status: #{response.body}" end - end - end end - - diff --git a/lib/oxidized/output/output.rb b/lib/oxidized/output/output.rb index 1355d03..9fae7b0 100644 --- a/lib/oxidized/output/output.rb +++ b/lib/oxidized/output/output.rb @@ -3,8 +3,7 @@ module Oxidized class NoConfig < OxidizedError; end def cfg_to_str cfg - cfg.select{ |h| h[:type]=='cfg' }.map{ |h| h[:data] }.join + cfg.select { |h| h[:type] == 'cfg' }.map { |h| h[:data] }.join end - end end diff --git a/lib/oxidized/source/csv.rb b/lib/oxidized/source/csv.rb index c1e310a..ae1c0a9 100644 --- a/lib/oxidized/source/csv.rb +++ b/lib/oxidized/source/csv.rb @@ -1,54 +1,53 @@ module Oxidized -class CSV < Source - def initialize - @cfg = Oxidized.config.source.csv - super - end - - def setup - if @cfg.empty? - Oxidized.asetus.user.source.csv.file = File.join(Config::Root, 'router.db') - Oxidized.asetus.user.source.csv.delimiter = /:/ - Oxidized.asetus.user.source.csv.map.name = 0 - Oxidized.asetus.user.source.csv.map.model = 1 - Oxidized.asetus.user.source.csv.gpg = false - Oxidized.asetus.save :user - raise NoConfig, 'no source csv config, edit ~/.config/oxidized/config' + class CSV < Source + def initialize + @cfg = Oxidized.config.source.csv + super end - require 'gpgme' if @cfg.gpg? - end - def load - nodes = [] - file = File.expand_path(@cfg.file) - file = if @cfg.gpg? - crypto = GPGME::Crypto.new password: @cfg.gpg_password - crypto.decrypt(file).to_s - else - open(file) - end - file.each_line do |line| - next if line.match(/^\s*#/) - 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] = node_var_interpolate data[position] + def setup + if @cfg.empty? + Oxidized.asetus.user.source.csv.file = File.join(Config::Root, 'router.db') + Oxidized.asetus.user.source.csv.delimiter = /:/ + Oxidized.asetus.user.source.csv.map.name = 0 + Oxidized.asetus.user.source.csv.map.model = 1 + Oxidized.asetus.user.source.csv.gpg = false + Oxidized.asetus.save :user + raise NoConfig, 'no source csv config, edit ~/.config/oxidized/config' end - keys[:model] = map_model keys[:model] if keys.key? :model + require 'gpgme' if @cfg.gpg? + end - # map node specific vars - vars = {} - @cfg.vars_map.each do |key, position| - vars[key.to_sym] = node_var_interpolate data[position] - end - keys[:vars] = vars unless vars.empty? + def load + nodes = [] + file = File.expand_path(@cfg.file) + file = if @cfg.gpg? + crypto = GPGME::Crypto.new password: @cfg.gpg_password + file = crypto.decrypt(File.open(file)).to_s + else + open(file) + end + file.each_line do |line| + next if line.match(/^\s*#/) + 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] = node_var_interpolate data[position] + end + keys[:model] = map_model keys[:model] if keys.has_key? :model - nodes << keys + # map node specific vars + vars = {} + @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 + end + nodes end - nodes end - -end end diff --git a/lib/oxidized/source/http.rb b/lib/oxidized/source/http.rb index 4fd388b..55dcd4c 100644 --- a/lib/oxidized/source/http.rb +++ b/lib/oxidized/source/http.rb @@ -1,60 +1,80 @@ module Oxidized -class HTTP < Source - def initialize - @cfg = Oxidized.config.source.http - super - end - - def setup - if @cfg.url.empty? - raise NoConfig, 'no source http url config, edit ~/.config/oxidized/config' + class HTTP < Source + def initialize + @cfg = Oxidized.config.source.http + super end - end - require "net/http" - require "uri" - require "json" - - def load - nodes = [] - 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 = {} - @cfg.headers.each do |header, value| - headers[header] = value + def setup + if @cfg.url.empty? + raise NoConfig, 'no source http url config, edit ~/.config/oxidized/config' + end end - request = Net::HTTP::Get.new(uri.request_uri, headers) - if (@cfg.user && @cfg.pass) - request.basic_auth(@cfg.user,@cfg.pass) - end + require "net/http" + require "uri" + require "json" + + def load node_want = nil + nodes = [] + 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 - response = http.request(request) - data = JSON.parse(response.body) - data.each do |line| - next if line.empty? - # map node parameters - keys = {} - @cfg.map.each do |key, position| - keys[key.to_sym] = node_var_interpolate line[position] + # map headers + headers = {} + @cfg.headers.each do |header, value| + headers[header] = value end - keys[:model] = map_model keys[:model] if keys.key? :model - # map node specific vars - vars = {} - @cfg.vars_map.each do |key, position| - vars[key.to_sym] = node_var_interpolate line[position] + req_uri = uri.request_uri + if node_want + req_uri = "#{req_uri}/#{node_want}" end - keys[:vars] = vars unless vars.empty? + request = Net::HTTP::Get.new(req_uri, headers) + if (@cfg.user? && @cfg.pass?) + request.basic_auth(@cfg.user, @cfg.pass) + end + + response = http.request(request) + data = JSON.parse(response.body) + data.each do |node| + next if node.empty? + # map node parameters + keys = {} + @cfg.map.each do |key, want_position| + want_positions = want_position.split('.') + keys[key.to_sym] = node_var_interpolate node.dig(*want_positions) + end + keys[:model] = map_model keys[:model] if keys.has_key? :model - nodes << keys + # map node specific vars + vars = {} + @cfg.vars_map.each do |key, want_position| + want_positions = want_position.split('.') + vars[key.to_sym] = node_var_interpolate node.dig(*want_positions) + end + keys[:vars] = vars unless vars.empty? + + nodes << keys + end + nodes end - nodes end - end + +if RUBY_VERSION < '2.3' + class Hash + def dig(key, *rest) + value = self[key] + if value.nil? || rest.empty? + value + elsif value.respond_to?(:dig) + value.dig(*rest) + else # foo.bar.baz (bar exist but is not hash) + return nil + end + end + end end diff --git a/lib/oxidized/source/source.rb b/lib/oxidized/source/source.rb index 9b8bc94..3bae0f6 100644 --- a/lib/oxidized/source/source.rb +++ b/lib/oxidized/source/source.rb @@ -11,13 +11,12 @@ module Oxidized end def node_var_interpolate var - case var - when "nil" then nil - when "false" then false - when "true" then true - else var - end + 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 13fc39b..c5fe650 100644 --- a/lib/oxidized/source/sql.rb +++ b/lib/oxidized/source/sql.rb @@ -1,63 +1,67 @@ module Oxidized -class SQL < Source - begin - require 'sequel' - rescue LoadError - raise OxidizedError, 'sequel not found: sudo gem install sequel' - end + class SQL < Source + begin + require 'sequel' + rescue LoadError + raise OxidizedError, 'sequel not found: sudo gem install sequel' + end - def setup - if @cfg.empty? - Oxidized.asetus.user.source.sql.adapter = 'sqlite' - Oxidized.asetus.user.source.sql.database = File.join(Config::Root, 'sqlite.db') - Oxidized.asetus.user.source.sql.table = 'devices' - Oxidized.asetus.user.source.sql.map.name = 'name' - Oxidized.asetus.user.source.sql.map.model = 'rancid' - Oxidized.asetus.save :user - raise NoConfig, 'no source sql config, edit ~/.config/oxidized/config' + def setup + if @cfg.empty? + Oxidized.asetus.user.source.sql.adapter = 'sqlite' + Oxidized.asetus.user.source.sql.database = File.join(Config::Root, 'sqlite.db') + Oxidized.asetus.user.source.sql.table = 'devices' + Oxidized.asetus.user.source.sql.map.name = 'name' + Oxidized.asetus.user.source.sql.map.model = 'rancid' + Oxidized.asetus.save :user + raise NoConfig, 'no source sql config, edit ~/.config/oxidized/config' + end end - end - def load - nodes = [] - db = connect - query = db[@cfg.table.to_sym] - query = query.with_sql(@cfg.query) if @cfg.query? - query.each do |node| - # map node parameters - keys = {} - @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 do |key, sql_column| - vars[key.to_sym] = node_var_interpolate node[sql_column.to_sym] + def load node_want = nil + nodes = [] + db = connect + query = db[@cfg.table.to_sym] + query = query.with_sql(@cfg.query) if @cfg.query? + + if node_want + query = query.where(@cfg.map.name.to_sym => node_want) end - keys[:vars] = vars unless vars.empty? - nodes << keys + query.each do |node| + # map node parameters + keys = {} + @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.has_key? :model + + # map node specific vars + vars = {} + @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 + end + db.disconnect + nodes end - db.disconnect - nodes - end - private + private - def initialize - super - @cfg = Oxidized.config.source.sql - end + def initialize + super + @cfg = Oxidized.config.source.sql + end - def connect - Sequel.connect(:adapter => @cfg.adapter, - :host => @cfg.host?, - :user => @cfg.user?, - :password => @cfg.password?, - :database => @cfg.database) - rescue Sequel::AdapterNotFound => error - raise OxidizedError, "SQL adapter gem not installed: " + error.message + def connect + Sequel.connect(:adapter => @cfg.adapter, + :host => @cfg.host?, + :user => @cfg.user?, + :password => @cfg.password?, + :database => @cfg.database) + rescue Sequel::AdapterNotFound => error + raise OxidizedError, "SQL adapter gem not installed: " + error.message + end end - -end end diff --git a/lib/oxidized/string.rb b/lib/oxidized/string.rb index 8bcb082..ca4862d 100644 --- a/lib/oxidized/string.rb +++ b/lib/oxidized/string.rb @@ -15,11 +15,11 @@ module Oxidized # sets @cmd and @name unless @name is already set def set_cmd command - @cmd = command + @cmd = command @name ||= @cmd.strip.gsub(/\s+/, '_') end - def initialize str='' + def initialize str = '' super if str.class == Oxidized::String @cmd = str.cmd @@ -27,6 +27,5 @@ module Oxidized @type = str.type end end - end end diff --git a/lib/oxidized/version.rb b/lib/oxidized/version.rb index 073aae9..b607e12 100644 --- a/lib/oxidized/version.rb +++ b/lib/oxidized/version.rb @@ -1,3 +1,19 @@ module Oxidized - VERSION = '0.19.0' + VERSION = '0.21.0' + VERSION_FULL = '0.21.0-180-g9691008' + def self.version_set + version_full = %x(git describe --tags).chop rescue "" + version = %x(git describe --tags --abbrev=0).chop rescue "" + + return false unless [version, version_full].none?(&:empty?) + + Oxidized.send(:remove_const, :VERSION) + Oxidized.send(:remove_const, :VERSION_FULL) + const_set(:VERSION, version) + const_set(:VERSION_FULL, version_full) + file = File.readlines(__FILE__) + file[1] = " VERSION = '%s'\n" % VERSION + file[2] = " VERSION_FULL = '%s'\n" % VERSION_FULL + File.write(__FILE__, file.join) + end end diff --git a/lib/oxidized/worker.rb b/lib/oxidized/worker.rb index 1952d01..692b060 100644 --- a/lib/oxidized/worker.rb +++ b/lib/oxidized/worker.rb @@ -3,8 +3,10 @@ module Oxidized require 'oxidized/jobs' class Worker def initialize nodes - @nodes = nodes - @jobs = Jobs.new(Oxidized.config.threads, Oxidized.config.interval, @nodes) + @jobs_done = 0 + @nodes = nodes + @jobs = Jobs.new(Oxidized.config.threads, Oxidized.config.interval, @nodes) + @nodes.jobs = @jobs Thread.abort_on_exception = true end @@ -13,8 +15,9 @@ module Oxidized @jobs.delete_if { |job| ended << job if not job.alive? } ended.each { |job| process job } @jobs.work + while @jobs.size < @jobs.want - Oxidized.logger.debug "lib/oxidized/worker.rb: Jobs #{@jobs.size}, Want: #{@jobs.want}" + Oxidized.logger.debug "lib/oxidized/worker.rb: Jobs running: #{@jobs.size} of #{@jobs.want} - ended: #{@jobs_done} of #{@nodes.size}" # ask for next node in queue non destructive way nextnode = @nodes.first unless nextnode.last.nil? @@ -28,6 +31,8 @@ module Oxidized @jobs.push Job.new node Oxidized.logger.debug "lib/oxidized/worker.rb: Added #{node.name} to the job queue" end + + run_done_hook if is_cycle_finished? Oxidized.logger.debug("lib/oxidized/worker.rb: #{@jobs.size} jobs running in parallel") unless @jobs.empty? end @@ -37,6 +42,8 @@ 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 Oxidized.Hooks.handle :node_success, :node => node, :job => job @@ -45,7 +52,7 @@ module Oxidized msg += " with message '#{node.msg}'" if node.msg output = node.output.new if output.store node.name, job.config, - :msg => msg, :user => node.user, :group => node.group + :msg => msg, :email => node.email, :user => node.user, :group => node.group Oxidized.logger.info "Configuration updated for #{node.group}/#{node.name}" Oxidized.Hooks.handle :post_store, :node => node, :job => job, @@ -70,5 +77,24 @@ module Oxidized Oxidized.logger.warn "#{node.name} not found, removed while collecting?" end + private + + def is_cycle_finished? + if @jobs_done > @nodes.count + true + else + @jobs_done > 0 && @jobs_done % @nodes.count == 0 + end + end + + def run_done_hook + Oxidized.logger.debug "lib/oxidized/worker.rb: Running :nodes_done hook" + Oxidized.Hooks.handle :nodes_done + rescue => e + # swallow the hook erros and continue as normal + Oxidized.logger.error "lib/oxidized/worker.rb: #{e.message}" + ensure + @jobs_done = 0 + end end end |