enfcli 3.3.4 → 3.4.0.pre.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,485 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2018-2019 Xaptum,Inc
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ require 'enfthor'
19
+ require 'enfapi'
20
+ require 'json'
21
+
22
+ module EnfCli
23
+ module Cmd
24
+ class Captive < EnfThor
25
+ desc 'list-wifi-configurations',
26
+ 'List wifi configuration information for all or the matching records.'
27
+ method_option :name, default: nil, type: :string, banner: 'NAME',
28
+ desc: 'where NAME will match the user-given name.'
29
+ method_option :domain, default: nil, type: :string, banner: 'DOMAIN',
30
+ aliases: '-d'
31
+ method_option :ssid, default: nil, type: :string, banner: 'SSID'
32
+ def list_wifi_configurations
33
+ try_with_rescue_in_session do
34
+ ## TODO: V1 is only listing all of the wifi configurations, it is not
35
+ ## using the options - add the queries based on the options given.
36
+ wifi_configs = EnfApi::Captive.instance.list_wifi_configurations
37
+
38
+ if wifi_configs.class == Array
39
+ # display the data
40
+ display_wifi_configs wifi_configs
41
+ else
42
+ say 'No WiFi configurations found.'
43
+ end
44
+ end
45
+ end
46
+
47
+ desc 'create-wifi-configuration', 'Create a new wifi configuration.'
48
+ method_option :'wifi-config-file', type: :array, required: true,
49
+ banner: '<file>',
50
+ desc: '<file> is JSON file with parameters required for configuring the router card.'
51
+ def create_wifi_configuration
52
+ try_with_rescue_in_session do
53
+ json_file_name = options[:'wifi-config-file'].join(' ')
54
+
55
+ # read in the entire file - yes it is slurping the file, but it should
56
+ # never be bigger than a few KB.
57
+ content = File.read json_file_name
58
+ json_hash = JSON.parse(content)
59
+
60
+ resp_data = EnfApi::Captive.instance.create_wifi_configuration json_hash
61
+
62
+ # display the data
63
+ display_wifi_detail resp_data
64
+ end
65
+ end
66
+
67
+ desc 'get-wifi-configuration',
68
+ 'Get details of the specified wifi configuration. Exactly one of: ' \
69
+ '--wifi-id, --profile-id, --device-id, or --wifi-name must be ' \
70
+ 'specified.'
71
+ method_option :'wifi-id', defaut: nil, type: :string, banner: 'WIFI-ID',
72
+ desc: 'WIFI-ID is the UUID of the wifi record'
73
+ method_option :version, defaut: nil, type: :string, banner: 'VERSION',
74
+ desc: 'Optionally used with --wifi-id'
75
+ method_option :'profile-id', defaut: nil, type: :string, banner: 'PROFILE-ID',
76
+ desc: 'PROFILE-ID is the UUID of the profile.'
77
+ method_option :'device-id', defaut: nil, type: :string, banner: 'DEVICE-ID',
78
+ desc: 'DEVICE-ID is the UUID of the device.'
79
+ method_option :'wifi-name', defaut: nil, type: :string, banner: 'WIFI-NAME',
80
+ desc: 'Result matches any wifi configuration that contains the substring WIFI-NAME'
81
+ def get_wifi_configuration
82
+ try_with_rescue_in_session do
83
+ ## TODO - Currently, this only handles wifi-id, add the others to v2
84
+
85
+ wifi_id = options[:'wifi-id']
86
+ profile_id = options[:'profile-id']
87
+ device_id = options[:'device-id']
88
+ wifi_name = options[:'wifi-name']
89
+ version = options[:version]
90
+
91
+ num_query_opts = 0
92
+ num_query_opts += 1 if wifi_id
93
+ num_query_opts += 1 if profile_id
94
+ num_query_opts += 1 if device_id
95
+ num_query_opts += 1 if wifi_name
96
+
97
+
98
+ raise 'ERROR: Exactly one of: --wifi-id, --profile-id, --device-id, or --wifi-name must be specified.' if num_query_opts != 1
99
+
100
+ if wifi_id
101
+ wifi_config = EnfApi::Captive.instance.get_wifi_configuration wifi_id, version
102
+
103
+ # display the data
104
+ display_wifi_detail wifi_config
105
+
106
+ end
107
+ end
108
+ end
109
+
110
+ desc 'update-wifi-configuration',
111
+ 'Update an existing wifi configuration.'
112
+ method_option :'wifi-id', defaut: nil, type: :string, banner: 'WIFI-ID',
113
+ desc: 'WIFI-ID is the UUID of the wifi profile'
114
+ method_option :'wifi-config-file', type: :array, required: true,
115
+ banner: '<file>',
116
+ desc: '<file> is JSON file with ' \
117
+ 'parameters required for ' \
118
+ 'configuring the router card.'
119
+ def update_wifi_configuration
120
+ try_with_rescue_in_session do
121
+ json_file_name = options[:'wifi-config-file'].join(' ')
122
+ # read in the entire file - yes it is slurping the file, but it should
123
+ # never be bigger than a few KB.
124
+ content = File.read json_file_name
125
+ json_hash = JSON.parse(content)
126
+
127
+ resp_data = EnfApi::Captive.instance.update_wifi_configuration options[:'wifi-id'], json_hash
128
+ display_wifi_detail resp_data
129
+ end
130
+ end
131
+
132
+
133
+ desc 'create-device',
134
+ 'Add a new device to the database. This is only available to Xaptum administrators'
135
+ method_option :'device-id', type: :string, required: true, banner: 'SERIAL-NUM',
136
+ desc: 'SERIAL-NUM is the serial number of the device.'
137
+ method_option :'device-name', type: :string, default: nil, banner: 'DEVICE-NAME',
138
+ desc: 'User-defined name for the device.'
139
+ method_option :'mac-addr1', type: :string, required: true, banner: 'MAC-ADDR1',
140
+ desc: 'MAC address 1 (wifi address on a wifi-enabled device)'
141
+ method_option :'mac-addr2', type: :string, required: true, banner: 'MAC-ADDR2',
142
+ desc: 'MAC address 2'
143
+ method_option :'mac-addr3', type: :string, required: true, banner: 'MAC-ADDR3',
144
+ desc: 'MAC address 3'
145
+ method_option :'mac-addr4', type: :string, required: true, banner: 'MAC-ADDR4',
146
+ desc: 'MAC address 4'
147
+ method_option :'profile-id', type: :string, default: nil, banner: 'PROFILE_ID',
148
+ desc: 'UUID of the profile that the device will use. The profile must already exist.'
149
+ def create_device
150
+ try_with_rescue_in_session do
151
+ new_device_hash = {
152
+ serial_number: options[:'device-id'],
153
+ mac_address: {
154
+ :wifi => options[:'mac-addr1'],
155
+ 2 => options[:'mac-addr2'],
156
+ 3 => options[:'mac-addr3'],
157
+ 4 => options[:'mac-addr4']
158
+ }
159
+ }
160
+ dev_name = options[:'device-name']
161
+ profile = options[:profile_id]
162
+ new_device_hash[:device_name] = dev_name if dev_name
163
+ new_device_hash[:profile] = profile if profile
164
+
165
+ # send the new device request
166
+ device_data = EnfApi::Captive.instance.create_device new_device_hash
167
+
168
+ # display the response
169
+ display_device device_data
170
+ end
171
+ end
172
+
173
+ desc 'list-devices',
174
+ 'List basic device information for all devices matching the option specified.'
175
+ method_option :network, default: nil, type: :string, banner: 'NETWORK',
176
+ desc: 'NETWORK is the ipv6 subnet used by the device.'
177
+ def list_devices
178
+ try_with_rescue_in_session do
179
+ network = options[:network]
180
+ data = EnfApi::Captive.instance.list_devices network
181
+
182
+ display_device_list data
183
+ end
184
+ end
185
+
186
+ desc 'get-device',
187
+ 'Get details of the specified device.'
188
+ method_option :'device-id', required: true, type: :string,
189
+ banner: 'DEVICE-ID',
190
+ desc: 'DEVICE-ID is either the device serial number or its ipv6 address.'
191
+ def get_device
192
+ try_with_rescue_in_session do
193
+ device_id = options[:'device-id']
194
+ data = EnfApi::Captive.instance.get_device device_id
195
+
196
+ display_device data
197
+ end
198
+ end
199
+
200
+ desc 'create-profile',
201
+ 'Create a new profile.'
202
+ method_option :'profile-name', type: :string, required: true,
203
+ banner: 'PROFILE-NAME',
204
+ desc: 'PROFILE-NAME is the user-given name.'
205
+ method_option :'device-mode', type: :string, required: true,
206
+ banner: 'DEVICE-MODE',
207
+ desc: 'secure-host or passthrough'
208
+ method_option :'wifi-id', type: :string, default: nil, banner: 'WIFI-ID',
209
+ desc: 'WIFI-ID is the UUID of the wifi record that the profile will use. wifi record must already exist'
210
+ def create_profile
211
+ try_with_rescue_in_session do
212
+ new_profile_hash = {
213
+ name: options[:'profile-name'],
214
+ config: {mode: options[:'device-mode']}
215
+ }
216
+
217
+ wifi = options[:'wifi-id']
218
+ if wifi
219
+ new_profile_hash[:config][:wifi] = {}
220
+ new_profile_hash[:config][:wifi][:id] = wifi
221
+ end
222
+
223
+ # send the POST to create a new profile
224
+ profile = EnfApi::Captive.instance.create_profile new_profile_hash
225
+ display_profile profile
226
+ end
227
+ end
228
+
229
+ desc 'list-profiles',
230
+ 'List the existing profiles.'
231
+ method_option :name, default: nil, type: :string, banner: 'NAME',
232
+ desc: 'where NAME will match all or part of the user-given profile name.'
233
+ def list_profiles
234
+ try_with_rescue_in_session do
235
+ query_name = options[:name]
236
+ profiles = EnfApi::Captive.instance.list_profiles query_name
237
+ display_profile_list profiles
238
+ end
239
+ end
240
+
241
+ desc 'get-profile',
242
+ 'Get full detail listing of the profile.'
243
+ method_option :'profile-id', required: true, type: :string,
244
+ banner: 'PROFILE-ID',
245
+ desc: 'PROFILE-ID is the UUID of the profile.'
246
+ # TODO - server doesn't support version yet.
247
+ # method_option :version, default: nil, type: :integer, banner: 'VERSION',
248
+ # desc: 'Get a specific version.'
249
+ def get_profile
250
+ try_with_rescue_in_session do
251
+ profile = EnfApi::Captive.instance.get_profile options[:'profile-id']
252
+ display_profile profile
253
+ end
254
+ end
255
+
256
+ desc 'update-profile',
257
+ 'Update a previously-created profile specified by PROFILE-ID. At ' \
258
+ 'least one property must be changed.'
259
+ method_option :'profile-id', required: true, type: :string,
260
+ banner: 'PROFILE-ID',
261
+ desc: 'UUID of the device profile.'
262
+ method_option :'profile-name', type: :string, default: nil,
263
+ banner: 'PROFILE-NAME',
264
+ desc: 'PROFILE-NAME is the user-given name.'
265
+ method_option :'device-mode', type: :string, default: nil,
266
+ banner: 'DEVICE-MODE',
267
+ desc: 'secure-host or passthrough'
268
+ method_option :'wifi-id', type: :string, default: nil, banner: 'WIFI-ID',
269
+ desc: 'WIFI-ID is the UUID of the wifi record that the profile will use. wifi record must already exist'
270
+ def update_profile
271
+ try_with_rescue_in_session do
272
+ id = options[:'profile-id']
273
+ name = options[:'profile-name']
274
+ mode = options[:'device-mode']
275
+ wifi_id = options[:'wifi-id']
276
+
277
+ raise "At least one option needs to change." if name == nil && mode == nil && wifi_id == nil
278
+
279
+ update_hash = {}
280
+ update_hash[:name] = name if name
281
+ if wifi_id || mode
282
+ update_hash[:config] = {}
283
+ update_hash[:config][:mode] = mode if mode
284
+ update_hash[:config][:wifi] = wifi_id if wifi_id
285
+ end
286
+
287
+ profile = EnfApi::Captive.instance.update_profile id, update_hash
288
+ display_profile profile
289
+
290
+ end
291
+ end
292
+
293
+
294
+ #########################################################################
295
+ #
296
+ # Helper functions
297
+ #
298
+ #########################################################################
299
+ no_commands do
300
+ # Displays the wifi configuration summary list.
301
+ # TODO - Does non-Xaptum-admin columns only - add option to do admin list
302
+ def display_wifi_configs(configs)
303
+ headings = ['ID', 'Wifi Name', 'Config Vers.']
304
+ rows = configs.map do |hash|
305
+ [hash[:id], hash[:name], hash[:version]]
306
+ end
307
+
308
+ render_table(headings, rows)
309
+ end
310
+
311
+ # Display a single wifi configuration in detail
312
+ def display_wifi_detail(wifi_data)
313
+ name = wifi_data[:name]
314
+ wifi_id = wifi_data[:id]
315
+ desc = wifi_data[:description]
316
+ configs = wifi_data[:config]
317
+
318
+ say "Wifi ID : #{wifi_id}"
319
+ say "Name : #{name}"
320
+ say "Description : #{desc}"
321
+ say "WiFi Networks :"
322
+ configs.each do |wifi_net|
323
+ display_wifi_net(wifi_net, 1)
324
+ end
325
+
326
+ end
327
+
328
+ def display_wifi_net(wifi_net, tabs)
329
+ indent = " " * tabs
330
+ say "Name : #{wifi_net[:name]}"
331
+ say indent + "SSID :#{wifi_net[:SSID]}"
332
+ say indent + "SSID type :#{wifi_net[:SSID_type]}"
333
+
334
+ auth = wifi_net[:auth]
335
+ if auth
336
+ say indent + "auth :#{auth[:type]}"
337
+ end
338
+
339
+ display_ipv4_addr(wifi_net[:IPv4], tabs)
340
+ display_ipv6_addr(wifi_net[:IPv6], tabs)
341
+ end
342
+
343
+ def display_ipv4_addr (ipv4, tabs)
344
+ indent = " " * tabs
345
+ if ipv4.instance_of? String
346
+ say indent + "IPv4 :#{ipv4}"
347
+ elsif ipv4.instance_of? NilClass
348
+ say indent + "IPv4 :off"
349
+ elsif ipv4.instance_of? Hash
350
+ addr_type = ipv4[:type]
351
+ if addr_type == "dhcp"
352
+ say indent + "IPv4 :#{addr_type}"
353
+ elsif addr_type == "static"
354
+ addr = ipv4[:address]
355
+ mask = ipv4[:mask]
356
+ gw = ipv4[:gateway]
357
+ say indent + "IPv4 :#{addr}/#{mask}/#{gw}"
358
+ else
359
+ say indent + "*** Invalid ipv4 address. Type is #{addr_type} ***"
360
+ end
361
+ else
362
+ say indent + "*** Invalid ipv4 address. Received as JSON class: #{ipv4.class} ***"
363
+ end
364
+ end
365
+
366
+ def display_ipv6_addr (ipv6, tabs)
367
+ indent = " " * tabs
368
+ if ipv6.instance_of? String
369
+ say indent + "IPv6 :#{ipv6}"
370
+ elsif ipv6.instance_of? NilClass
371
+ say indent + "IPv6 :off"
372
+ elsif ipv6.instance_of? Hash
373
+ addr_type = ipv6[:type]
374
+ if addr_type == "auto"
375
+ say indent + "IPv6 :#{addr_type}"
376
+ elsif addr_type == "static"
377
+ addr = ipv6[:address]
378
+ pref = ipv6[:prefix_length]
379
+ gw = ipv6[:gateway]
380
+ say indent + "IPv6 :#{addr}/#{pref}/#{gw}"
381
+ else
382
+ say indent + "*** Invalid ipv6 address. Type is #{addr_type} ***"
383
+ end
384
+ else
385
+ say indent + "*** Invalid ipv6 address. Received as JSON class: #{ipv6.class} ***"
386
+ end
387
+ end
388
+
389
+
390
+
391
+ #
392
+ # display the device info.
393
+ # device_data is a hash matching the json structure
394
+ #
395
+ def display_device(device_data)
396
+ name = device_data[:device_name]
397
+ ctl_addr = device_data[:control_address]
398
+ dev_addr = device_data[:device_address]
399
+ mac_addrs = device_data[:mac_address]
400
+ firmware = device_data[:firmware_version]
401
+ profile_id = device_data[:profile]
402
+ status = device_data[:status]
403
+
404
+ say "Serial Number : #{device_data[:serial_number]}"
405
+ say "Device Name : #{name}"
406
+ say "Control Address : #{ctl_addr}"
407
+ say "Device Address : #{dev_addr}"
408
+ say 'Mac Address :'
409
+ if mac_addrs
410
+ say " 1 : #{mac_addrs[:wifi]}"
411
+ say " 2 : #{mac_addrs[:'2']}"
412
+ say " 3 : #{mac_addrs[:'3']}"
413
+ say " 4 : #{mac_addrs[:'4']}"
414
+ end
415
+ say "Firmware Version : #{firmware}"
416
+ say "Profile ID : #{profile_id}"
417
+ say "Status : #{status}"
418
+ end
419
+
420
+ #
421
+ # display the devices summary list
422
+ #
423
+ def display_device_list(devices)
424
+ headings = ['Serial No', 'Dev Name', 'Dev Addr', 'Router Mode',
425
+ 'Connected', 'SSID']
426
+ rows = devices.map do |hash|
427
+ status = hash[:status]
428
+ if status
429
+ [hash[:serial_number], hash[:device_name], hash[:device_address],
430
+ status[:router_mode], status[:wifi][:connected],
431
+ status[:wifi][:SSID]]
432
+ else
433
+ [hash[:serial_number], hash[:device_name], hash[:device_address],
434
+ nil, nil, nil]
435
+ end
436
+ end
437
+ # [hash[:serial_number], hash[:device_name], hash[:device_address],
438
+ # hash[:status][:router_mode], hash[:status][:wifi][:connected],
439
+ # hash[:status][:wifi][:SSID]]
440
+
441
+ render_table(headings, rows)
442
+ end
443
+
444
+ #
445
+ # Display profile detail
446
+ #
447
+ def display_profile (profile)
448
+ say "Name :#{profile[:name]}"
449
+ say "Profile ID :#{profile[:id]}"
450
+ say "Configuration version :#{profile[:config][:version]}"
451
+ say "Mode :#{profile[:config][:mode]}"
452
+ display_wifi_summary profile[:config][:wifi]
453
+ end
454
+
455
+ #
456
+ # Display summary of the WIFI configuration info
457
+ #
458
+ def display_wifi_summary (wifi)
459
+ say "Wifi config :"
460
+ say " id :#{wifi[:id]}"
461
+ # TODO - make accessible only to Xaptum admins
462
+ say " domain :#{wifi[:domain]}"
463
+ say " name :#{wifi[:name]}"
464
+ desc = wifi[:description]
465
+ say " description :#{desc}" if desc
466
+ say " config version :#{wifi[:version]}"
467
+ end
468
+
469
+ #
470
+ # display the profile summary list
471
+ #
472
+ def display_profile_list(profiles)
473
+ headings = ['Profile Name', 'Profile ID', 'Version']
474
+ rows = profiles.map do |hash|
475
+ [hash[:name], hash[:id], hash[:config][:version]]
476
+ end
477
+
478
+ render_table(headings, rows)
479
+ end
480
+
481
+
482
+ end
483
+ end
484
+ end
485
+ end