※当サイトの記事には、広告・プロモーションが含まれます。

OxidizedをDockerで動作検証する際はmodelクラスで現在日時を出力するコマンドを追加するが吉

gigazine.net

Google DeepMindが2018年に開発した、アミノ酸の配列情報からタンパク質の立体構造を予測するAI「AlphaFold」は、多くの重要な分野での研究を加速させることを目的に、2021年にオープンソース化されました。2024年5月にはより多くの生命分子の構造と相互作用を予測できるAIモデル「AlphaFold 3」をリリースし、当初は完全なモデルを公開していませんでしたが、前モデルに続いてAlphaFold 3もオープンソース化しました。

Google DeepMindがAIですべての生命分子の構造と相互作用を予測できる「AlphaFold 3」をオープンソース化、科学的発見と創薬の加速へ - GIGAZINE

⇧ どちらが正しいのか分かりませんが、

  1. すべての生命分子の構造
  2. 多くの生命分子の構造

では天と地ほど意味が変わってくる気がするんですけどね...

OxidizedをDockerで動作検証する際はmodelクラスで現在日時を出力するコマンドを追加するが吉

何度か本ブログでも話に出ている、

github.com

Oxidized is a network device configuration backup tool. It's a RANCID replacement!

It is light and extensible and supports over 130 operating system types.

https://github.com/ytti/oxidized/tree/master

⇧「Oxidized」という「Ruby」のライブラリでまたもや泥沼にハマったので備忘録として。

「Oxidized」側で「modelクラス」のテンプレートが用意されているのですが、

github.com

github.com

■標準的なLinux ディストリビューションで利用可能なmodelクラス

github.com

https://github.com/ytti/oxidized/blob/master/lib/oxidized/model/linuxgeneric.rb

