luna_scanner 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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