bin/alaxala-deploy in netutils-0.1.1 vs bin/alaxala-deploy in netutils-0.1.2

- old
+ new

@@ -36,12 +36,12 @@ progname = File.basename($0) STDERR.print "ERROR: #{errmsg}\n\n" if errmsg STDERR.print "\ Usage: #{progname} -h - #{progname} [-c] [-w] [-r] -a - #{progname} [-c] [-w] [-r] <switch IP address1> <switch IP address2> ... + #{progname} [-c] [-d] [-f] [-w] [-r] -a + #{progname} [-c] [-d] [-f] [-w] [-r] <switch IP address 1> ... Description: deploy files to Alaxala switches, such as certificate files (i.e., certificate itself, private key, intermediate CA certificate), HTML files for Web authentication. If an option, ``-a,'' is given, deploy to all switches using LLDP or @@ -52,10 +52,12 @@ Arguments: switch IP address: an IP address of a switch. Options: -h: output this help message. -c: deploy certificate files. + -d: dry run (do not actually reboot). + -f: force to consider all switches as requiring reboots. -w: deploy HTML files. -r: reload a switch if necessary (only for certificate files). -a: deploy to all switches. Example: output information: @@ -73,25 +75,96 @@ " exit 1 end ## +ROOT_DEPTH = 0 + +class Vertices + class Vertex + attr_reader :sw, :depth + + def initialize(sw) + @sw = sw + depth_compute + end + + def depth + @sw.note + end + + def depth_compute + return if ! @sw.note.nil? + depth = 0 + nparent = @sw.parent + loop do + parent = nparent + if parent.nil? + break + end + nparent = parent.parent + if ! parent.note.nil? + depth += parent.note + 1 + break + end + end + @sw.note = depth + end + end + + attr_reader :maxdepth + + def initialize + @vertices = [ Queue.new ] + @maxdepth = 0 + @mutex = Thread::Mutex.new + end + + def length(depth) + @vertices[depth].length + end + + def push(sw) + v = Vertex.new(sw) + @mutex.synchronize do + if @vertices[v.depth].nil? + @vertices[v.depth] = Queue.new + end + end + @vertices[v.depth].push(v) + if v.depth > @maxdepth + @maxdepth = v.depth + end + return v + end + + def pop(depth) + @vertices[depth].pop(true) + end +end + +vertices = Vertices.new + +## # inject() requires Ruby2.4... take a more operational way. -options0 = ARGV.getopts('hcwra', 'help', 'certificate', 'web', 'reload', 'all') +options0 = ARGV.getopts( + 'hcdfwra', 'help', 'certificate', 'dry', 'force', 'web', 'reload', 'all') options = {} options0.map { |k, v| options[k.to_sym] = v } usage if options[:h] if options[:a] if ARGV.length != 0 usage('Extra argument specified') end Switch.set_retrieve_all - SWITCHES.each { |name, ia| Switch.new(name, Switch::Type::ROUTER, ia, ) } + SWITCHES.each do |name, ia| + Switch.new(name, Switch::Type::ROUTER, ia, ).note = 0 + end elsif ARGV.length > 0 ARGV.each do |ia| - Switch.new(nil, Switch::Type::ROUTER, ia) + Switch.new(nil, Switch::Type::ROUTER, ia).note = 0 end else usage('No IP address is given.') end @@ -101,15 +174,15 @@ if options[:c] || options[:w] print 'Input FTP user name: ' user = STDIN.gets.strip print 'Input FTP password: ' password = STDIN.noecho(&:gets).strip - puts end ## -reboots = Queue.new +retrieve_start_time = Time.now + Switch.retrieve do |sw| if sw.name print "Connecting: ``#{sw.name}'' (#{sw.ia})\n" else print "Connecting: #{sw.ia}\n" @@ -148,12 +221,12 @@ r = sw.cmd('show system') if r =~ /^.*Boot Date[^:]+: ([^\n]+).*$/ v = $1.strip boottime = Time.parse(v) msg += ", boot: #{v}" - if boottime < installedtime || options[:c] - reboots.push(sw) + if boottime < installedtime || options[:c] || options[:f] + v = vertices.push(sw) msg += ' (need reboot)' end end puts msg @@ -237,35 +310,62 @@ print " Done: ``#{sw.name}'' (#{sw.ia}) " + "(#{sw.maker_to_s} #{sw.product})\n" end Switch.warn +retrieve_end_time = Time.now + exit if ! options[:r] +reboot_times = [ Time.now ] +reboot_counts = [] + thread_concurrency = 64 -threads = [] -for i in 1..thread_concurrency do - threads <<= Thread.new do - begin - while sw = reboots.pop(true) do - sw.configure - sw.cmd('save') - sw.unconfigure - begin - # - # note that pager configuration or - # other configurations barks without - # an option, ``-f.'' - # - sw.cmd('reload -f') - rescue +for rdepth in 0 .. vertices.maxdepth do + depth = vertices.maxdepth - rdepth + reboot_counts[depth] = vertices.length(depth) + puts "examine: #{depth}-th depth (#{vertices.length(depth)} switches)" + threads = [] + for i in 1..thread_concurrency do + threads <<= Thread.new do + begin + while v = vertices.pop(depth) do + sw = v.sw + if ! options[:d] + sw.configure + sw.cmd('save') + sw.unconfigure + # + # note that pager configuration + # or other configurations barks + # without an option, ``-f.'' + # + begin + sw.cmd('reload -f') + rescue + end + end + puts " Reboot: ``#{sw.name}'' " + + "(#{sw.ia}) done" end - puts " Reboot: ``#{sw.name}'' (#{sw.ia}) done" + rescue end - rescue end end + + threads.each do |thread| + thread.join + end + + reboot_times.push(Time.now) end -threads.each do |thread| - thread.join +puts "Retrieve time : #{retrieve_start_time.iso8601(3)}" + + " (#{retrieve_end_time - retrieve_start_time})" +puts " Reboot time: #{reboot_times.first.iso8601(3)}" + + " (#{reboot_times.last - reboot_times.first})" + +for depth in 0 .. vertices.maxdepth do + printf "% 10d-th depth: % 4d switches: " + + "#{reboot_times[depth + 1] - reboot_times[depth]}\n", + depth, reboot_counts[depth] end