class LinuxGeneric < Oxidized::Model
  using Refinements

  prompt /^(\w.*|\W.*)[:#$] /
  comment '# '

  # add a comment in the final conf
  def add_comment(comment)
    "\n###### #{comment} ######\n"
  end

  cmd :all do |cfg|
    cfg.gsub! /^(default (\S+).* (expires) ).*/, '\\1 <redacted>'
    cfg.cut_both
  end

  # 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 'ip link'

    cfg += add_comment 'RESOLV.CONF'
    cfg += cmd 'cat /etc/resolv.conf'

    cfg += add_comment 'IP Routes'
    cfg += cmd 'ip route'

    cfg += add_comment 'IPv6 Routes'
    cfg += cmd 'ip -6 route'

    cfg += add_comment 'MOTD'
    cfg += cmd 'cat /etc/motd'

    cfg += add_comment 'PASSWD'
    cfg += cmd 'cat /etc/passwd'

    cfg += add_comment 'GROUP'
    cfg += cmd 'cat /etc/group'

    cfg += add_comment 'nsswitch.conf'
    cfg += cmd 'cat /etc/nsswitch.conf'

    cfg += add_comment 'VERSION'
    cfg += cmd 'cat /etc/issue'

    cfg
  end

  cfg :telnet do
    username /^Username:/
    password /^Password:/
  end

  cfg :telnet, :ssh do
    post_login do
      if vars(:enable) == true
        cmd "sudo su -", /^\[sudo\] password/
        cmd @node.auth[:password]
      elsif vars(:enable)
        cmd "su -", /^Password:/
        cmd vars(:enable)
      end
    end

    pre_logout do
      cmd "exit" if vars(:enable)
    end
    pre_logout 'exit'
  end
end    

Cisco IOSで利用可能なmodelクラス

github.com

https://github.com/ytti/oxidized/blob/master/lib/oxidized/model/ios.rb

class IOS < Oxidized::Model
  using Refinements

  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
    # get rid of errors for commands that don't work on some devices
    cfg.gsub! /^% Invalid input detected at '\^' marker\.$|^\s+\^$/, ''
    cfg.cut_both
  end

  cmd :secret do |cfg|
    cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>'
    cfg.gsub! /^(snmp-server host \S+( vrf \S+)?( informs?)?( version (1|2c))?) +\S+( .*)?$*/, '\\1 <secret hidden>\\6'
    cfg.gsub! /^(username .+ (password|secret) \d) .+/, '\\1 <secret hidden>'
    cfg.gsub! /^(enable (password|secret)( level \d+)? \d) .+/, '\\1 <secret hidden>'
    cfg.gsub! /^( +(?:password|secret)) (?:\d )?\S+/, '\\1 <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) .+/, '\\1 <secret hidden>'
    cfg.gsub! /^(crypto isakmp key) (\S+) (.*)/, '\\1 <secret hidden> \\3'
    cfg.gsub! /^( +ip ospf message-digest-key \d+ md5) .+/, '\\1 <secret hidden>'
    cfg.gsub! /^( +ip ospf authentication-key) .+/, '\\1 <secret hidden>'
    cfg.gsub! /^( +neighbor \S+ password) .+/, '\\1 <secret hidden>'
    cfg.gsub! /^( +vrrp \d+ authentication text) .+/, '\\1 <secret hidden>'
    cfg.gsub! /^( +standby \d+ authentication) .{1,8}$/, '\\1 <secret hidden>'
    cfg.gsub! /^( +standby \d+ authentication md5 key-string) .+?( timeout \d+)?$/, '\\1 <secret hidden> \\2'
    cfg.gsub! /^( +key-string) .+/, '\\1 <secret hidden>'
    cfg.gsub! /^((tacacs|radius) server [^\n]+\n( +[^\n]+\n)* +key) [^\n]+$/m, '\1 <secret hidden>'
    cfg.gsub! /^( +ppp (chap|pap) password \d) .+/, '\\1 <secret hidden>'
    cfg.gsub! /^( +security wpa psk set-key (?:ascii|hex) \d) (.*)$/, '\\1 <secret hidden>'
    cfg.gsub! /^( +dot1x username \S+ password \d) (.*)$/, '\\1 <secret hidden>'
    cfg.gsub! /^( +mgmtuser username \S+ password \d) (.*) (secret \d) (.*)$/, '\\1 <secret hidden> \\3 <secret hidden>'
    cfg.gsub! /^( +client \S+ server-key \d) (.*)$/, '\\1 <secret hidden>'
    cfg
  end

  cmd 'show version' do |cfg|
    comments = []
    comments << cfg.lines.first
    lines = cfg.lines
    lines.each_with_index do |line, i|
      slave = ''
      slaveslot = ''

      if line =~ /^Slave in slot (\d+) is running/
        slave = " Slave:"
        slaveslot = ", slot #{Regexp.last_match(1)}"
      end

      comments << "Image:#{slave} Compiled: #{Regexp.last_match(1)}" if line =~ /^Compiled (.*)$/

      comments << "Image:#{slave} Software: #{Regexp.last_match(1)}, #{Regexp.last_match(2)}" if line =~ /^(?:Cisco )?IOS .* Software,? \(([A-Za-z0-9_-]*)\), .*Version\s+(.*)$/

      comments << "ROM Bootstrap: #{Regexp.last_match(3)}" if line =~ /^ROM: (IOS \S+ )?(System )?Bootstrap.*(Version.*)$/

      comments << "BOOTFLASH: #{Regexp.last_match(1)}" if line =~ /^BOOTFLASH: .*(Version.*)$/

      comments << "Memory: nvram #{Regexp.last_match(1)}" if line =~ /^(\d+[kK]) bytes of (non-volatile|NVRAM)/

      comments << "Memory: flash #{Regexp.last_match(1)}" if line =~ /^(\d+[kK]) bytes of (flash memory|flash internal|processor board System flash|ATA CompactFlash)/i

      comments << "Memory: pcmcia #{Regexp.last_match(2)} #{Regexp.last_match(3)}#{Regexp.last_match(4)} #{Regexp.last_match(1)}" if line =~ /^(\d+[kK]) bytes of (Flash|ATA)?.*PCMCIA .*(slot|disk) ?(\d)/i

      if line =~ /(\S+(?:\sseries)?)\s+(?:\(([\S ]+)\)\s+processor|\(revision[^)]+\)).*\s+with (\S+k) bytes/i
        sproc = Regexp.last_match(1)
        cpu = Regexp.last_match(2)
        mem = Regexp.last_match(3)
        cpuxtra = ''
        comments << "Chassis type:#{slave} #{sproc}"
        comments << "Memory:#{slave} main #{mem}"
        # check the next two lines for more CPU info
        comments << "Processor ID: #{Regexp.last_match(1)}" if cfg.lines[i + 1] =~ /processor board id (\S+)/i
        if cfg.lines[i + 2] =~ /(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

      comments << "Image: #{Regexp.last_match(1)}" if line =~ /^System image file is "([^"]*)"$/
    end
    comments << "\n"
    comment comments.join "\n"
  end

  cmd 'show vtp status' do |cfg|
    cfg.gsub! /^$\n/, ''
    cfg.gsub! /Configuration last modified by.*\n/, ''
    cfg.gsub! /^/, 'VTP: ' unless cfg.empty?
    comment "#{cfg}\n"
  end

  cmd 'show inventory' do |cfg|
    comment cfg
  end

  post do
    cmd_line = 'show running-config'
    cmd_line += ' view full' if vars(:ios_rbac)
    cmd cmd_line do |cfg|
      cfg = cfg.each_line.to_a[3..-1]
      cfg = cfg.reject { |line| line.match /^ntp clock-period / }.join
      cfg = cfg.each_line.reject { |line| line.match /^! (Last|No) configuration change (at|since).*/ unless line =~ /\d+\sby\s\S+$/ }.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
    end
  end

  cfg :telnet do
    username /^Username:/i
    password /^Password:/i
  end

  cfg :telnet, :ssh do
    # preferred way to handle additional passwords
    post_login do
      if vars(:enable) == true
        cmd "enable"
      elsif vars(:enable)
        cmd "enable", /^[pP]assword:/
        cmd vars(:enable)
      end
    end
    post_login 'terminal length 0'
    post_login 'terminal width 0'
    pre_logout 'exit'
  end
end    

⇧ どういうわけか、取得した日時を出力するコマンドが実装されていないというね...

Oxidized is a network device configuration backup tool.』というぐらいだから、当然、処理された日時は出力してくれてるものだと思い込んでいたのが良くなかったのだが、これに気付かず貴重な時間を浪費してしまいましたと...

残念ながら、「Oxidized」側で用意されている「modelクラス」を検証する場合は、対象機器の方で設定を変えたりする必要があることになるのだけど、今回は、「modelクラス」を追加する話であったので、表題の件に落ち着きましたと。

github.com

まぁ、普通、バックアップするとなったら、バックアップした日時を出力するものだと思うのだけど、そうしない世界線もあるということが教訓というわけで、バイアスを排除するのは難しいということですかね。

出力について、

github.com

⇧「git」以外であれば気にしなくても良い問題だったのかもしれないですが...

今回は、「Cisco IOS」の「modelクラス」をカスタマイズする話であったので、

www.cisco.com

⇧「show clock」というコマンドを追加した「modelクラス」を用意し、出力されるようにして解決した。

これで、docker-compose up、downを繰り返しても、出力に差分が生まれることになるので(現在日時を出力するようにしたので、ミリ秒の差分を扱えるはず)、「Oxidized」内部で利用されている「Rugged」というライブラリの処理で、Gitのローカルの「bare repository」にgit commitされるようになる。

何と言うか、対象機器の設定が変更されない限り、初回のバックアップを取得後は、永久にバックアップされないということになるんかね?

それは、最早、バックアップしてることにはならないのでは?と思うなどした。

「Oxidized」の意図していることが分からんので、何とも言えんけど...

毎度モヤモヤ感が半端ない…

今回はこのへんで。