enfcli 4.1.0.pre.beta → 5.0.1
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 +4 -4
- data/Gemfile.lock +4 -4
- data/lib/enfapi.rb +127 -169
- data/lib/enfapi/dns.rb +95 -0
- data/lib/enfapi/firewall.rb +37 -0
- data/lib/enfapi/user.rb +75 -0
- data/lib/enfcli.rb +51 -24
- data/lib/enfcli/commands/captive.rb +375 -14
- data/lib/enfcli/commands/user.rb +198 -153
- data/lib/enfcli/commands/xcr.rb +67 -48
- data/lib/enfcli/commands/xdns.rb +17 -10
- data/lib/enfcli/commands/xfw.rb +6 -3
- data/lib/enfcli/version.rb +2 -2
- metadata +7 -4
data/lib/enfapi/dns.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2020 Xaptum,Inc
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
module EnfApi
|
17
|
+
class Dns
|
18
|
+
include Singleton
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@version = "v1"
|
22
|
+
@xdns_base_url = "/api/xdns/#{@version}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def list_zones(domain)
|
26
|
+
EnfApi::API.instance.get "#{@xdns_base_url}/zones?enf_domain=#{domain}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_dns_zone(new_zone)
|
30
|
+
json = EnfApi::to_json(new_zone)
|
31
|
+
EnfApi::API.instance.post "#{@xdns_base_url}/zones", json
|
32
|
+
end
|
33
|
+
|
34
|
+
def update_dns_zone(zone_id, updated_zone)
|
35
|
+
json = EnfApi::to_json(updated_zone)
|
36
|
+
EnfApi::API.instance.put "#{@xdns_base_url}/zones/#{zone_id}", json
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete_dns_zone(zone_id)
|
40
|
+
EnfApi::API.instance.delete "#{@xdns_base_url}/zones/#{zone_id}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_networks_to_zone(zone_id, add_networks)
|
44
|
+
json = EnfApi::to_json(add_networks)
|
45
|
+
EnfApi::API.instance.post "#{@xdns_base_url}/zones/#{zone_id}/networks", json
|
46
|
+
end
|
47
|
+
|
48
|
+
def replace_networks_in_zone(zone_id, replace_networks)
|
49
|
+
json = EnfApi::to_json(replace_networks)
|
50
|
+
EnfApi::API.instance.put "#{@xdns_base_url}/zones/#{zone_id}/networks", json
|
51
|
+
end
|
52
|
+
|
53
|
+
def list_networks_in_zone(zone_id)
|
54
|
+
EnfApi::API.instance.get "#{@xdns_base_url}/zones/#{zone_id}/networks"
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete_networks_from_zone(zone_id, delete_networks)
|
58
|
+
EnfApi::API.instance.delete "#{@xdns_base_url}/zones/#{zone_id}/networks?delete=#{delete_networks}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def list_zones_in_network(network)
|
62
|
+
EnfApi::API.instance.get "#{@xdns_base_url}/networks/#{network}/zones"
|
63
|
+
end
|
64
|
+
|
65
|
+
def provision_server(network, new_server)
|
66
|
+
json = EnfApi::to_json(new_server)
|
67
|
+
EnfApi::API.instance.post "#{@xdns_base_url}/networks/#{network}/servers", json
|
68
|
+
end
|
69
|
+
|
70
|
+
def list_servers(network)
|
71
|
+
EnfApi::API.instance.get "#{@xdns_base_url}/networks/#{network}/servers"
|
72
|
+
end
|
73
|
+
|
74
|
+
def delete_server(network, server_ipv6)
|
75
|
+
EnfApi::API.instance.delete "#{@xdns_base_url}/networks/#{network}/servers/#{server_ipv6}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def create_dns_record(zone_id, new_record)
|
79
|
+
json = EnfApi::to_json(new_record)
|
80
|
+
EnfApi::API.instance.post "#{@xdns_base_url}/zones/#{zone_id}/records", json
|
81
|
+
end
|
82
|
+
|
83
|
+
def list_dns_records(zone_id)
|
84
|
+
EnfApi::API.instance.get "#{@xdns_base_url}/zones/#{zone_id}/records"
|
85
|
+
end
|
86
|
+
|
87
|
+
def query(network, type, name)
|
88
|
+
EnfApi::API.instance.get "#{@xdns_base_url}/networks/#{network}/query/#{type}/#{name}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def delete_dns_record(record_id)
|
92
|
+
EnfApi::API.instance.delete "#{@xdns_base_url}/records/#{record_id}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2020 Xaptum,Inc
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require "singleton"
|
18
|
+
|
19
|
+
module EnfApi
|
20
|
+
class Firewall
|
21
|
+
include Singleton
|
22
|
+
|
23
|
+
def list_firewall_rules(network)
|
24
|
+
EnfApi::API.instance.get "/api/xfw/v2/#{network}/rule"
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_firewall_rule(network, rule)
|
28
|
+
rule_json = EnfApi::to_json(rule)
|
29
|
+
EnfApi::API.instance.post "/api/xfw/v2/#{network}/rule", rule_json
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete_firewall_rules(network, id = nil)
|
33
|
+
# Same method to call to delete all firewall rules in a network. if id is nil
|
34
|
+
EnfApi::API.instance.delete "/api/xfw/v2/#{network}/rule/#{id}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/enfapi/user.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2020 Xaptum,Inc
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
module EnfApi
|
17
|
+
class UserManager
|
18
|
+
include Singleton
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@version = "v3"
|
22
|
+
@xcr_base_url = "/api/xcr/#{@version}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def list_users(query)
|
26
|
+
EnfApi::API.instance.get "#{@xcr_base_url}/users#{query}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_user(email)
|
30
|
+
EnfApi::API.instance.get "#{@xcr_base_url}/users/#{email}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def list_user_roles(user, network)
|
34
|
+
url = "#{@xcr_base_url}/users/#{user}/roles"
|
35
|
+
url += "?network=#{network}" if network
|
36
|
+
EnfApi::API.instance.get url
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete_user_roles(user_id, roles, network)
|
40
|
+
url = "#{@xcr_base_url}/users/#{user_id}/roles?roles=#{roles}"
|
41
|
+
url += "&network=#{network}" if network
|
42
|
+
EnfApi::API.instance.delete url
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_user_role(user_id, role_hash)
|
46
|
+
json = EnfApi::to_json(role_hash)
|
47
|
+
url = "#{@xcr_base_url}/users/#{user_id}/roles"
|
48
|
+
EnfApi::API.instance.post url, json
|
49
|
+
end
|
50
|
+
|
51
|
+
def list_invites(domain)
|
52
|
+
url = "#{@xcr_base_url}/invites"
|
53
|
+
url += "?domain=#{domain}" if domain
|
54
|
+
EnfApi::API.instance.get url
|
55
|
+
end
|
56
|
+
|
57
|
+
def invite(hash)
|
58
|
+
json = EnfApi::to_json(hash)
|
59
|
+
EnfApi::API.instance.post "#{@xcr_base_url}/invites", json
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete_invite(invite_id)
|
63
|
+
EnfApi::API.instance.delete "#{@xcr_base_url}/invites/#{invite_id}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def resend_invite(invite_id)
|
67
|
+
EnfApi::API.instance.put "#{@xcr_base_url}/invites/#{invite_id}", "{}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def update_user_status(user_id, status)
|
71
|
+
json = EnfApi::to_json(status)
|
72
|
+
EnfApi::API.instance.put "#{@xcr_base_url}/users/#{user_id}/status", json
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/enfcli.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright 2018 Xaptum,Inc
|
2
|
+
# Copyright 2018-2020 Xaptum,Inc
|
3
3
|
#
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -112,7 +112,7 @@ module EnfCli
|
|
112
112
|
|
113
113
|
def self.ask_password(prompt = nil)
|
114
114
|
begin
|
115
|
-
prompt
|
115
|
+
prompt ||= "Enter Password:"
|
116
116
|
print prompt
|
117
117
|
# We hide the entered characters before to ask for the password
|
118
118
|
system "stty -echo"
|
@@ -134,7 +134,7 @@ module EnfCli
|
|
134
134
|
|
135
135
|
# Generate cert
|
136
136
|
cert = OpenSSL::X509::Certificate.new
|
137
|
-
cert.subject = cert.issuer = OpenSSL::X509::Name.new([["CN",
|
137
|
+
cert.subject = cert.issuer = OpenSSL::X509::Name.new([["CN", ipv6.to_s]])
|
138
138
|
cert.not_before = Time.now
|
139
139
|
cert.not_after = Time.now + 365 * 24 * 60 * 60
|
140
140
|
cert.public_key = key
|
@@ -190,15 +190,41 @@ module EnfCli
|
|
190
190
|
end
|
191
191
|
|
192
192
|
def xaptum_admin?
|
193
|
-
|
193
|
+
has_role? "XAPTUM_ADMIN"
|
194
194
|
end
|
195
195
|
|
196
|
-
def
|
197
|
-
|
196
|
+
def domain_admin?
|
197
|
+
has_role? "DOMAIN_ADMIN"
|
198
|
+
end
|
199
|
+
|
200
|
+
def domain_user?
|
201
|
+
has_role? "DOMAIN_USER"
|
202
|
+
end
|
203
|
+
|
204
|
+
def network_admin?
|
205
|
+
has_role? "NETWORK_ADMIN"
|
206
|
+
end
|
207
|
+
|
208
|
+
def network_user?
|
209
|
+
has_role? "NETWORK_USER"
|
210
|
+
end
|
211
|
+
|
212
|
+
def edit_domain_role?
|
213
|
+
xaptum_admin? || domain_admin?
|
214
|
+
end
|
215
|
+
|
216
|
+
def has_role?(role)
|
217
|
+
all_roles = @session[:roles]
|
218
|
+
all_roles.each do |cur_role|
|
219
|
+
if cur_role[:role] == role
|
220
|
+
return true
|
221
|
+
end
|
222
|
+
end
|
223
|
+
false
|
198
224
|
end
|
199
225
|
|
200
226
|
def host
|
201
|
-
|
227
|
+
@host.to_s
|
202
228
|
end
|
203
229
|
|
204
230
|
def auth_token
|
@@ -229,8 +255,8 @@ module EnfCli
|
|
229
255
|
}
|
230
256
|
|
231
257
|
desc "connect", "Connect to ENF Controller"
|
232
|
-
method_option :host, :
|
233
|
-
method_option :user, :
|
258
|
+
method_option :host, type: :string
|
259
|
+
method_option :user, type: :string
|
234
260
|
|
235
261
|
def connect(*names)
|
236
262
|
host = ""
|
@@ -278,7 +304,7 @@ module EnfCli
|
|
278
304
|
puts EnfCli::VERSION
|
279
305
|
end
|
280
306
|
|
281
|
-
desc "update", "", :
|
307
|
+
desc "update", "", hide: true
|
282
308
|
|
283
309
|
def update
|
284
310
|
cmd = Gem::Commands::UpdateCommand.new
|
@@ -286,7 +312,7 @@ module EnfCli
|
|
286
312
|
execute_gem_cmd cmd
|
287
313
|
end
|
288
314
|
|
289
|
-
desc "search", "", :
|
315
|
+
desc "search", "", hide: true
|
290
316
|
|
291
317
|
def search
|
292
318
|
cmd = Gem::Commands::SearchCommand.new
|
@@ -295,14 +321,14 @@ module EnfCli
|
|
295
321
|
end
|
296
322
|
|
297
323
|
desc "create-config-file", "Create a Xaptum configuration file in your home directory"
|
298
|
-
method_option :host, :
|
299
|
-
method_option :user, :
|
324
|
+
method_option :host, type: :string, required: true
|
325
|
+
method_option :user, type: :string, required: true
|
300
326
|
|
301
327
|
def create_config_file
|
302
328
|
host = options[:host]
|
303
329
|
user = options[:user]
|
304
330
|
config_file = File.new(CONFIG_FILE, "w+")
|
305
|
-
config_file.puts({ :
|
331
|
+
config_file.puts({ host: host, user: user }.to_json)
|
306
332
|
config_file.close
|
307
333
|
say "Config file created successfully at #{CONFIG_FILE}!", :green
|
308
334
|
end
|
@@ -349,7 +375,7 @@ module EnfCli
|
|
349
375
|
trap("INT") { system("stty", stty_save); exit }
|
350
376
|
|
351
377
|
while input = Readline.readline(EnfCli::CTX.instance.prompt, true)
|
352
|
-
break if input == "exit" or input ==
|
378
|
+
break if input == "exit" or input == '\q' or input == "quit"
|
353
379
|
|
354
380
|
# Remove blank lines from history
|
355
381
|
Readline::HISTORY.pop if input == ""
|
@@ -362,12 +388,12 @@ module EnfCli
|
|
362
388
|
|
363
389
|
# Shell CLI class
|
364
390
|
class CLI < EnfCli::EnfThor
|
365
|
-
desc "ls [<dir>]", "List files in a directory", :
|
366
|
-
method_option :dir, :
|
391
|
+
desc "ls [<dir>]", "List files in a directory", hide: true
|
392
|
+
method_option :dir, type: :string, required: false
|
367
393
|
|
368
394
|
def ls(dir = nil)
|
369
395
|
try_with_rescue do
|
370
|
-
dir
|
396
|
+
dir ||= "."
|
371
397
|
dir = EnfCli::expand_path(dir)
|
372
398
|
|
373
399
|
Dir.entries(dir).each { |f|
|
@@ -376,7 +402,7 @@ module EnfCli
|
|
376
402
|
end
|
377
403
|
end
|
378
404
|
|
379
|
-
desc "cat <file>", "Display contents of a file", :
|
405
|
+
desc "cat <file>", "Display contents of a file", hide: true
|
380
406
|
|
381
407
|
def cat(file)
|
382
408
|
try_with_rescue do
|
@@ -390,7 +416,7 @@ module EnfCli
|
|
390
416
|
end
|
391
417
|
end
|
392
418
|
|
393
|
-
desc "pwd", "Current Working Directory", :
|
419
|
+
desc "pwd", "Current Working Directory", hide: true
|
394
420
|
|
395
421
|
def pwd
|
396
422
|
try_with_rescue do
|
@@ -398,17 +424,18 @@ module EnfCli
|
|
398
424
|
end
|
399
425
|
end
|
400
426
|
|
401
|
-
desc "cd [<dir>]", "Change working directory", :
|
427
|
+
desc "cd [<dir>]", "Change working directory", hide: true
|
402
428
|
|
403
429
|
def cd(dir = "~")
|
404
430
|
try_with_rescue do
|
405
431
|
dir = EnfCli::expand_path(dir)
|
406
432
|
raise EnfCli::ERROR, "No such directory #{dir}" unless Dir.exist?(dir)
|
433
|
+
|
407
434
|
Dir.chdir(dir)
|
408
435
|
end
|
409
436
|
end
|
410
437
|
|
411
|
-
desc "host", "Display ENF Controller host", :
|
438
|
+
desc "host", "Display ENF Controller host", hide: true
|
412
439
|
|
413
440
|
def host
|
414
441
|
try_with_rescue do
|
@@ -416,7 +443,7 @@ module EnfCli
|
|
416
443
|
end
|
417
444
|
end
|
418
445
|
|
419
|
-
desc "clear", "Clear Terminal Screen", :
|
446
|
+
desc "clear", "Clear Terminal Screen", hide: true
|
420
447
|
|
421
448
|
def clear
|
422
449
|
try_with_rescue do
|
@@ -429,7 +456,7 @@ module EnfCli
|
|
429
456
|
|
430
457
|
def display_session_token
|
431
458
|
try_with_rescue_in_session do
|
432
|
-
say
|
459
|
+
say EnfCli::CTX.instance.auth_token.to_s
|
433
460
|
end
|
434
461
|
end
|
435
462
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#
|
4
|
-
# Copyright 2018-
|
4
|
+
# Copyright 2018-2020 Xaptum,Inc
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -278,6 +278,10 @@ module EnfCli
|
|
278
278
|
desc: "secure-host or passthrough"
|
279
279
|
method_option :'wifi-id', type: :string, default: nil, banner: "WIFI-ID",
|
280
280
|
desc: "WIFI-ID is the UUID of the wifi record that the profile will use. wifi record must already exist"
|
281
|
+
method_option :'update-id', type: :string, default: nil,
|
282
|
+
banner: "UPDATE-ID",
|
283
|
+
desc: "UPDATE-ID is the UUID of the firmware update record. " \
|
284
|
+
"The firmware-update record must already exist."
|
281
285
|
|
282
286
|
def create_profile
|
283
287
|
try_with_rescue_in_session do
|
@@ -293,6 +297,12 @@ module EnfCli
|
|
293
297
|
new_profile_hash[:config][:wifi][:id] = wifi
|
294
298
|
end
|
295
299
|
|
300
|
+
fw_update = options[:'update-id']
|
301
|
+
if fw_update
|
302
|
+
new_profile_hash[:config][:firmware] = {}
|
303
|
+
new_profile_hash[:config][:firmware][:id] = fw_update if fw_update
|
304
|
+
end
|
305
|
+
|
296
306
|
# send the POST to create a new profile
|
297
307
|
profile = EnfApi::Captive.instance.create_profile new_profile_hash
|
298
308
|
display_profile profile
|
@@ -341,6 +351,10 @@ module EnfCli
|
|
341
351
|
desc: "secure-host or passthrough"
|
342
352
|
method_option :'wifi-id', type: :string, default: nil, banner: "WIFI-ID",
|
343
353
|
desc: "WIFI-ID is the UUID of the wifi record that the profile will use. wifi record must already exist"
|
354
|
+
method_option :'update-id', type: :string, default: nil,
|
355
|
+
banner: "UPDATE-ID",
|
356
|
+
desc: "UPDATE-ID is the UUID of the firmware update record. " \
|
357
|
+
"The firmware-update record must already exist."
|
344
358
|
|
345
359
|
def update_profile
|
346
360
|
try_with_rescue_in_session do
|
@@ -348,23 +362,250 @@ module EnfCli
|
|
348
362
|
name = options[:'profile-name']
|
349
363
|
mode = options[:'device-mode']
|
350
364
|
wifi_id = options[:'wifi-id']
|
365
|
+
fw_update = options[:'update-id']
|
351
366
|
|
352
|
-
raise "At least one option needs to change." if name == nil && mode == nil && wifi_id == nil
|
367
|
+
raise "At least one option needs to change." if name == nil && mode == nil && wifi_id == nil && fw_update == nil
|
353
368
|
|
354
369
|
update_hash = {}
|
355
370
|
update_hash[:name] = name if name
|
356
|
-
update_hash[:config] = {} if wifi_id || mode
|
371
|
+
update_hash[:config] = {} if wifi_id || mode || fw_update
|
357
372
|
update_hash[:config][:mode] = mode if mode
|
358
373
|
if wifi_id
|
359
374
|
update_hash[:config][:wifi] = {}
|
360
375
|
update_hash[:config][:wifi][:id] = wifi_id
|
361
376
|
end
|
362
377
|
|
378
|
+
if fw_update
|
379
|
+
update_hash[:config][:firmware] = {}
|
380
|
+
update_hash[:config][:firmware][:id] = fw_update
|
381
|
+
end
|
363
382
|
profile = EnfApi::Captive.instance.update_profile id, update_hash
|
364
383
|
display_profile profile
|
365
384
|
end
|
366
385
|
end
|
367
386
|
|
387
|
+
desc "list-firmware-images",
|
388
|
+
"Lists all of the available versions of the firmware"
|
389
|
+
|
390
|
+
def list_firmware_images
|
391
|
+
try_with_rescue_in_session do
|
392
|
+
data = EnfApi::Captive.instance.list_firmware_images
|
393
|
+
display_firmware_image_list data
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
desc "upload-firmware-image",
|
398
|
+
"Uploads a new firmware image. Each version will have multiple " \
|
399
|
+
"images -- one for each type of hardware. \nNOTE: This version " \
|
400
|
+
"doesn't actually upload the file, it merely informs the server of " \
|
401
|
+
"its existance."
|
402
|
+
method_option :'image-file', required: true, type: :string,
|
403
|
+
banner: "<file>", desc: "<file> is the firmware binary image."
|
404
|
+
method_option :version, required: true, type: :string,
|
405
|
+
banner: "VERSION",
|
406
|
+
desc: "VERSION is the release version."
|
407
|
+
method_option :'image-name', type: :string, default: nil, banner: "NAME",
|
408
|
+
desc: "NAME is the name to associate with the firmware " \
|
409
|
+
"image. This is usually the generated filename. " \
|
410
|
+
"Defaults to the base name of <file>"
|
411
|
+
|
412
|
+
def upload_firmware_image
|
413
|
+
try_with_rescue_in_session do
|
414
|
+
filename = options[:'image-file']
|
415
|
+
version = options[:version]
|
416
|
+
image_name = options[:'image-name']
|
417
|
+
|
418
|
+
temp_img_name = File.basename(filename)
|
419
|
+
image_name ||= temp_img_name
|
420
|
+
|
421
|
+
raise "image-name does not match image-file" if temp_img_name != image_name
|
422
|
+
|
423
|
+
# This version PUTs an empty body
|
424
|
+
resp = EnfApi::Captive.instance.upload_firmware_image version, image_name
|
425
|
+
|
426
|
+
if (resp.code == 200)
|
427
|
+
say "Upload complete."
|
428
|
+
else
|
429
|
+
say "Upload failed with code #{resp.code}"
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
desc "get-firmware-info",
|
435
|
+
"Prints details of an existing firmware version."
|
436
|
+
method_option :version, required: true, type: :string,
|
437
|
+
banner: "VERSION", desc: "VERSION is the release version."
|
438
|
+
|
439
|
+
def get_firmware_info
|
440
|
+
try_with_rescue_in_session do
|
441
|
+
version = options[:version]
|
442
|
+
|
443
|
+
fw_info = EnfApi::Captive.instance.get_firmware_info version
|
444
|
+
|
445
|
+
display_firmware_detail fw_info
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
desc "create-schedule",
|
450
|
+
"Creates a schedule object that will be used to determine when a " \
|
451
|
+
"device or group of devices may be upgraded. The schedule will be " \
|
452
|
+
"uploaded from a JSON-formatted file."
|
453
|
+
method_option :schedule, required: true, type: :string,
|
454
|
+
banner: "<file>",
|
455
|
+
desc: "<file> is a JSON file containing the desired schedule."
|
456
|
+
|
457
|
+
def create_schedule
|
458
|
+
try_with_rescue_in_session do
|
459
|
+
filename = options[:schedule]
|
460
|
+
|
461
|
+
# reading the whole file - shouldn't get more than a few KB
|
462
|
+
content = File.read filename
|
463
|
+
sched_hash = JSON.parse(content)
|
464
|
+
|
465
|
+
resp_data = EnfApi::Captive.instance.create_schedule sched_hash
|
466
|
+
display_schedule_detail resp_data
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
desc "list-schedules",
|
471
|
+
"Prints a summary list of all existing schedules."
|
472
|
+
|
473
|
+
def list_schedules
|
474
|
+
try_with_rescue_in_session do
|
475
|
+
sched_list = EnfApi::Captive.instance.list_schedules
|
476
|
+
display_schedule_list sched_list
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
desc "get-schedule",
|
481
|
+
"Prints the detail of a specific, existing schedule."
|
482
|
+
method_option :'schedule-id', type: :string, required: true, banner: "ID",
|
483
|
+
desc: "ID is the system-assigned UUID of the schedule " \
|
484
|
+
"to retrieve."
|
485
|
+
|
486
|
+
def get_schedule
|
487
|
+
try_with_rescue_in_session do
|
488
|
+
sched_id = options[:'schedule-id']
|
489
|
+
sched_data = EnfApi::Captive.instance.get_schedule sched_id
|
490
|
+
display_schedule_detail sched_data
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
desc "update-schedule",
|
495
|
+
"Modifies an existing schedule by uploading an updated schedule in " \
|
496
|
+
"the form of a JSON file."
|
497
|
+
method_option :'schedule-id', type: :string, required: true, banner: "ID",
|
498
|
+
desc: "ID is the UUID of the schedule to update."
|
499
|
+
method_option :schedule, type: :string, required: true, bannder: "<file>",
|
500
|
+
desc: "<file> is a JSON file containing the desired schedule."
|
501
|
+
|
502
|
+
def update_schedule
|
503
|
+
try_with_rescue_in_session do
|
504
|
+
sched_id = options[:'schedule-id']
|
505
|
+
filename = options[:schedule]
|
506
|
+
|
507
|
+
# read in whole schedule file
|
508
|
+
content = File.read filename
|
509
|
+
sched_hash = JSON.parse(content)
|
510
|
+
|
511
|
+
resp_data = EnfApi::Captive.instance.update_schedule sched_id, sched_hash
|
512
|
+
display_schedule_detail resp_data
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
desc "delete-schedule",
|
517
|
+
"Deletes the specified, existing schedule."
|
518
|
+
method_option :'schedule-id', type: :string, required: true, banner: "ID",
|
519
|
+
desc: "ID is the system-assigned UUID of the schedule " \
|
520
|
+
"to delete."
|
521
|
+
|
522
|
+
def delete_schedule
|
523
|
+
try_with_rescue_in_session do
|
524
|
+
sched_id = options[:'schedule-id']
|
525
|
+
|
526
|
+
# get the name of the schedule
|
527
|
+
resp = EnfApi::Captive.instance.get_schedule(sched_id)
|
528
|
+
name = resp[:name]
|
529
|
+
|
530
|
+
resp = EnfApi::Captive.instance.delete_schedule sched_id
|
531
|
+
|
532
|
+
if (resp.code == 200)
|
533
|
+
say "Successfully deleted schedule named: #{name}"
|
534
|
+
elsif (resp.code == 409)
|
535
|
+
say "Schedule #{name} is being used by pending updates and cannot be deleted."
|
536
|
+
else
|
537
|
+
say "Failed to delete schedule #{name} with code #{resp.code}"
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
desc "list-firmware-updates",
|
543
|
+
"Lists the existing firmware update tasks."
|
544
|
+
|
545
|
+
def list_firmware_updates
|
546
|
+
try_with_rescue_in_session do
|
547
|
+
updates_list = EnfApi::Captive.instance.list_firmware_updates
|
548
|
+
display_updates_list updates_list
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
desc "get-firmware-update",
|
553
|
+
"Prints the details of the specified firmware update task."
|
554
|
+
method_option :'update-id', type: :string, required: true, banner: "ID",
|
555
|
+
desc: "ID is the UUID of the firmware update task"
|
556
|
+
|
557
|
+
def get_firmware_update
|
558
|
+
try_with_rescue_in_session do
|
559
|
+
update_id = options[:'update-id']
|
560
|
+
|
561
|
+
update_data = EnfApi::Captive.instance.get_firmware_update update_id
|
562
|
+
display_update_detail update_data
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
desc "create-firmware-update",
|
567
|
+
"Creates a firmware-update task that the system will use to update " \
|
568
|
+
"router-card firmware as prescribed in the specified JSON file."
|
569
|
+
method_option :update, type: :string, required: true, banner: "<file>",
|
570
|
+
desc: "<file> is a JSON file containing the details of " \
|
571
|
+
"the update task."
|
572
|
+
|
573
|
+
def create_firmware_update
|
574
|
+
try_with_rescue_in_session do
|
575
|
+
filename = options[:update]
|
576
|
+
|
577
|
+
# reading the whole file - shouldn't get more than a few KB
|
578
|
+
content = File.read filename
|
579
|
+
update_hash = JSON.parse(content)
|
580
|
+
|
581
|
+
resp_data = EnfApi::Captive.instance.create_firmware_update update_hash
|
582
|
+
display_update_detail resp_data
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
desc "modify-firmware-update",
|
587
|
+
"Modifies an existing firmware-update task with the information " \
|
588
|
+
"contained in the specified JSON file"
|
589
|
+
method_option :'update-id', type: :string, required: true, banner: "ID",
|
590
|
+
desc: "ID is the UUID of the firmware update task to be modified."
|
591
|
+
method_option :update, type: :string, required: true, banner: "<file>",
|
592
|
+
desc: "<file> is a JSON file containing the details of " \
|
593
|
+
"the update task."
|
594
|
+
|
595
|
+
def modify_firmware_update
|
596
|
+
try_with_rescue_in_session do
|
597
|
+
update_id = options[:'update-id']
|
598
|
+
filename = options[:update]
|
599
|
+
|
600
|
+
# reading the whole file - shouldn't get more than a few KB
|
601
|
+
content = File.read filename
|
602
|
+
update_hash = JSON.parse(content)
|
603
|
+
|
604
|
+
resp_data = EnfApi::Captive.instance.modify_firmware_update update_id, update_hash
|
605
|
+
display_update_detail resp_data
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
368
609
|
#########################################################################
|
369
610
|
#
|
370
611
|
# Helper functions
|
@@ -383,20 +624,21 @@ module EnfCli
|
|
383
624
|
end
|
384
625
|
|
385
626
|
# Display a single wifi configuration in detail
|
386
|
-
def display_wifi_detail(wifi_data, full_listing = true)
|
627
|
+
def display_wifi_detail(wifi_data, full_listing = true, tabs = 0)
|
628
|
+
indent = " " * tabs
|
387
629
|
name = wifi_data[:name]
|
388
630
|
wifi_id = wifi_data[:id]
|
389
631
|
desc = wifi_data[:description]
|
390
632
|
nets = wifi_data[:networks]
|
391
633
|
|
392
|
-
say "
|
393
|
-
say "
|
394
|
-
say "Description : #{desc}", nil, true if desc
|
634
|
+
say indent + "Name : #{name}", nil, true
|
635
|
+
say indent + "Wifi ID : #{wifi_id}", nil, true
|
636
|
+
say indent + "Description : #{desc}", nil, true if desc
|
395
637
|
if full_listing
|
396
638
|
say "WiFi Networks :"
|
397
639
|
if nets
|
398
640
|
nets.each do |wifi_net|
|
399
|
-
display_wifi_net(wifi_net, 1)
|
641
|
+
display_wifi_net(wifi_net, tabs + 1)
|
400
642
|
end
|
401
643
|
end
|
402
644
|
end
|
@@ -567,7 +809,7 @@ module EnfCli
|
|
567
809
|
say "Router Mode : #{mode}", nil, true
|
568
810
|
say "Uptime (in seconds) : #{uptime}", nil, true
|
569
811
|
say "Status refresh time : #{refresh}", nil, true
|
570
|
-
say "WIFI
|
812
|
+
say "WIFI status :"
|
571
813
|
say " connected : #{connected}", nil, true
|
572
814
|
say " SSID : #{ssid}" if ssid
|
573
815
|
if ipv4 && !ipv4.empty?
|
@@ -584,7 +826,17 @@ module EnfCli
|
|
584
826
|
end
|
585
827
|
end
|
586
828
|
|
587
|
-
|
829
|
+
say "WIFI configuration :"
|
830
|
+
display_wifi_detail(wifi_config, false, 1) if wifi_config
|
831
|
+
|
832
|
+
fw_status = device_status[:firmware]
|
833
|
+
if fw_status
|
834
|
+
image_name = fw_status[:image_name] || "< not available >"
|
835
|
+
image_state = fw_status[:state] || " < not available >"
|
836
|
+
say "Firmware Status :"
|
837
|
+
say " image name : #{image_name}", nil, true
|
838
|
+
say " operating state : #{image_state}", nil, true
|
839
|
+
end
|
588
840
|
|
589
841
|
say " ", nil, true
|
590
842
|
end
|
@@ -613,7 +865,6 @@ module EnfCli
|
|
613
865
|
# [hash[:serial_number], hash[:device_name], hash[:device_address],
|
614
866
|
# hash[:status][:router_mode], hash[:status][:wifi][:connected],
|
615
867
|
# hash[:status][:wifi][:SSID]]
|
616
|
-
|
617
868
|
render_table(headings, rows)
|
618
869
|
end
|
619
870
|
|
@@ -623,11 +874,20 @@ module EnfCli
|
|
623
874
|
def display_profile(profile, summary = false)
|
624
875
|
indent = summary ? " " : ""
|
625
876
|
|
877
|
+
firmware = profile[:config][:firmware]
|
878
|
+
fw_id = "< not configured >"
|
879
|
+
if firmware
|
880
|
+
fw_id = firmware[:id]
|
881
|
+
fw_version = firmware[:version]
|
882
|
+
end
|
883
|
+
|
626
884
|
say indent + "Name : #{profile[:name]}", nil, true
|
627
885
|
say indent + "Profile ID : #{profile[:id]}", nil, true
|
628
886
|
say indent + "Configuration version : #{profile[:config][:version]}", nil, true
|
887
|
+
say indent + "Firmware Update ID : #{fw_id}", nil, true
|
629
888
|
unless summary
|
630
|
-
say "
|
889
|
+
say indent + " Update version : #{fw_version}", nil, true if fw_version
|
890
|
+
say indent + "Mode : #{profile[:config][:mode]}", nil, true
|
631
891
|
display_wifi_summary profile[:config][:wifi]
|
632
892
|
end
|
633
893
|
end
|
@@ -654,13 +914,114 @@ module EnfCli
|
|
654
914
|
# display the profile summary list
|
655
915
|
#
|
656
916
|
def display_profile_list(profiles)
|
657
|
-
headings = ["Profile Name", "Profile ID", "Version"]
|
917
|
+
headings = ["Profile Name", "Profile ID", "Version", "Mode", "Wifi ID", "Firmware Update ID"]
|
658
918
|
rows = profiles.map do |hash|
|
659
|
-
|
919
|
+
config = hash[:config]
|
920
|
+
fw_id = config[:firmware] ? config[:firmware][:id] : "none"
|
921
|
+
[hash[:name], hash[:id], config[:version], config[:mode],
|
922
|
+
config[:wifi][:id], fw_id]
|
660
923
|
end
|
924
|
+
render_table(headings, rows)
|
925
|
+
end
|
661
926
|
|
927
|
+
#
|
928
|
+
# Displays the list of firmware releases
|
929
|
+
#
|
930
|
+
def display_firmware_image_list(images)
|
931
|
+
headings = ["Firmware Version"]
|
932
|
+
rows = images.map do |element|
|
933
|
+
[element[:version]]
|
934
|
+
end
|
662
935
|
render_table(headings, rows)
|
663
936
|
end
|
937
|
+
|
938
|
+
#
|
939
|
+
# Displays firmware detail
|
940
|
+
#
|
941
|
+
def display_firmware_detail(fw_info)
|
942
|
+
version = fw_info[:version] || "< not available >"
|
943
|
+
images = fw_info[:images]
|
944
|
+
|
945
|
+
say "Release version: #{version}"
|
946
|
+
display_image_list images
|
947
|
+
end
|
948
|
+
|
949
|
+
#
|
950
|
+
# Displays the list of images available for a release version.
|
951
|
+
#
|
952
|
+
def display_image_list(images)
|
953
|
+
headings = ["Image Name", "Hardware Model", "Update Type", "SHA256"]
|
954
|
+
rows = images.map do |hash|
|
955
|
+
[hash[:name], hash[:model], hash[:type], hash[:sha256]]
|
956
|
+
end
|
957
|
+
render_table(headings, rows)
|
958
|
+
end
|
959
|
+
|
960
|
+
#
|
961
|
+
# Displays the full SCHEDULE object detail
|
962
|
+
#
|
963
|
+
def display_schedule_detail(sched_data)
|
964
|
+
name = sched_data[:name]
|
965
|
+
id = sched_data[:id]
|
966
|
+
domain = sched_data[:domain]
|
967
|
+
times = sched_data[:times]
|
968
|
+
|
969
|
+
say "Schedule Name : #{name}", nil, true
|
970
|
+
say "Shedule ID : #{id}", nil, true
|
971
|
+
say "domain : #{domain}", nil, true
|
972
|
+
say "Times:", nil, true
|
973
|
+
display_time_list(times)
|
974
|
+
end
|
975
|
+
|
976
|
+
#
|
977
|
+
# Displays the list of times in a schedule as a table
|
978
|
+
#
|
979
|
+
def display_time_list(times)
|
980
|
+
headings = ["Name", "year", "Month", "Date", "Weekday", "Hour",
|
981
|
+
"Minute", "id"]
|
982
|
+
rows = times.map do |hash|
|
983
|
+
[hash[:name], hash[:year], hash[:month], hash[:day_of_month],
|
984
|
+
hash[:day_of_week], hash[:hour], hash[:minute], hash[:id]]
|
985
|
+
end
|
986
|
+
render_table(headings, rows)
|
987
|
+
end
|
988
|
+
|
989
|
+
#
|
990
|
+
# Displays a list of schedules
|
991
|
+
#
|
992
|
+
def display_schedule_list(sched_list)
|
993
|
+
headings = ["Name", "ID"]
|
994
|
+
rows = sched_list.map do |hash|
|
995
|
+
[hash[:name], hash[:id]]
|
996
|
+
end
|
997
|
+
render_table(headings, rows)
|
998
|
+
end
|
999
|
+
|
1000
|
+
#
|
1001
|
+
# Displays a list of firmware updates
|
1002
|
+
#
|
1003
|
+
def display_updates_list(updates_list)
|
1004
|
+
headings = ["Update ID", "Update Version"]
|
1005
|
+
rows = updates_list.map do |hash|
|
1006
|
+
[hash[:id], hash[:version]]
|
1007
|
+
end
|
1008
|
+
render_table(headings, rows)
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
#
|
1012
|
+
# Displays detail of a specific firmware update task
|
1013
|
+
#
|
1014
|
+
def display_update_detail(update_data)
|
1015
|
+
id = update_data[:id]
|
1016
|
+
sched_id = update_data[:schedule_id]
|
1017
|
+
version = update_data[:version]
|
1018
|
+
percent = update_data[:update_percentage]
|
1019
|
+
|
1020
|
+
say "Update Task ID : #{id}"
|
1021
|
+
say "Schedule ID : #{sched_id}"
|
1022
|
+
say "Firmware Version : #{version}"
|
1023
|
+
say "Percent of devices to update : #{percent}"
|
1024
|
+
end
|
664
1025
|
end
|
665
1026
|
end
|
666
1027
|
end
|