luna_scanner 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +18 -0
- data/Changelog.txt +10 -0
- data/Gemfile +4 -0
- data/INSTALL +21 -0
- data/LICENSE.txt +22 -0
- data/README.md +99 -0
- data/Rakefile +14 -0
- data/bin/luna_scanner +7 -0
- data/lib/luna_scanner.rb +66 -0
- data/lib/luna_scanner/cli.rb +159 -0
- data/lib/luna_scanner/device.rb +132 -0
- data/lib/luna_scanner/logger.rb +32 -0
- data/lib/luna_scanner/rcommand.rb +128 -0
- data/lib/luna_scanner/scanner.rb +156 -0
- data/lib/luna_scanner/update_firmware.sh +273 -0
- data/lib/luna_scanner/util.rb +64 -0
- data/lib/luna_scanner/version.rb +3 -0
- data/lib/luna_scanner/web.rb +98 -0
- data/luna_scanner.gemspec +29 -0
- data/test/device_test.rb +77 -0
- data/test/local_ip_test.rb +14 -0
- data/test/logger_test.rb +16 -0
- data/test/luna_scanner_test.rb +21 -0
- data/test/test_helper.rb +11 -0
- data/test/web_test.rb +2 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZDVhMmFmOGU4NzgyY2I3NTIzMzNjNWYzOWJiNDFhNTVkMDM2ZDFiNg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NTE5YzNhOGJjNjY1ZDM4NzdmMzJiODNhMTFlZTY0ZDlhYTk0N2RmYg==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OTY4ZDNhZWUwYjgwYzRlZGQxMWQ5YTQzYjc4ZTJmYzZmM2FmYjBjMDhmYjlm
|
10
|
+
M2RlYmM2NWI1MWYwOWFiZDA3NzU5NWJmMjE4YjgwMmQ1MTBkMGQ1YzIyN2Fl
|
11
|
+
ZjVmNGU5NTFkMTUwYjBkY2UzNzYyMjU2MzVmZmQxMjlhN2VjOWY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MmMwNTI5OWE1Nzc4YzFmY2QxOTBiNWYwYjMxMGVhOWMwNjE0YTg0ZmM2OTc3
|
14
|
+
ZmQ1YTIzMTNhZDIyNmIwZmI1NzUyNzhhZWU4ZDU1NDg4MDc0YzkyMjM2ZTYx
|
15
|
+
ZDkyZTcyYTJkYTg3YjViMmQ0YmI0YThhMDBkNmMzNDM1YzE2MTA=
|
data/.gitignore
ADDED
data/Changelog.txt
ADDED
data/Gemfile
ADDED
data/INSTALL
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
gem build luna_scanner.gemspec
|
4
|
+
|
5
|
+
cd vendor/cache
|
6
|
+
gem install --no-ri --no-rdoc --local net-ssh-2.7.0.gem
|
7
|
+
gem install --no-ri --no-rdoc --local net-scp-1.1.2.gem
|
8
|
+
gem install --no-ri --no-rdoc --local rack-1.5.2.gem
|
9
|
+
gem install --no-ri --no-rdoc --local rack-protection-1.5.1.gem
|
10
|
+
gem install --no-ri --no-rdoc --local rake-10.1.0.gem
|
11
|
+
gem install --no-ri --no-rdoc --local sinatra-1.4.4.gem
|
12
|
+
gem install --no-ri --no-rdoc --local tilt-1.4.1.gem
|
13
|
+
gem install --no-ri --no-rdoc --local tilt-1.4.1.gem
|
14
|
+
|
15
|
+
echo "Installed dependency."
|
16
|
+
|
17
|
+
cd ../..
|
18
|
+
gem install --no-ri --no-rdoc --local luna_scanner-0.0.2.gem
|
19
|
+
rm -f luna_scanner-0.0.2.gem
|
20
|
+
|
21
|
+
echo "\n Installed success."
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 qichunren
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# LunaScanner
|
2
|
+
|
3
|
+
LunaScanner是一个局域网中设备扫描的命令行工具。它可以批量局域网中设备,批量执行远程命令。
|
4
|
+
|
5
|
+
## 使用场景
|
6
|
+
|
7
|
+
我们在做的一套系统中包括服务器和许多客户端机器,它们都是Linux机器。这些客户端机器的系统(固件)都是统一刷机部署的。日常管理客户端机器是通
|
8
|
+
过SSH加上一个private key登录进入终端,执行相应的SHELL命令来完成操作的。在平时开发和测试的过程中,经常需要统一地批量修改当前网络中客户端
|
9
|
+
系统中的某一个文件或者重启等各种操作,于是我就创建了这个工具。
|
10
|
+
|
11
|
+
## 安装
|
12
|
+
|
13
|
+
```
|
14
|
+
$ gem install luna_scanner
|
15
|
+
```
|
16
|
+
|
17
|
+
## 使用方法
|
18
|
+
|
19
|
+
`luna_scanner`
|
20
|
+
直接输入这个命令是扫描当前网段中的客户端Linux机器,如当前机器的IP是192.168.1.23,那么扫描的网段是192.168.1.1到192.168.1.255
|
21
|
+
|
22
|
+
参数:
|
23
|
+
|
24
|
+
--ip_range 是用于设置扫描的网段,如--ip_range=192.168.0.1,192.168.4.255
|
25
|
+
|
26
|
+
-t 是用于设置扫描的并发线程数,如-t 180,这个数值默认是120,建议不要设置得过高
|
27
|
+
|
28
|
+
-r 是用于设置扫描结果的文件,默认是/tmp/lunascan.txt
|
29
|
+
|
30
|
+
luna_scanner reboot
|
31
|
+
|
32
|
+
luna_scanner --ip_range=192.168.0.1,192.168.4.255 -r 192_ip.txt
|
33
|
+
|
34
|
+
luna_scanner change_ip -i 10_ip.txt
|
35
|
+
|
36
|
+
luna_scanner upload --source_file update_firmware.sh --target_file /usr/local/luna-client/script/update_firmware.sh -i to_be_update_devices.txt -c 'chmod a+x /usr/local/luna-client/script/update_firmware.sh'
|
37
|
+
|
38
|
+
luna_scanner upload --source_file update_firmware.sh --target_file /tmp/no_use.sh -i to_be_update_devices.txt -c '/usr/local/luna-client/script/update_firmware.sh http://192.168.3.100 900k'
|
39
|
+
|
40
|
+
luna_scanner upload --source_file /Users/qichunren/Downloads/update_firmware.sh --target_file /usr/local/luna-client/script/update_firmware.sh -c 'chmod a+x /usr/local/luna-client/script/update_firmware.sh && /usr/local/luna-client/script/update_firmware.sh http://10.0.4.48 900k' -i 10_ip.txt
|
41
|
+
|
42
|
+
### 批量更新终端设备
|
43
|
+
|
44
|
+
1. 批量扫描当前网段的设置
|
45
|
+
2. 修改扫描结果的文件,只留下需要更新的设备的文本行
|
46
|
+
3. 远程更新命令批量执行
|
47
|
+
|
48
|
+
|
49
|
+
luna_scanner --ip_range=10.0.0.1,10.0.15.150 -t 150 -r 10_ip.txt
|
50
|
+
|
51
|
+
<pre>
|
52
|
+
------IP-----------SN---------MODEL-----VERSION------
|
53
|
+
10.0.6.27 00000389 T-7203D 20131112.0506
|
54
|
+
10.0.6.221 00000208 T-7203D 20131112.0506
|
55
|
+
10.0.10.227 00000032 T-7203D 20131209.2239
|
56
|
+
10.0.11.40 00000307 T-7203D 20131112.0506
|
57
|
+
10.0.13.220 00000262 T-7203D 20131112.0506
|
58
|
+
|
59
|
+
5 devices found.
|
60
|
+
Write scan result to /Users/qichunren/RubymineProjects/luna_scanner/10_ip.txt
|
61
|
+
</pre>
|
62
|
+
|
63
|
+
vim 10_ip.txt # Remote device which ip is '10.0.10.227', it is latest version, so I remove this text line.
|
64
|
+
# Because new version update_firmware.sh file is not compatible with old clone, so before we update firmware, we should upload the new one to replace old one.
|
65
|
+
# Then invoke update command to upgrade firmware from remote luna-server.
|
66
|
+
luna_scanner upload --source_file /Users/qichunren/Downloads/update_firmware.sh --target_file /usr/local/luna-client/script/update_firmware.sh -c 'chmod a+x /usr/local/luna-client/script/update_firmware.sh && /usr/local/luna-client/script/update_firmware.sh http://10.0.4.48 1300k' -i 10_ip.txt
|
67
|
+
|
68
|
+
## Contributing
|
69
|
+
|
70
|
+
1. Fork it
|
71
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
72
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
73
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
74
|
+
5. Create new Pull Request
|
75
|
+
|
76
|
+
|
77
|
+
scp ~/Downloads/update_firmware.sh root@10.0.6.12:/usr/local/luna-client/script/update_firmware.sh
|
78
|
+
scp ~/Downloads/update_firmware.sh root@10.0.7.156:/usr/local/luna-client/script/update_firmware.sh
|
79
|
+
|
80
|
+
|
81
|
+
### Work Step
|
82
|
+
|
83
|
+
1. Scan
|
84
|
+
luna_scanner --ip_range 192.168.0.1,192.168.3.254 -t 150
|
85
|
+
luna_scanner --ip_range 8.128.2.0,8.128.3.254 -t 150 -r /tmp/lunascan8.txt
|
86
|
+
luna_scanner --ip_range 10.0.0.0,10.0.15.254 -t 150 -r /tmp/lunascan8.txt
|
87
|
+
|
88
|
+
luna_scanner --ip_range 8.128.0.0,8.128.1.254 -t 150 -r /tmp/lunascan0.txt
|
89
|
+
|
90
|
+
|
91
|
+
cat /tmp/lunascan8.txt | grep U > /tmp/lunascan.txt
|
92
|
+
|
93
|
+
2. Update
|
94
|
+
luna_scanner update # /tmp/lunascan.txt
|
95
|
+
|
96
|
+
luna_scanner change_ip --reboot
|
97
|
+
|
98
|
+
|
99
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
### Gem local install.
|
4
|
+
#
|
5
|
+
# bundle
|
6
|
+
# bundle package --all
|
7
|
+
# cd vendor/cache
|
8
|
+
# gem install --no-ri --no-rdoc --local net-ssh-2.7.0.gem
|
9
|
+
# gem install --no-ri --no-rdoc --local net-scp-1.1.2.gem
|
10
|
+
# gem install --no-ri --no-rdoc --local rack-1.5.2.gem
|
11
|
+
# gem install --no-ri --no-rdoc --local rack-protection-1.5.1.gem
|
12
|
+
# gem install --no-ri --no-rdoc --local rake-10.1.0.gem
|
13
|
+
# gem install --no-ri --no-rdoc --local sinatra-1.4.4.gem
|
14
|
+
# gem install --no-ri --no-rdoc --local tilt-1.4.1.gem
|
data/bin/luna_scanner
ADDED
data/lib/luna_scanner.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'net/ssh'
|
3
|
+
require "luna_scanner/version"
|
4
|
+
|
5
|
+
module LunaScanner
|
6
|
+
|
7
|
+
class SSHKeyNotFoundError < StandardError; end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def root
|
11
|
+
@root_path ||= File.expand_path("../", __FILE__)
|
12
|
+
end
|
13
|
+
|
14
|
+
def pwd
|
15
|
+
@pwd ||= Dir.pwd
|
16
|
+
end
|
17
|
+
|
18
|
+
def local_ip
|
19
|
+
@local_ip ||= begin
|
20
|
+
orig = Socket.do_not_reverse_lookup
|
21
|
+
Socket.do_not_reverse_lookup =true # turn off reverse DNS resolution temporarily
|
22
|
+
UDPSocket.open do |s|
|
23
|
+
s.connect '64.233.187.99', 1 #google
|
24
|
+
s.addr.last
|
25
|
+
end
|
26
|
+
rescue
|
27
|
+
|
28
|
+
ensure
|
29
|
+
Socket.do_not_reverse_lookup = orig
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def ssh_key
|
34
|
+
@ssh_key ||= Dir.home.to_s + "/yu_pri"
|
35
|
+
end
|
36
|
+
|
37
|
+
def check_ssh_key!
|
38
|
+
raise SSHKeyNotFoundError.new("Please provide ssh key file: #{ssh_key}") if not File.exist?(ssh_key)
|
39
|
+
|
40
|
+
ssh_key
|
41
|
+
end
|
42
|
+
|
43
|
+
def start_ssh(ip, &block)
|
44
|
+
Net::SSH.start(
|
45
|
+
"#{ip}", 'root',
|
46
|
+
:auth_methods => ["publickey"],
|
47
|
+
:user_known_hosts_file => "/dev/null",
|
48
|
+
:timeout => 8,
|
49
|
+
:verbose => :error,
|
50
|
+
:keys => [ ssh_key ] # Fix key permission: chmod g-wr ./yu_pri chmod o-wr ./yu_pri chmod u-w ./yu_pri
|
51
|
+
) do |session|
|
52
|
+
block.call(session)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
LunaScanner.autoload :Logger, "luna_scanner/logger"
|
61
|
+
LunaScanner.autoload :Device, "luna_scanner/device"
|
62
|
+
LunaScanner.autoload :Util, "luna_scanner/util"
|
63
|
+
LunaScanner.autoload :CLI, "luna_scanner/cli"
|
64
|
+
LunaScanner.autoload :Rcommand, "luna_scanner/rcommand"
|
65
|
+
LunaScanner.autoload :Scanner, "luna_scanner/scanner"
|
66
|
+
LunaScanner.autoload :Web, "luna_scanner/web"
|
@@ -0,0 +1,159 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
module LunaScanner
|
5
|
+
class CLI
|
6
|
+
|
7
|
+
def initialize()
|
8
|
+
@options = {
|
9
|
+
:thread_size => 120,
|
10
|
+
:reboot => false,
|
11
|
+
:result => "/tmp/lunascan.txt",
|
12
|
+
:start_ip => nil,
|
13
|
+
:end_ip => nil,
|
14
|
+
:source_file => nil,
|
15
|
+
:target_file => nil,
|
16
|
+
:input_ip => "/tmp/lunascan.txt",
|
17
|
+
:command => ""
|
18
|
+
}
|
19
|
+
option_parser = OptionParser.new do |opts|
|
20
|
+
opts.banner = 'Luna Scanner toolkit.'
|
21
|
+
|
22
|
+
opts.on('-t', '--thread_size COUNT', 'Set luna_scanner scan thread size.') do |thread_size|
|
23
|
+
@options[:thread_size] = thread_size.to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
# TODO include extra ip, exclude extra ip from --ip_range option
|
27
|
+
opts.on('--ip_range START_IP,END_IP', 'Set luna_scanner scan ip range.') do |ip_param|
|
28
|
+
ip_range = ip_param.split(",")
|
29
|
+
@options[:start_ip] = ip_range[0]
|
30
|
+
@options[:end_ip] = ip_range[1]
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-i', '--input INPUT_IP', 'Scan from given INPUT_IP file.') do |file|
|
34
|
+
#TODO fixed path string, such as ~ or ./
|
35
|
+
if file && file.start_with?("/")
|
36
|
+
@options[:input_ip] = file
|
37
|
+
else
|
38
|
+
@options[:input_ip] = LunaScanner.pwd + "/" + file
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('-r', '--result RESULT_FILE', 'Store scan result to file.') do |result_file|
|
43
|
+
#TODO fixed path string, such as ~ or ./
|
44
|
+
if result_file && result_file.start_with?("/")
|
45
|
+
@options[:result] = result_file
|
46
|
+
else
|
47
|
+
@options[:result] = LunaScanner.pwd + "/" + result_file
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('--reboot', 'Reboot devices.') do
|
52
|
+
@options[:reboot] = true
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('--source_file SOURCE_FILE', 'Source file to upload to remote. Only be used for [upload] action') do |file|
|
56
|
+
#TODO fixed path string, such as ~ or ./
|
57
|
+
if file && file.start_with?("/")
|
58
|
+
@options[:source_file] = file
|
59
|
+
else
|
60
|
+
@options[:source_file] = LunaScanner.pwd + "/" + file
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on('--target_file TARGET_FILE', 'File to upload to target place. Only be used for [upload] action') do |file|
|
65
|
+
if file && file.start_with?("/")
|
66
|
+
@options[:target_file] = file
|
67
|
+
else
|
68
|
+
puts "--target_file option value must start with / absolute path"
|
69
|
+
exit 1
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on('-c', '--command SHELL_COMMAND', 'Shell command execute on remote devices. Only be used for [upload] action') do |command_string|
|
74
|
+
@options[:command] = command_string
|
75
|
+
end
|
76
|
+
|
77
|
+
opts.on_tail("-h", "--help", "luna_scanner usage.") do
|
78
|
+
puts "Luna Scanner usage:"
|
79
|
+
puts " luna_scanner [action] [option]"
|
80
|
+
puts " luna_scanner -> Scan currnet LAN devices with default configuration"
|
81
|
+
puts " luna_scanner -t 60 -> Set scan thread size to 60"
|
82
|
+
puts " luna_scanner change_ip -> Batch change LAN devices ip configuration"
|
83
|
+
puts " luna_scanner upload --source_file file1 --target_file file2 -> Upload file1 to file2 on LAN devices"
|
84
|
+
puts " luna_scanner web -> Start luna_scanner web ui on 4567 port (To be done)"
|
85
|
+
puts " luna_scanner -v -> Display luna_scanner version"
|
86
|
+
puts "\n"
|
87
|
+
puts opts
|
88
|
+
exit 1
|
89
|
+
end
|
90
|
+
|
91
|
+
opts.on('-v', '--version', 'Display luna_scanner version.') do
|
92
|
+
puts "luna_scanner v#{LunaScanner::VERSION}"
|
93
|
+
exit 1
|
94
|
+
end
|
95
|
+
|
96
|
+
end.parse!
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
def execute
|
101
|
+
if ARGV[0].to_s == ''
|
102
|
+
LunaScanner.check_ssh_key!
|
103
|
+
LunaScanner::Scanner.scan!(@options)
|
104
|
+
elsif ARGV[0].to_s == 'reboot'
|
105
|
+
LunaScanner.check_ssh_key!
|
106
|
+
@options[:reboot] = true
|
107
|
+
LunaScanner::Scanner.scan!(@options)
|
108
|
+
elsif ARGV[0].to_s == 'upload'
|
109
|
+
LunaScanner.check_ssh_key!
|
110
|
+
source_file = @options.delete(:source_file)
|
111
|
+
target_file = @options.delete(:target_file)
|
112
|
+
if source_file.nil? || target_file.nil?
|
113
|
+
puts "--source_file SOURCE_FILE or --target_file TARGET_FILE options missing for upload action."
|
114
|
+
exit 1
|
115
|
+
end
|
116
|
+
if !File.exist?(source_file)
|
117
|
+
puts "Source file #{source_file} not exist."
|
118
|
+
exit 2
|
119
|
+
end
|
120
|
+
if @options[:input_ip] && !File.exist?(@options[:input_ip])
|
121
|
+
puts "Input ip file #{@options[:input_ip]} not exist."
|
122
|
+
exit 3
|
123
|
+
end
|
124
|
+
|
125
|
+
LunaScanner::Scanner.upload!(source_file, target_file, @options) do |shell|
|
126
|
+
shell.exec!(@options[:command]) if @options[:command].length > 0
|
127
|
+
end
|
128
|
+
elsif ARGV[0].to_s == 'web'
|
129
|
+
LunaScanner::Web.run!
|
130
|
+
elsif ARGV[0].to_s == 'update'
|
131
|
+
LunaScanner.check_ssh_key!
|
132
|
+
|
133
|
+
if @options[:input_ip].nil? || !File.exist?(@options[:input_ip])
|
134
|
+
puts "Input ip file #{@options[:input_ip]} not exist."
|
135
|
+
exit 3
|
136
|
+
end
|
137
|
+
|
138
|
+
LunaScanner::Rcommand.batch_update(@options)
|
139
|
+
elsif ARGV[0].to_s == 'change_ip'
|
140
|
+
LunaScanner.check_ssh_key!
|
141
|
+
|
142
|
+
if @options[:input_ip].nil? || !File.exist?(@options[:input_ip])
|
143
|
+
puts "Input ip file #{@options[:input_ip]} not exist."
|
144
|
+
exit 3
|
145
|
+
end
|
146
|
+
|
147
|
+
LunaScanner::Rcommand.batch_change_ip(@options)
|
148
|
+
|
149
|
+
else
|
150
|
+
puts "Invalid action / options"
|
151
|
+
exit 1
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.start
|
156
|
+
self.new.execute
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|