isaac_toolbelt 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +103 -0
- data/Rakefile +10 -0
- data/bin/console +16 -0
- data/bin/isaac +6 -0
- data/bin/setup +7 -0
- data/isaac_toolbelt.gemspec +39 -0
- data/lib/isaac_toolbelt.rb +548 -0
- data/lib/isaac_toolbelt/version.rb +3 -0
- metadata +182 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 40358e002bf9fe9c20944a4e8c2ec5f1bec045c7
|
4
|
+
data.tar.gz: 8f57377a2edb23046216096fcc974729fdfad333
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ee3a5322e9b329e1bd8634026e5f39d208315b7b2302c346f226fb317c8f2f00ea5f24de3f030c4f3dfeb53888afcd04b8cb03e795daf3520b684ef94465d859
|
7
|
+
data.tar.gz: 446643e8c4e3fc45bb20070abc0a86cd7ae666175df41617e5060204cb3a530159396b75bf1960475870886cb7d8ce4c0bee0a54490eb629577cec23c3e128a9
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# IsaacToolbelt
|
2
|
+
|
3
|
+
CLI tool for issac.
|
4
|
+
It is inspired by [heroku toolbelt](https://toolbelt.heroku.com/).
|
5
|
+
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
$ brew brew install dfu-util coreutils gnu-getopt
|
10
|
+
$ brew tap jlhonora/lsusb && brew install lsusb
|
11
|
+
$ gem install specific_install
|
12
|
+
$ gem specific_install git@github.com:xshellinc/isaac_toolbelt.git
|
13
|
+
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
Connect your SBC with USB cable.
|
18
|
+
|
19
|
+
### Show all commands
|
20
|
+
|
21
|
+
```sh
|
22
|
+
isaac help
|
23
|
+
```
|
24
|
+
|
25
|
+
### Show device informations
|
26
|
+
|
27
|
+
```sh
|
28
|
+
isaac device info
|
29
|
+
```
|
30
|
+
|
31
|
+
### Write access key
|
32
|
+
|
33
|
+
```sh
|
34
|
+
isaac device config YOUR_ACCESS_KEY
|
35
|
+
```
|
36
|
+
|
37
|
+
### Show device logs
|
38
|
+
|
39
|
+
```sh
|
40
|
+
isaac device logs
|
41
|
+
```
|
42
|
+
|
43
|
+
### Run an isaac app
|
44
|
+
|
45
|
+
```sh
|
46
|
+
cd path/to/isaac_project
|
47
|
+
vim main.py # At now, only the file named "main.py" is runnable.
|
48
|
+
isaac local start
|
49
|
+
```
|
50
|
+
|
51
|
+
### Run a command with isaac environment
|
52
|
+
|
53
|
+
```sh
|
54
|
+
cd path/to/isaac_project
|
55
|
+
isaac local run python
|
56
|
+
```
|
57
|
+
|
58
|
+
### Run shell on the SBC
|
59
|
+
|
60
|
+
```sh
|
61
|
+
isaac local console
|
62
|
+
```
|
63
|
+
|
64
|
+
## Development
|
65
|
+
|
66
|
+
### Setup
|
67
|
+
|
68
|
+
```sh
|
69
|
+
bundle install
|
70
|
+
```
|
71
|
+
|
72
|
+
### Debug in pry
|
73
|
+
|
74
|
+
```sh
|
75
|
+
bin/console
|
76
|
+
```
|
77
|
+
|
78
|
+
then, you can access the instance of IsaacToolbelt::Device named `device`:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
[1] pry(main)> device
|
82
|
+
=> #<IsaacToolbelt::Device:0x007ffbfa11f140
|
83
|
+
@_initializer=[[], {}, {}],
|
84
|
+
@_invocations={},
|
85
|
+
@args=[],
|
86
|
+
@options={},
|
87
|
+
@shell=#<Thor::Shell::Color:0x007ffbfa0a6fb0 @always_force=false, @base=#<IsaacToolbelt::Device:0x007ffbfa11f140 ...>, @mute=false, @padding=0>>
|
88
|
+
[2] pry(main)> cd device
|
89
|
+
[3] pry(#<IsaacToolbelt::Device>):1> ls
|
90
|
+
Thor::Base#methods: args args= options options= parent_options parent_options=
|
91
|
+
Thor::Invocation#methods: current_command_chain invoke invoke_all invoke_command invoke_task invoke_with_padding
|
92
|
+
Thor::Shell#methods: ask error file_collision no? print_in_columns print_table print_wrapped say say_status set_color shell shell= terminal_width with_padding yes?
|
93
|
+
IsaacToolbelt::Helper#methods: check_file config_network host mkdir_if_not reboot ssh_run_command temp_path transfer_files upload_string_as_file waitfor waitfor_network
|
94
|
+
IsaacToolbelt::Device#methods: _reboot config_access_key config_wifi help init show
|
95
|
+
self.methods: __pry__
|
96
|
+
instance variables: @_initializer @_invocations @args @options @shell
|
97
|
+
locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_
|
98
|
+
```
|
99
|
+
|
100
|
+
## Contributing
|
101
|
+
|
102
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/xshellinc/isaac_toolbelt.
|
103
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "isaac_toolbelt"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'pry'
|
14
|
+
|
15
|
+
device = IsaacToolbelt::Device.new
|
16
|
+
Pry.start self
|
data/bin/isaac
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'isaac_toolbelt/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "isaac_toolbelt"
|
8
|
+
spec.version = IsaacToolbelt::VERSION
|
9
|
+
spec.authors = ["xshell inc."]
|
10
|
+
spec.email = ["info@xshell.io"]
|
11
|
+
|
12
|
+
spec.summary = %q{Isaac CLI tool}
|
13
|
+
spec.description = %q{Isaac CLI tool}
|
14
|
+
spec.homepage = "https://isaacapp.io/"
|
15
|
+
|
16
|
+
## Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
17
|
+
## delete this section to allow pushing this gem to any host.
|
18
|
+
#if spec.respond_to?(:metadata)
|
19
|
+
# spec.metadata['allowed_push_host'] = ""
|
20
|
+
#else
|
21
|
+
# raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
22
|
+
#end
|
23
|
+
|
24
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
spec.bindir = "bin"
|
26
|
+
#spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.executables = ["isaac"]
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
31
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
32
|
+
spec.add_development_dependency "minitest"
|
33
|
+
spec.add_development_dependency "pry"
|
34
|
+
spec.add_dependency "sshkit"
|
35
|
+
spec.add_dependency "thor"
|
36
|
+
spec.add_dependency "curb"
|
37
|
+
spec.add_dependency "ruby-progressbar"
|
38
|
+
spec.add_dependency "archive"
|
39
|
+
end
|
@@ -0,0 +1,548 @@
|
|
1
|
+
require "isaac_toolbelt/version"
|
2
|
+
|
3
|
+
require 'digest/sha1'
|
4
|
+
require 'sshkit'
|
5
|
+
require 'sshkit/dsl'
|
6
|
+
require 'thor'
|
7
|
+
require 'yaml'
|
8
|
+
require 'curb'
|
9
|
+
require 'ruby-progressbar'
|
10
|
+
require 'archive'
|
11
|
+
require 'mkmf'
|
12
|
+
|
13
|
+
REQUIREMENTS_TXT = <<"EOF"
|
14
|
+
cffi==1.4.2
|
15
|
+
cryptography==1.2.1
|
16
|
+
ecdsa==0.13
|
17
|
+
enum34==1.1.2
|
18
|
+
honcho==0.6.6
|
19
|
+
idna==2.0
|
20
|
+
ipaddress==1.0.16
|
21
|
+
ndg-httpsclient==0.4.0
|
22
|
+
paho-mqtt==1.1
|
23
|
+
paramiko==1.16.0
|
24
|
+
pyasn1==0.1.9
|
25
|
+
pycparser==2.14
|
26
|
+
pycrypto==2.6.1
|
27
|
+
pyOpenSSL==0.15.1
|
28
|
+
PyYAML==3.11
|
29
|
+
requests==2.9.1
|
30
|
+
six==1.10.0
|
31
|
+
wheel==0.24.0
|
32
|
+
EOF
|
33
|
+
|
34
|
+
DEVICE_MODE = <<"EOS"
|
35
|
+
Usage:
|
36
|
+
device mode development # Set ISAAC to development mode.
|
37
|
+
device mode production # Set ISAAC to production mode.
|
38
|
+
EOS
|
39
|
+
|
40
|
+
## /etc/opkg/base-feeds.conf
|
41
|
+
#BASE_FEEDS_CONF = <<"EOF"
|
42
|
+
#src/gz all http://repo.opkg.net/edison/repo/all
|
43
|
+
#src/gz edison http://repo.opkg.net/edison/repo/edison
|
44
|
+
#src/gz core2-32 http://repo.opkg.net/edison/repo/core2-32
|
45
|
+
#EOF
|
46
|
+
#
|
47
|
+
## /etc/opkg/intel-iotdk.conf
|
48
|
+
#INTEL_IOTDK_CONF = <<"EOF"
|
49
|
+
#src intel-iotdk http://iotdk.intel.com/repos/2.0/intelgalactic
|
50
|
+
#src intel-all http://iotdk.intel.com/repos/2.0/iotdk/all
|
51
|
+
#src intel-i586 http://iotdk.intel.com/repos/2.0/iotdk/i586
|
52
|
+
#src intel-x86 http://iotdk.intel.com/repos/2.0/iotdk/x86
|
53
|
+
#EOF
|
54
|
+
#
|
55
|
+
## /etc/opkg/isaac-repo.conf
|
56
|
+
#ISAAC_REPO_CONF = <<"EOF"
|
57
|
+
#src isaac http://ec2-52-26-157-115.us-west-2.compute.amazonaws.com
|
58
|
+
#EOF
|
59
|
+
|
60
|
+
module MakeMakefile::Logging
|
61
|
+
@logfile = '/dev/null'
|
62
|
+
end
|
63
|
+
|
64
|
+
SSHKit::Backend::Netssh.configure do |ssh|
|
65
|
+
ssh.connection_timeout = 3
|
66
|
+
ssh.ssh_options = {
|
67
|
+
:auth_methods => ['keyboard-interactive'],
|
68
|
+
:paranoid => false,
|
69
|
+
:user_known_hosts_file => '/dev/null',
|
70
|
+
:keepalive_interval => 1,
|
71
|
+
:keepalive_maxcount => 3
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
module IsaacToolbelt
|
76
|
+
HOME = ENV['HOME']
|
77
|
+
ISAAC_DIR = File.join(HOME, '.isaac')
|
78
|
+
DEVICE_IP_ADDRESS = '192.168.2.15'
|
79
|
+
SSH_USERNAME = 'root'
|
80
|
+
SSH_OPTIONS = '-o PreferredAuthentications=keyboard-interactive -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ServerAliveInterval=1 -o ServerAliveCountMax=3'
|
81
|
+
TEMP_ROOT = '/tmp/isaac'
|
82
|
+
ISAAC_CONF_PATH = '/etc/isaac.conf'
|
83
|
+
ISAAC_EDISON_IMAGE_URL='https://s3-ap-northeast-1.amazonaws.com/isaac-os-images/edison/toFlash.tar.bz2'
|
84
|
+
|
85
|
+
module Helper
|
86
|
+
def check_file(filename)
|
87
|
+
unless File.exist?(File.join(Dir.pwd, filename))
|
88
|
+
STDERR.puts "#{filename} not found!"
|
89
|
+
exit(1)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def host
|
94
|
+
SSHKit::Host.new("#{SSH_USERNAME}@#{DEVICE_IP_ADDRESS}")
|
95
|
+
end
|
96
|
+
|
97
|
+
def temp_path
|
98
|
+
File.join(TEMP_ROOT, Digest::SHA1.hexdigest(Dir.pwd));
|
99
|
+
end
|
100
|
+
|
101
|
+
def mkdir_if_not(destination_path)
|
102
|
+
on host do
|
103
|
+
unless test("[ -d #{destination_path} ]")
|
104
|
+
execute :mkdir, '-p', destination_path
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def upload_string_as_file(string, dst)
|
110
|
+
on host do
|
111
|
+
io = StringIO.new(string)
|
112
|
+
upload! io, dst
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def transfer_files(destination_path)
|
117
|
+
check_file('Procfile')
|
118
|
+
check_file('.gitignore')
|
119
|
+
mkdir_if_not(destination_path)
|
120
|
+
puts "Uploading files to: #{destination_path}"
|
121
|
+
pwd = Dir.pwd
|
122
|
+
ret = system("rsync -av -e \"ssh #{SSH_OPTIONS}\" -C --include=\".env\" --exclude=\".git\" --filter=\":- .gitignore\" #{File.join(pwd, '/')} #{SSH_USERNAME}@#{DEVICE_IP_ADDRESS}:#{destination_path}")
|
123
|
+
end
|
124
|
+
|
125
|
+
def config_network
|
126
|
+
on host do
|
127
|
+
dns_settings = StringIO.new("nameserver 8.8.8.8\n")
|
128
|
+
upload! dns_settings, '/etc/resolv.conf'
|
129
|
+
execute :ip, *%w(route replace default via 192.168.2.1)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def waitfor
|
134
|
+
until system('ping -c 1 192.168.2.15 > /dev/null 2>&1') do
|
135
|
+
puts 'waiting board...'
|
136
|
+
sleep 1
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def waitfor_network
|
141
|
+
on host do
|
142
|
+
until test(:ping, *%w(-c 1 8.8.8.8)) do
|
143
|
+
puts 'The board waiting network...'
|
144
|
+
sleep 1
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def reboot
|
150
|
+
puts 'rebooting...'
|
151
|
+
ssh_run_command 'reboot'
|
152
|
+
sleep 10
|
153
|
+
waitfor
|
154
|
+
puts 'rebooted!'
|
155
|
+
end
|
156
|
+
|
157
|
+
#
|
158
|
+
# ssh options:
|
159
|
+
# -o LogLevel=info
|
160
|
+
#
|
161
|
+
def ssh_run_command(cmd_string)
|
162
|
+
Process.fork do
|
163
|
+
exec 'ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ServerAliveInterval=1 -o ServerAliveCountMax=3 -t %s@%s "%s"' % ['root', DEVICE_IP_ADDRESS, cmd_string]
|
164
|
+
end
|
165
|
+
Process.waitall
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class Local < Thor
|
170
|
+
extend Helper
|
171
|
+
|
172
|
+
# from https://github.com/ddollar/foreman/blob/59d87c91a2004e05f6b12d4b150dcb4c911eb31c/lib/foreman/cli.rb#L28
|
173
|
+
class << self
|
174
|
+
# Hackery. Take the run method away from Thor so that we can redefine it.
|
175
|
+
def is_thor_reserved_word?(word, type)
|
176
|
+
return false if word == "run"
|
177
|
+
super
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
desc 'start', 'Run the project on the debug mode device that connected via RNDIS'
|
182
|
+
def start
|
183
|
+
temp_path = self.class.temp_path
|
184
|
+
self.class.transfer_files(temp_path)
|
185
|
+
self.class.ssh_run_command("cd #{temp_path}; " + 'honcho start')
|
186
|
+
end
|
187
|
+
|
188
|
+
desc 'run', 'Run a command with .env on the debug mode device that connected via RNDIS'
|
189
|
+
def run(*args)
|
190
|
+
temp_path = self.class.temp_path
|
191
|
+
self.class.transfer_files(temp_path)
|
192
|
+
self.class.ssh_run_command("cd #{temp_path}; " + 'honcho run ' + args.shelljoin)
|
193
|
+
end
|
194
|
+
|
195
|
+
desc 'debug_node', 'Run node.js app on the device that connected via RNDIS'
|
196
|
+
def debug_node
|
197
|
+
temp_path = self.class.make_temp_dir(TEMP_ROOT)
|
198
|
+
result = self.class.transfer_files(temp_path)
|
199
|
+
if result
|
200
|
+
self.class.ssh_run_command('cd %s; npm install; node app.js' % [temp_path])
|
201
|
+
else
|
202
|
+
puts 'Transfer failed'
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
#map 'shell' => 'console'
|
207
|
+
#map 'shell' => 'shell_'
|
208
|
+
end
|
209
|
+
|
210
|
+
class Device < Thor
|
211
|
+
include IsaacToolbelt::Helper
|
212
|
+
|
213
|
+
def self.curl(url, save_dir)
|
214
|
+
filename = url.split('/').last
|
215
|
+
filepath = File.join(save_dir, filename)
|
216
|
+
curl = Curl::Easy.new
|
217
|
+
curl.url = url
|
218
|
+
curl.follow_location = true
|
219
|
+
print "Downloads: '#{curl.url}'"
|
220
|
+
File.open(filepath, 'wb') do |f|
|
221
|
+
bar = ProgressBar.create(:title => filename, :starting_at => 0)
|
222
|
+
curl.on_progress do |dl_total, dl_now, ul_total, ul_now|
|
223
|
+
if dl_total > dl_now
|
224
|
+
bar.total = dl_total
|
225
|
+
bar.progress = dl_now
|
226
|
+
end
|
227
|
+
true
|
228
|
+
end
|
229
|
+
curl.on_body do |data|
|
230
|
+
f << data; data.size
|
231
|
+
end
|
232
|
+
curl.perform
|
233
|
+
puts "=> '#{filepath}'"
|
234
|
+
end
|
235
|
+
return filename
|
236
|
+
end
|
237
|
+
|
238
|
+
desc 'init', 'Initialize SBC for use with isaac'
|
239
|
+
option :ssid, :aliases => '-s', :required => true, :type => :string, :desc => 'Specify WiFi SSID'
|
240
|
+
option :wpa, :aliases => '-w', :required => true, :type => :string, :desc => 'Specify WPA Key for the SSID'
|
241
|
+
option :access_key, :aliases => '-a', :required => false, :type => :string, :desc => 'Specify ISAAC access key for the device'
|
242
|
+
def init
|
243
|
+
unless !!find_executable('dfu-util')
|
244
|
+
STDERR.puts 'dfu-util is not installed!'
|
245
|
+
STDERR.puts 'try: brew install dfu-util'
|
246
|
+
exit 1
|
247
|
+
end
|
248
|
+
|
249
|
+
# donwload and extract yocto image
|
250
|
+
extract_path = prepare_os_image()
|
251
|
+
|
252
|
+
# execute flashall.sh
|
253
|
+
flash_os_image(extract_path)
|
254
|
+
|
255
|
+
# wait for reboot
|
256
|
+
waitfor
|
257
|
+
|
258
|
+
# configure WiFi
|
259
|
+
configure_wifi(options[:ssid], options[:wpa])
|
260
|
+
waitfor_network
|
261
|
+
|
262
|
+
# install pip dependencies for isaacd : hacky
|
263
|
+
on host do
|
264
|
+
execute :pip, *%w(install --upgrade pip)
|
265
|
+
execute :pip, *%w(install --upgrade setuptools)
|
266
|
+
execute :rm, *%w(-rf /usr/lib/python2.7/site-packages/distribute*)
|
267
|
+
requirements = StringIO.new(REQUIREMENTS_TXT)
|
268
|
+
dst = '/tmp/requirements.txt'
|
269
|
+
upload! requirements, dst
|
270
|
+
execute :pip, *%w(install -r), dst
|
271
|
+
end
|
272
|
+
|
273
|
+
## install opkg dependencies for isaacd : hacky
|
274
|
+
#upload_string_as_file(BASE_FEEDS_CONF, '/etc/opkg/base-feeds.conf')
|
275
|
+
#upload_string_as_file(INTEL_IOTDK_CONF, '/etc/opkg/intel-iotdk.conf')
|
276
|
+
#upload_string_as_file(ISAAC_REPO_CONF, '/etc/opkg/isaac-repo.conf')
|
277
|
+
#on host do
|
278
|
+
# execute :opkg, 'update'
|
279
|
+
# execute :opkg, *%w(install libmraa0)
|
280
|
+
#end
|
281
|
+
|
282
|
+
# init isaacd
|
283
|
+
if options[:access_key]
|
284
|
+
set_conf({'access_key' => options[:access_key]})
|
285
|
+
on host do
|
286
|
+
execute :systemctl, *%w(enable isaacd)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# wait until rebooted
|
291
|
+
reboot
|
292
|
+
end
|
293
|
+
|
294
|
+
desc 'info', 'Show device status'
|
295
|
+
def info
|
296
|
+
uname = get_uname()
|
297
|
+
conf = get_conf()
|
298
|
+
|
299
|
+
rjust_count = 20
|
300
|
+
|
301
|
+
print "Operating System: ".rjust(rjust_count, ' ')
|
302
|
+
puts "#{uname}"
|
303
|
+
|
304
|
+
print "Access Key: ".rjust(rjust_count, ' ')
|
305
|
+
puts "#{conf['access_key']}"
|
306
|
+
end
|
307
|
+
|
308
|
+
desc 'config:access_key ACCESS_KEY', 'Set ISAAC Access Token'
|
309
|
+
def config_access_key(access_key)
|
310
|
+
configure_access_key(access_key)
|
311
|
+
end
|
312
|
+
map 'config:access_key' => 'config_access_key'
|
313
|
+
|
314
|
+
desc 'config:wifi', 'Configure WiFi'
|
315
|
+
option :ssid, :aliases => '-s', :required => true, :type => :string, :desc => 'Specify WiFi SSID'
|
316
|
+
option :wpa, :aliases => '-w', :required => true, :type => :string, :desc => 'Specify WPA Key for the SSID'
|
317
|
+
def config_wifi
|
318
|
+
configure_wifi(options[:ssid], options[:wpa])
|
319
|
+
end
|
320
|
+
map 'config:wifi' => 'config_wifi'
|
321
|
+
|
322
|
+
desc 'console', 'Shell on the debug mode device that connected via RNDIS'
|
323
|
+
def console
|
324
|
+
ssh_run_command('')
|
325
|
+
end
|
326
|
+
|
327
|
+
desc 'logs', 'show logs on the device that connected via RNDIS'
|
328
|
+
def logs
|
329
|
+
ssh_run_command('journalctl -f')
|
330
|
+
end
|
331
|
+
|
332
|
+
desc 'mode', 'Set modes whether production mode or development mode.'
|
333
|
+
def mode(state)
|
334
|
+
on host do
|
335
|
+
if state == "development" then
|
336
|
+
execute :systemctl, *%w(stop isaacd)
|
337
|
+
execute :systemctl, *%w(disable isaacd)
|
338
|
+
elsif state == "production" then
|
339
|
+
execute :systemctl, *%w(enable isaacd)
|
340
|
+
execute :systemctl, *%w(start isaacd)
|
341
|
+
else
|
342
|
+
puts DEVICE_MODE
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
desc 'reboot', 'Reboot ISAAC OS'
|
348
|
+
def _reboot
|
349
|
+
reboot
|
350
|
+
end
|
351
|
+
map 'reboot' => '_reboot'
|
352
|
+
|
353
|
+
|
354
|
+
private
|
355
|
+
def prepare_os_image
|
356
|
+
easy = Curl::Easy.new
|
357
|
+
easy.follow_location = true
|
358
|
+
easy.url = ISAAC_EDISON_IMAGE_URL
|
359
|
+
images_path = File.join(ISAAC_DIR, 'images')
|
360
|
+
system("mkdir -p #{images_path}")
|
361
|
+
filename = easy.url.split('/').last
|
362
|
+
filepath = File.join(images_path, filename)
|
363
|
+
unless File.exist? filepath
|
364
|
+
print "Downloads: '#{easy.url}'"
|
365
|
+
File.open(filepath, 'wb') do|f|
|
366
|
+
bar = ProgressBar.create(:title => filename, :starting_at => 0)
|
367
|
+
easy.on_progress do |dl_total, dl_now, ul_total, ul_now|
|
368
|
+
bar.total = dl_total if dl_total > dl_now
|
369
|
+
bar.progress = dl_now
|
370
|
+
end
|
371
|
+
easy.on_body {|data| f << data; data.size }
|
372
|
+
easy.perform
|
373
|
+
puts "=> '#{filepath}'"
|
374
|
+
end
|
375
|
+
else
|
376
|
+
puts "#{filepath} already exists"
|
377
|
+
end
|
378
|
+
|
379
|
+
# extract
|
380
|
+
extract_dir = filename.split('.').first
|
381
|
+
extract_path = File.join(images_path, extract_dir)
|
382
|
+
unless Dir.exist?(extract_path)
|
383
|
+
puts "Extracting..."
|
384
|
+
Archive.extract(filepath, images_path)
|
385
|
+
end
|
386
|
+
|
387
|
+
return extract_path
|
388
|
+
end
|
389
|
+
|
390
|
+
def flash_os_image(extract_path)
|
391
|
+
base_dir = extract_path
|
392
|
+
usb_vid = '8087'
|
393
|
+
usb_pid = '0a99'
|
394
|
+
ifwi_dfu_file = File.join(base_dir, 'edison_ifwi-dbg')
|
395
|
+
var_dir = File.join(base_dir, 'u-boot-envs')
|
396
|
+
|
397
|
+
dfu_wait
|
398
|
+
|
399
|
+
puts "Flashing IFWI"
|
400
|
+
%w(00 01 02 03 04 05 06).each do |ifwi_number|
|
401
|
+
ifwi_file_path = File.join(extract_path, "edison_ifwi-dbg-#{ifwi_number}-dfu.bin")
|
402
|
+
dfu_ifwi_download(usb_vid, usb_pid, "ifwi#{ifwi_number}", ifwi_file_path)
|
403
|
+
dfu_ifwi_download(usb_vid, usb_pid, "ifwib#{ifwi_number}", ifwi_file_path)
|
404
|
+
end
|
405
|
+
|
406
|
+
puts "Flashing U-Boot"
|
407
|
+
uboot_file_path = File.join(extract_path, "u-boot-edison.bin")
|
408
|
+
dfu_download(usb_vid, usb_pid, "u-boot0", uboot_file_path)
|
409
|
+
|
410
|
+
variant_file_path = File.join(extract_path, "u-boot-envs/edison-blankcdc.bin")
|
411
|
+
|
412
|
+
puts "Flashing U-Boot Environment"
|
413
|
+
dfu_download(usb_vid, usb_pid, "u-boot-env0", variant_file_path)
|
414
|
+
|
415
|
+
puts "Flashing U-Boot Environment Backup"
|
416
|
+
dfu_download(usb_vid, usb_pid, "u-boot-env0", variant_file_path, true)
|
417
|
+
|
418
|
+
dfu_wait
|
419
|
+
|
420
|
+
puts "Flashing boot partition (kernel)"
|
421
|
+
kernel_file_path = File.join(extract_path, "edison-image-edison.hddimg")
|
422
|
+
dfu_download(usb_vid, usb_pid, "boot", kernel_file_path)
|
423
|
+
|
424
|
+
puts "Flashing rootfs"
|
425
|
+
rootfs_file_path = File.join(extract_path, "edison-image-edison.ext4")
|
426
|
+
dfu_download(usb_vid, usb_pid, "rootfs", rootfs_file_path, true)
|
427
|
+
end
|
428
|
+
|
429
|
+
def dfu_ifwi_download(usb_vid, usb_pid, alt_name, file_path)
|
430
|
+
if dfu_alt_name_exists?(usb_vid, usb_pid, alt_name)
|
431
|
+
dfu_download(usb_vid, usb_pid, alt_name, file_path)
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
def dfu_download(usb_vid, usb_pid, alt_name, file_path, do_reboot=false)
|
436
|
+
loop do
|
437
|
+
params = %W(dfu-util -v -d #{usb_vid}:#{usb_pid} --alt #{alt_name} -D #{file_path})
|
438
|
+
params << '-R' if do_reboot
|
439
|
+
_pid = Process.fork do
|
440
|
+
Process.exec *params
|
441
|
+
end
|
442
|
+
pid, status = Process.waitpid2(_pid)
|
443
|
+
puts "dfu-util exit with: #{status.to_i}"
|
444
|
+
if status.to_i == 0
|
445
|
+
break
|
446
|
+
else
|
447
|
+
sleep 1
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
def dfu_alt_name_exists?(usb_vid, usb_pid, alt_name)
|
453
|
+
dfu_list(usb_vid, usb_pid).select {|e|
|
454
|
+
e[:name] == alt_name
|
455
|
+
}.count > 0
|
456
|
+
end
|
457
|
+
|
458
|
+
def dfu_wait
|
459
|
+
usb_vid = '8087'
|
460
|
+
usb_pid = '0a99'
|
461
|
+
until dfu_device_counts(usb_vid, usb_pid) > 0
|
462
|
+
puts 'waiting dfu...'
|
463
|
+
sleep 1
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def dfu_device_counts(usb_vid, usb_pid)
|
468
|
+
`dfu-util -l -d #{usb_vid}:#{usb_pid}`.split("\n").select{|l0|
|
469
|
+
l0 =~ /Found/
|
470
|
+
}.select{|l1|
|
471
|
+
l1 =~ /#{usb_vid}/
|
472
|
+
}.count
|
473
|
+
end
|
474
|
+
|
475
|
+
def dfu_list(usb_vid, usb_pid)
|
476
|
+
`dfu-util -l -d #{usb_vid}:#{usb_pid}`.split("\n").select{|l0|
|
477
|
+
l0 =~ /Found/
|
478
|
+
}.map {|str|
|
479
|
+
ver = str.slice(/ver=.+?,/).chop.split('=')[1]
|
480
|
+
devnum = str.slice(/devnum=.+?,/).chop.split('=')[1]
|
481
|
+
cfg = str.slice(/cfg=.+?,/).chop.split('=')[1]
|
482
|
+
intf = str.slice(/intf=.+?,/).chop.split('=')[1]
|
483
|
+
alt = str.slice(/alt=.+?,/).chop.split('=')[1]
|
484
|
+
name = str.slice(/name=.+?,/).gsub('"', '').chop.split('=')[1]
|
485
|
+
serial = str.slice(/serial.+/).gsub('"', '').split('=')[1]
|
486
|
+
{
|
487
|
+
:ver => ver,
|
488
|
+
:devnum => devnum,
|
489
|
+
:cfg => cfg,
|
490
|
+
:intf => intf,
|
491
|
+
:alt => alt,
|
492
|
+
:name => name,
|
493
|
+
:serial => serial
|
494
|
+
}
|
495
|
+
}
|
496
|
+
end
|
497
|
+
|
498
|
+
def configure_wifi(ssid, wpa_key)
|
499
|
+
on host do
|
500
|
+
execute :wpa_passphrase, ssid, wpa_key, '>>', '/etc/wpa_supplicant/wpa_supplicant.conf'
|
501
|
+
execute :systemctl, *%w(restart wpa_supplicant.service)
|
502
|
+
execute :systemctl, *%w(enable wpa_supplicant.service)
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
def configure_access_key(access_key)
|
507
|
+
conf = get_conf
|
508
|
+
conf["access_key"] = access_key
|
509
|
+
set_conf(conf)
|
510
|
+
end
|
511
|
+
|
512
|
+
def get_conf
|
513
|
+
conf = nil
|
514
|
+
on host do
|
515
|
+
conf = YAML.load(capture(:cat, ISAAC_CONF_PATH))
|
516
|
+
end
|
517
|
+
return conf
|
518
|
+
end
|
519
|
+
|
520
|
+
def set_conf(conf)
|
521
|
+
y = YAML.dump(conf)
|
522
|
+
upload_io = StringIO.new(y)
|
523
|
+
on host do
|
524
|
+
upload! upload_io, ISAAC_CONF_PATH
|
525
|
+
end
|
526
|
+
return nil
|
527
|
+
end
|
528
|
+
|
529
|
+
def get_uname
|
530
|
+
uname = nil
|
531
|
+
on host do
|
532
|
+
uname = capture(:uname, '-a')
|
533
|
+
end
|
534
|
+
return uname
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
class CLI < Thor
|
539
|
+
register Local, 'local', 'local <COMMAND>', 'Run the app on local SBC'
|
540
|
+
register Device, 'device', 'device <COMMAND>', 'Configure SBC connected via USB'
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
Signal.trap(:INT) {
|
545
|
+
puts 'Quit'
|
546
|
+
exit 1
|
547
|
+
}
|
548
|
+
|
metadata
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: isaac_toolbelt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- xshell inc.
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sshkit
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: thor
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: curb
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: ruby-progressbar
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: archive
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: Isaac CLI tool
|
140
|
+
email:
|
141
|
+
- info@xshell.io
|
142
|
+
executables:
|
143
|
+
- isaac
|
144
|
+
extensions: []
|
145
|
+
extra_rdoc_files: []
|
146
|
+
files:
|
147
|
+
- ".gitignore"
|
148
|
+
- ".travis.yml"
|
149
|
+
- Gemfile
|
150
|
+
- README.md
|
151
|
+
- Rakefile
|
152
|
+
- bin/console
|
153
|
+
- bin/isaac
|
154
|
+
- bin/setup
|
155
|
+
- isaac_toolbelt.gemspec
|
156
|
+
- lib/isaac_toolbelt.rb
|
157
|
+
- lib/isaac_toolbelt/version.rb
|
158
|
+
homepage: https://isaacapp.io/
|
159
|
+
licenses: []
|
160
|
+
metadata: {}
|
161
|
+
post_install_message:
|
162
|
+
rdoc_options: []
|
163
|
+
require_paths:
|
164
|
+
- lib
|
165
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - ">="
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
requirements: []
|
176
|
+
rubyforge_project:
|
177
|
+
rubygems_version: 2.4.5.1
|
178
|
+
signing_key:
|
179
|
+
specification_version: 4
|
180
|
+
summary: Isaac CLI tool
|
181
|
+
test_files: []
|
182
|
+
has_rdoc:
|