luna_scanner 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,132 @@
1
+ #encoding: utf-8
2
+ require 'csv'
3
+
4
+ module LunaScanner
5
+ class Device
6
+ attr_accessor :ip, :sn, :model, :dialno, :version, :to_change_ip
7
+ IP_REGEX = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/
8
+
9
+ def initialize(options={})
10
+ # ip, sn, model, version, to_change_ip=nil
11
+ @ip = options[:ip] || ""
12
+ @sn = options[:sn] || ""
13
+ @model = options[:model] || ""
14
+ @version = options[:version] || ""
15
+ @to_change_ip = options[:to_change_ip]
16
+ end
17
+
18
+ def self.display_header
19
+ "-----SN--------IP-------------MODEL------VERSION------"
20
+ end
21
+
22
+ def ip_from_sn
23
+ raise "SN not valid. 8 numbers." if self.sn !~ /^\d{8}$/
24
+
25
+ sn_integer = self.sn.to_i
26
+ ip = "8.128.#{2+sn_integer/250}.#{sn_integer%250}"
27
+ ip
28
+ end
29
+
30
+ def new_ip
31
+ ip_template = <<TXT
32
+ # and how to activate them. For more information, see interfaces(5).
33
+
34
+ # The loopback network interface
35
+ auto lo
36
+ iface lo inet loopback
37
+
38
+ #enable this if you are using 100Mbps
39
+ auto eth0
40
+ allow-hotplug eth0
41
+ iface eth0 inet static
42
+ address 0.0.0.0
43
+
44
+ auto eth1
45
+ allow-hotplug eth1
46
+ iface eth1 inet static
47
+ address #{ip_from_sn}
48
+ netmask 255.255.252.0
49
+ gateway 8.128.3.254
50
+ TXT
51
+ ip_template
52
+ end
53
+
54
+ def write_ip_to_config
55
+ File.open("/tmp/interfaces", "w") do |f|
56
+ f.puts self.new_ip
57
+ end
58
+ end
59
+
60
+ def display
61
+ " #{sn} #{ip.ljust(15)} #{model.ljust(8)} #{version}" # two space
62
+ end
63
+
64
+ def dev?
65
+ sn.start_with?("f")
66
+ end
67
+
68
+ def ==(other)
69
+ return false if not other.is_a?(Device)
70
+
71
+ self.sn == other.sn
72
+ end
73
+
74
+ def self.load_from_file(file_name)
75
+ if file_name && file_name.to_s.end_with?(".csv")
76
+ self.validate_change_ip_csv_file!(file_name)
77
+
78
+ devices = Array.new
79
+ CSV.foreach(file_name) do |row|
80
+ # sn,changed_ip
81
+ devices << Device.new({
82
+ :ip => nil, :sn => row[0], :model => nil, :version => nil, :to_change_ip => row[1]
83
+ })
84
+ end
85
+ return devices
86
+ end
87
+
88
+ ip_file = File.read(file_name)
89
+ devices = Array.new
90
+
91
+ ip_file.each_line do |ip_line|
92
+ temp = ip_line.split(",")
93
+ if temp.size < 5
94
+ raise "ip file not valid, please check."
95
+ end
96
+ end
97
+
98
+ ip_file.each_line do |ip_line|
99
+ temp = ip_line.split(",")
100
+ #f.puts "#{device.ip},#{device.sn},#{device.model},#{device.version},#{device.ip_from_sn}"
101
+ devices << Device.new(:ip => temp[0], :sn => temp[1], :model => temp[2], :version => temp[3], :to_change_ip => temp[4]) if temp[1] && !temp[1].start_with?("f")
102
+ end
103
+ devices
104
+ end
105
+
106
+ def self.validate_change_ip_csv_file!(file_name)
107
+ return false if file_name.nil? || !file_name.to_s.end_with?(".csv")
108
+
109
+ changed_sn = Array.new
110
+ changed_ip = Array.new
111
+ CSV.foreach(file_name) do |row|
112
+ abort "SN is blank!" if row[0].to_s.strip.length == 0
113
+ abort "SN length in file #{file_name} should be 8" if row[0].to_s.strip.length != 8
114
+ changed_sn << row[0].to_s.strip
115
+
116
+ abort "IP is blank!" if row[1].to_s.strip.length == 0
117
+ abort "IP #{row[1]} in file #{file_name} is an invalid ip string!" if row[1].to_s !~ IP_REGEX
118
+
119
+ changed_ip << row[1].to_s.strip
120
+ end
121
+
122
+ if changed_sn.uniq.size != changed_sn.size
123
+ abort "SN duplicated in file #{file_name}"
124
+ end
125
+
126
+ if changed_ip.uniq.size != changed_ip.size
127
+ abort "IP duplicated in file #{file_name}"
128
+ end
129
+ end
130
+
131
+ end
132
+ end
@@ -0,0 +1,32 @@
1
+ require 'socket'
2
+ require 'net/ssh'
3
+
4
+ module LunaScanner
5
+ class Logger
6
+ class << self
7
+ def info message, options={}
8
+ if options[:time] == false
9
+ $stderr.puts "\r#{message.to_s}"
10
+ else
11
+ $stderr.puts "\r#{Time.now.strftime("%H:%M:%S")} > #{message.to_s}"
12
+ end
13
+ end
14
+
15
+ def success message, options={}
16
+ if options[:time] == false
17
+ $stdout.puts "\r\e[32m#{message.to_s}\e[0m"
18
+ else
19
+ $stdout.puts "\r#{Time.now.strftime("%H:%M:%S")} > \e[32m#{message.to_s}\e[0m"
20
+ end
21
+ end
22
+
23
+ def error message, options={}
24
+ if options[:time] == false
25
+ $stderr.puts "\r\e[33m#{message.to_s}\e[0m"
26
+ else
27
+ $stderr.puts "\r#{Time.now.strftime("%H:%M:%S")} > \e[33m#{message.to_s}\e[0m"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,128 @@
1
+ #encoding: utf-8
2
+
3
+ require 'erb'
4
+ require 'net/ssh'
5
+ require 'net/scp'
6
+
7
+ module LunaScanner
8
+ class Rcommand # mean remote command execute.
9
+
10
+ @@ip_max_length = 7 #1.1.1.1
11
+ @@total_devices_count = 0
12
+ @@success_devices_count = 0
13
+
14
+ def reboot!(ip)
15
+ return false if ip.nil?
16
+
17
+ Logger.info "Try to reboot #{ip} ..."
18
+ begin
19
+ Net::SSH.start(
20
+ "#{ip}", 'root',
21
+ :auth_methods => ["publickey"],
22
+ :user_known_hosts_file => "/dev/null",
23
+ :timeout => 3,
24
+ :keys => [ "#{LunaScanner.root}/keys/yu_pri" ] # Fix key permission: chmod g-wr ./yu_pri chmod o-wr ./yu_pri chmod u-w ./yu_pri
25
+ ) do |session|
26
+ session.exec!("reboot")
27
+ end
28
+ rescue
29
+ Logger.error " #{ip} no response. #{$!.message}"
30
+ end
31
+ end
32
+
33
+ def change_ip(connect_device, is_reboot)
34
+ if connect_device.ip == ''
35
+ Logger.error "Device #{connect_device.sn} not found in LAN."
36
+ return false
37
+ end
38
+
39
+ begin
40
+ LunaScanner.start_ssh(connect_device.ip) do |shell|
41
+ Logger.info "Changed device #{connect_device.sn} [#{connect_device.ip.ljust(@@ip_max_length)}] to new ip #{connect_device.to_change_ip.ljust(15)}"
42
+ shell.exec!("echo '#{connect_device.new_ip}' > /etc/network/interfaces")
43
+ shell.exec!("reboot") if is_reboot
44
+ end
45
+ rescue
46
+ Logger.error "Failed to change device #{connect_device.sn} to new ip #{connect_device.new_ip.ljust(15)} #Error reason: #{$!.message}"
47
+ return false
48
+ else
49
+ return true
50
+ end
51
+ end
52
+
53
+ def self.batch_change_ip(options={})
54
+ target_devices = Device.load_from_file(options[:input_ip])
55
+ @@total_devices_count = target_devices.size
56
+
57
+ Logger.info "-> Start batch change ip.", :time => false
58
+
59
+ thread_pool = []
60
+ 10.times do
61
+ ssh_thread = Thread.new do
62
+ go = true
63
+ while go
64
+ device = target_devices.pop
65
+ if device.nil?
66
+ go = false
67
+ else
68
+ rcommand = self.new
69
+ if rcommand.change_ip(device, options[:reboot])
70
+ @@success_devices_count += 1
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ thread_pool << ssh_thread
77
+ end
78
+
79
+ thread_pool.each{|thread| thread.join }
80
+
81
+ Logger.info "\n#{@@success_devices_count}/#{@@total_devices_count} devices changed.", :time => false
82
+ Logger.info "Restart all devices to make changes work.", :time => false if !options[:reboot]
83
+ end
84
+
85
+ def self.batch_update(options={})
86
+ update_script_file = File.expand_path("../update_firmware.sh", __FILE__)
87
+ target_devices = Device.load_from_file(options[:input_ip])
88
+ Logger.info "-> Start batch update.", :time => false
89
+
90
+ thread_pool = []
91
+ 10.times do
92
+ ssh_thread = Thread.new do
93
+ go = true
94
+ while go
95
+ device = target_devices.pop
96
+ if device.nil?
97
+ go = false
98
+ else
99
+ begin
100
+
101
+ Logger.info "-> Update #{device.ip} #{device.sn}"
102
+ LunaScanner.start_ssh(device.ip) do |shell|
103
+ shell.scp.upload!("#{update_script_file}", "/usr/local/luna-client/script/update_firmware.sh")
104
+ shell.exec!("chmod a+x /usr/local/luna-client/script/update_firmware.sh")
105
+ shell.exec!("sed -i 's/iface eth0 inet dhcp/iface eth0 inet static\naddress 0.0.0.0/' /etc/network/interfaces")
106
+ shell.exec!("/usr/local/luna-client/script/update_firmware.sh http://8.128.3.254 1500k")
107
+ end
108
+
109
+
110
+ rescue
111
+ Logger.error "Failed to update device #{device.sn} #{device.ip} #{$!.message}"
112
+ # return false
113
+ else
114
+ # return true
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ thread_pool << ssh_thread
121
+ end
122
+
123
+ thread_pool.each{|thread| thread.join }
124
+
125
+ end
126
+
127
+ end
128
+ end
@@ -0,0 +1,156 @@
1
+ require 'socket'
2
+ require 'net/ssh'
3
+
4
+ module LunaScanner
5
+ class Scanner
6
+
7
+ @@found_devices = Array.new
8
+
9
+ def initialize(thread_size, start_ip, end_ip)
10
+ raise "thread pool size not correct!" if thread_size.to_i <= 0
11
+ @thread_size = thread_size.to_i
12
+ Logger.info "Local ip #{LunaScanner.local_ip}"
13
+
14
+ @scan_ip_range = Util.ip_range(start_ip, end_ip)
15
+ end
16
+
17
+ def generate_ip_range(options)
18
+ if options[:input_ip].to_s.length > 0 # get ip range from file
19
+ raise "IP input file not exist." if !File.exist?(options[:input_ip])
20
+
21
+
22
+ else # get ip range from start_ip to end_ip
23
+ if options[:start_ip].to_s.length == 0 || options[:end_ip].to_s.length == 0
24
+ raise "Require arguments to get ip range"
25
+ end
26
+
27
+
28
+ end
29
+
30
+ end
31
+
32
+ def scan(is_reboot, shell_command)
33
+ thread_pool = []
34
+ @scan_ip_range.reverse!
35
+ @thread_size.times do
36
+ ssh_thread = Thread.new do
37
+ go = true
38
+ while go
39
+ ip = @scan_ip_range.pop
40
+ if ip.nil?
41
+ go = false
42
+ else
43
+ begin
44
+ Logger.info "Scan ip #{ip} ..."
45
+ LunaScanner.start_ssh(ip) do |shell|
46
+ sn = shell.exec!('cat /proc/itc_sn/sn').chomp
47
+ model = shell.exec!('cat /proc/itc_sn/model').chomp
48
+ version = shell.exec!('cat /jenkins_version.txt').chomp
49
+
50
+ if shell_command.to_s.length > 0
51
+ Logger.info " execute command: #{shell_command} ...", :time => false
52
+ shell.exec!("#{shell_command}")
53
+ end
54
+
55
+ if sn != "cat: /proc/itc_sn/sn: No such file or directory"
56
+ @@found_devices << Device.new(:ip => ip, :sn => sn, :model => model, :version => version)
57
+ Logger.success " #{ip} #{sn} #{model} #{version}", :time => false
58
+ shell.exec!("reboot") if is_reboot
59
+ end
60
+ end
61
+ rescue
62
+ Logger.error " #{ip} no response. #{$!.message}", :time => false
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ thread_pool << ssh_thread
69
+ end
70
+
71
+ thread_pool.each{|thread| thread.join }
72
+ end
73
+
74
+ def self.scan!(options={})
75
+ start_ip = options[:start_ip] || Util.begin_ip(LunaScanner.local_ip)
76
+ end_ip = options[:end_ip] || Util.end_ip(LunaScanner.local_ip)
77
+ scanner = self.new(options[:thread_size], start_ip, end_ip)
78
+
79
+ Logger.info "Start scan from #{start_ip} to #{end_ip} #{options[:reboot] ? '(reboot)' : ''} ..."
80
+ scanner.scan(options[:reboot], options[:command])
81
+
82
+ Logger.info "\n#{Device.display_header}", :time => false
83
+ @@found_devices.each do |device|
84
+ Logger.success device.display, :time => false
85
+ end
86
+ Logger.info "\n#{@@found_devices.size} devices found. #{options[:reboot] ? '(reboot)' : ''}", :time => false
87
+ if options[:result]
88
+ begin
89
+ File.open(options[:result], "w") do |f|
90
+ @@found_devices.each do |device|
91
+ f.puts "#{device.ip},#{device.sn},#{device.model},#{device.version},#{device.ip_from_sn}"
92
+ end
93
+ end
94
+ rescue
95
+ Logger.error "Failed to write scan result to #{options[:result]}", :time => false
96
+ else
97
+ Logger.error "Write scan result to #{options[:result]}", :time => false
98
+ end
99
+ end
100
+ Logger.info "\n", :time => false
101
+ end
102
+
103
+ def self.upload!(source_file, target_file, options={}, &block)
104
+ require 'net/scp'
105
+ if options[:input_ip] # Scan from given input ip file.
106
+ source_devices = File.read(options[:input_ip])
107
+ upload_hosts = Array.new
108
+ source_devices.each_line do |device|
109
+ ip,sn,model,version = device.split(" ")
110
+ upload_hosts << ip
111
+ end
112
+ upload_hosts.uniq!
113
+
114
+ return if upload_hosts.size == 0
115
+
116
+ thread_pool = []
117
+ options[:thread_size].times do
118
+ ssh_thread = Thread.new do
119
+ go = true
120
+ while go
121
+ ip = upload_hosts.pop
122
+ if ip.nil?
123
+ go = false
124
+ else
125
+ begin
126
+ Logger.info "Connect to ip #{ip} ..."
127
+ LunaScanner.start_ssh(ip) do |shell|
128
+ Logger.info " upload file #{source_file} to #{ip} #{target_file}", :time => false
129
+ shell.scp.upload!(source_file, target_file)
130
+ block.call(shell) if block
131
+ end
132
+ rescue
133
+ Logger.error " #{ip} not connected. #{$!.message}"
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ thread_pool << ssh_thread
140
+ end
141
+
142
+ thread_pool.each{|thread| thread.join }
143
+
144
+ else
145
+ # self.scan!(options)
146
+ puts "Not implement yet."
147
+ exit 4
148
+ end
149
+ end
150
+
151
+ def self.found_devices
152
+ @@found_devices
153
+ end
154
+
155
+ end
156
+ end