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 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
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /vendor/bundle
11
+ /.bundle
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in isaac_toolbelt.gemspec
4
+ gemspec
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
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
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
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "isaac_toolbelt"
4
+
5
+ IsaacToolbelt::CLI.start
6
+
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -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
+
@@ -0,0 +1,3 @@
1
+ module IsaacToolbelt
2
+ VERSION = "0.1.0"
3
+ end
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: