-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrun_nxai_installer.sh
1406 lines (1204 loc) · 50 KB
/
run_nxai_installer.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env bash
###############################################################################
# Instructions
#
# Step 1: Set environment variables:
# source set_nxai_installer.sh
#
# Step 2: Install and configure NxAI:
# sudo -E bash run_nxai_installer.sh
#
# To run both steps in a single command:
# source set_nxai_installer.sh && sudo -E bash run_nxai_installer.sh
###############################################################################
###############################################################################
#
# This script automates installing and configuring Network Optix (Nx) software:
#
# 1) Installs dependencies and the Nx server (checks connectivity, sets OS info).
# 2) Authenticates, sets up the Nx system, and registers a license.
# 3) Installs Nx AI plugin, OAAX runtime, and, optional, a stream or testcamera.
# 4) Turns on minimal recording of on all cameras (that is, all "devices").
# 4) Connects to Nx Cloud and merges multiple Nx systems
# sharing the same name in the Cloud.
#
###############################################################################
# Use a special IFS setting to avoid issues with word splitting:
# - This helps handle filenames or other parameters with spaces/new lines
IFS=$'\n\t'
# Enforce that the script must be run as root, since installation steps
# and service restarts require elevated privileges.
if [ "$EUID" -ne 0 ]; then
echo "This script must be run as root. Exiting."
exit 1
fi
# Print an empty line at the start of the script
echo
# Use a trap to ensure an empty line is printed on exit
trap 'echo' EXIT
###############################################################################
# Required Environmental Variables
###############################################################################
# Define required variables
required_vars=(
NX_CLOUD_USER
NX_CLOUD_PASS
NX_SYSTEM_NAME
LOCAL_PASSWORD_NEW
)
# Initialize a flag to track missing variables
missing_vars=()
short_password_vars=()
# Check for unset required variables
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
missing_vars+=("$var")
elif [[ "$var" == "NX_CLOUD_PASS" || "$var" == "LOCAL_PASSWORD_NEW" ]]; then
# Check for password length
if [ "${#var}" -lt 8 ]; then
short_password_vars+=("$var")
fi
fi
done
# If any variables are missing, list them and exit
if [ "${#missing_vars[@]}" -gt 0 ]; then
echo -e "\033[1;31mERROR:\033[0m The following required environment variables are not set:"
echo "--------------------------------------------------"
for var in "${missing_vars[@]}"; do
echo -e " - \033[1;33m$var\033[0m"
done
echo "--------------------------------------------------"
echo -e "\033[1;31mPlease set the above environment variables\033[0m"
echo -e "\033[1;31m(e.g., export VAR_NAME='value') and re-run this script.\033[0m"
exit 1
fi
# If any passwords are too short, list them and exit
if [ "${#short_password_vars[@]}" -gt 0 ]; then
echo -e "\033[1;31mERROR:\033[0m The following password variables must be at least 8 characters long:"
echo "--------------------------------------------------"
for var in "${short_password_vars[@]}"; do
echo -e " - \033[1;33m$var\033[0m"
done
echo "--------------------------------------------------"
echo -e "\033[1;31mPlease update the above passwords to meet the length requirement and re-run this script.\033[0m"
exit 1
fi
###############################################################################
# Environment Variables and Defaults
# - Set defaults for environment variables if not already defined.
# - Notify the user if any required variables are unset.
###############################################################################
# Cloud Credentials & Connectivity
: "${NX_CLOUD_HOST:="meta.nxvms.com"}"
: "${NX_CLOUD_URL:="https://${NX_CLOUD_HOST}"}"
: "${NX_CLOUD_USER:=""}"
if [ -z "$NX_CLOUD_USER" ]; then
echo "Error: NX_CLOUD_USER is not set. Please set this variable."
exit 1
fi
: "${NX_CLOUD_PASS:=""}"
if [ -z "$NX_CLOUD_PASS" ]; then
echo "Error: NX_CLOUD_PASS is not set. Please set this variable."
exit 1
fi
# Nx Local Settings
: "${ENABLE_AI_PLUGIN:=true}" # Enable Nx AI Plugin
: "${ENABLE_SAME_NAME_SYSTEM_MERGE:=true}" # Allow merging systems with the same name in Nx Cloud
: "${ENABLE_DEVICE_RECORDING:=true}" # Enable minimal recording on all devices
: "${NX_SYSTEM_NAME:=""}"
if [ -z "$NX_SYSTEM_NAME" ]; then
echo "Error: NX_SYSTEM_NAME is not set. Please set this variable."
exit 1
fi
: "${SYSTEM_LICENSE_KEY:="0000-0000-0000-0029"}" # Default system license key
# Local Nx User Credentials
: "${LOCAL_LOGIN:="admin"}"
: "${LOCAL_PASSWORD:="admin"}"
: "${LOCAL_PASSWORD_NEW:=""}"
if [ -z "$LOCAL_PASSWORD_NEW" ]; then
echo "Error: LOCAL_PASSWORD_NEW is not set. Please set this variable."
exit 1
fi
: "${LOCAL_SERVER_URL:="https://localhost:7001"}" # Local Nx server URL
# Nx Server Download & Package URLs
: "${NX_SERVER_DOWNLOAD_URL:="https://updates.networkoptix.com/metavms/39873/linux/metavms-server-6.0.1.39873-linux_x64.deb"}"
: "${NX_AI_PLUGIN_VERSION:=""}"
: "${NX_AI_PLUGIN_INSTALL_URL:="https://artifactory.nxvms.dev/artifactory/nxai_open/NXAIPlugin/install.sh"}"
: "${NX_AI_PLUGIN_DOWNLOAD_URL:="https://artifactory.nxvms.dev/artifactory/nxai_open/NXAIManager/release/nxai_manager-x86_64.tgz"}"
: "${NX_AI_OAAX_RUNTIME_DOWNLOAD_URL:="https://artifactory.nxvms.dev/artifactory/nxai_open/OAAX/runtimes/release/cpu-x86_64-ort.tar.gz"}"
# Test Camera / Stream
: "${ENABLE_TESTCAMERA:=false}" # Enable testcamera
: "${MOVIE_DIR:="/opt/movies"}" # Directory to store test camera footage
: "${MOVIE_URL:="http://www.robinvanemden.dds.nl/walking.mp4"}" # Test movie URL
: "${MOVIE_FILE:=$(basename "$MOVIE_URL")}" # Extract the filename from the URL
: "${ENABLE_TEST_STREAM:=true}" # Enable test stream
: "${STREAM_URL:="rtsp://5.75.171.116:8554/face-blur-4-people"}" # Stream URL
: "${STREAM_USER:=""}" # Stream username
: "${STREAM_PASS:=""}" # Stream password
# Other Settings
: "${MAX_PLUGIN_ATTEMPTS:=10}" # Maximum attempts to wait for a plugin
: "${WAIT_TIMEOUT:=60}" # Timeout for operations in seconds
: "${RESPONSE_EXPIRATION_TIMEOUT_S:=10}" # Expiration timeout for responses in seconds
# Suppress interactive prompts during apt-get installations
DEBIAN_FRONTEND=noninteractive
###############################################################################
# Generic Helper Functions
###############################################################################
# These functions perform repeated operations like waiting for a service
# or restarting the Nx server.
# -----------------------------------------------------------------------------
# wait_for_service
# Usage:
# wait_for_service "My Service" "check_command_here" TIMEOUT_SECONDS
# Waits until 'check_command' succeeds or times out.
# -----------------------------------------------------------------------------
wait_for_service() {
local service_name="$1"
local check_command="$2"
local timeout="$3"
local elapsed_seconds=0
echo "Waiting for $service_name to start..."
# Loop until the check_command succeeds or we hit the timeout
while ! eval "$check_command"; do
sleep 1
(( elapsed_seconds++ ))
if [ "$elapsed_seconds" -ge "$timeout" ]; then
echo "Error: Timed out waiting for $service_name."
exit 1
fi
done
echo "$service_name is running."
}
# -----------------------------------------------------------------------------
# wait_for_nx_server
# Waits specifically for the Nx server to respond at LOCAL_SERVER_URL/api/ping.
# -----------------------------------------------------------------------------
wait_for_nx_server() {
wait_for_service "NX Server" \
"curl --insecure -sk -f \"$LOCAL_SERVER_URL/api/ping\" > /dev/null 2>&1" \
"$WAIT_TIMEOUT"
}
# -----------------------------------------------------------------------------
# restart_nx_media_server
# Restarts the Nx media server service and waits for it to become available.
# -----------------------------------------------------------------------------
restart_nx_media_server() {
echo "Restarting Nx Media Server..."
systemctl restart networkoptix-metavms-mediaserver.service || {
echo "Error: Failed to restart Nx Media Server."
exit 1
}
wait_for_nx_server
}
###############################################################################
# Connectivity and Environment Check Functions
###############################################################################
# -----------------------------------------------------------------------------
# check_connectivity
# Checks basic internet connectivity, DNS resolution, and access to NX_CLOUD_URL.
# -----------------------------------------------------------------------------
check_connectivity() {
echo ""
echo "=== Checking internet and DNS connectivity ==="
# Basic check: ping Google's DNS to verify internet connectivity.
echo "Checking internet connectivity..."
if ! ping -c 1 8.8.8.8 &>/dev/null; then
echo "Error: Unable to reach the internet. Check your network connection."
exit 1
fi
echo "Internet connectivity OK."
# DNS resolution check for the configured NX_CLOUD_HOST.
echo "Checking DNS resolution for $NX_CLOUD_HOST..."
if command -v dig &>/dev/null; then
# Use 'dig' if available
if ! dig +short "$NX_CLOUD_HOST" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' &>/dev/null; then
echo "Error: DNS resolution failed."
exit 1
fi
elif command -v nslookup &>/dev/null; then
# Use 'nslookup' if 'dig' is not available
if ! nslookup "$NX_CLOUD_HOST" &>/dev/null; then
echo "Error: DNS resolution failed."
exit 1
fi
else
echo "Warning: Neither 'dig' nor 'nslookup' is installed; skipping explicit DNS test."
fi
echo "DNS resolution for $NX_CLOUD_HOST OK."
# Test HTTP connectivity to the Nx cloud host.
echo "Checking HTTP connectivity to $NX_CLOUD_URL..."
if ! curl -sk --head --fail "$NX_CLOUD_URL" &>/dev/null; then
echo "Error: Unable to connect to $NX_CLOUD_URL."
exit 1
fi
echo "Connectivity to $NX_CLOUD_URL is OK."
}
# -----------------------------------------------------------------------------
# report_system_info
# Gathers and logs information about the OS version, glibc version, etc.
# -----------------------------------------------------------------------------
report_system_info() {
echo ""
echo "=== Gathering system information ==="
# OS version detection:
if command -v lsb_release &>/dev/null; then
echo "OS Version: $(lsb_release -ds)"
elif [ -f /etc/os-release ]; then
# If /etc/os-release is present, source it to get PRETTY_NAME.
. /etc/os-release
echo "OS Version: $PRETTY_NAME"
else
echo "Warning: Unable to determine OS version."
fi
# glibc version detection via 'ldd'
if command -v ldd &>/dev/null; then
local glibc_version
glibc_version=$(ldd --version | head -n1 | awk '{print $NF}')
echo "glibc Version: $glibc_version"
else
echo "Warning: 'ldd' not found; cannot determine glibc version."
fi
}
###############################################################################
# Nx Server and Dependencies Installation
###############################################################################
# These functions download, install, and verify Nx server and other dependencies.
# -----------------------------------------------------------------------------
# install_libasound
# Checks if libasound2 is available and installs it if not present.
# -----------------------------------------------------------------------------
install_libasound() {
local package="libasound2"
echo "Checking if '$package' is available in the repositories..."
if apt-cache search "^$package$" | grep -q "^$package"; then
echo "The package '$package' is available in the repositories."
echo "Proceeding to check installation..."
if dpkg -l | grep -q "^ii\s*$package"; then
echo "The package '$package' is already installed."
else
echo "The package '$package' is not installed. Installing now..."
apt-get -qq update && apt-get -qq install -y "$package" || {
echo "Not able to install '$package'."
}
fi
else
echo "The package '$package' is not available in the current repositories."
fi
}
# -----------------------------------------------------------------------------
# install_dep
# Installs the primary dependencies needed by Nx and this script:
# - libgomp1, gdebi, curl, jq, tar, cifs-utils, dnsutils, and libasound2
# -----------------------------------------------------------------------------
install_dep() {
echo ""
echo "=== Installing dependencies ==="
apt-get -qq update || {
echo "Error: apt-get update failed."
exit 1
}
# Attempt to install libasound2 by calling our helper function
install_libasound
echo "Installing required packages."
apt-get -qq install -y libgomp1 gdebi curl jq tar cifs-utils dnsutils || {
echo "Info: Failed to install required packages."
exit 1
}
echo "Dependencies installation completed."
}
# -----------------------------------------------------------------------------
# download_nx_server_package
# Downloads the Nx Server .deb from the NX_SERVER_DOWNLOAD_URL.
# -----------------------------------------------------------------------------
download_nx_server_package() {
echo "Downloading Nx server package from $NX_SERVER_DOWNLOAD_URL..."
curl -fsSL -o ./nx_server_package.deb "$NX_SERVER_DOWNLOAD_URL" || {
echo "Error: Failed to download Nx server package from $NX_SERVER_DOWNLOAD_URL"
exit 1
}
}
# -----------------------------------------------------------------------------
# install_nx_server_package
# Installs the downloaded Nx server .deb package.
# -----------------------------------------------------------------------------
install_nx_server_package() {
echo "Installing Nx server package..."
if ! DEBIAN_FRONTEND=noninteractive apt-get -qq install -y ./nx_server_package.deb; then
echo "Error: Failed to install nx_server_package.deb" >&2
exit 1
fi
echo "Nx server package installed successfully."
}
# -----------------------------------------------------------------------------
# install_nx
# Coordinates the download and installation of the Nx server .deb package,
# then waits for the Nx server to become available.
# -----------------------------------------------------------------------------
install_nx() {
echo ""
echo "=== Nx Server Installation ==="
download_nx_server_package
install_nx_server_package
# Clean up the .deb after installation
rm -f ./nx_server_package.deb
# Wait for Nx Server to become responsive on the configured API endpoint
wait_for_nx_server
}
###############################################################################
# Nx Authentication, Setup, and Registration
###############################################################################
# -----------------------------------------------------------------------------
# get_login_token
# Attempts to authenticate against the Nx server using the original password,
# then the new password if the original fails. Returns a valid token or exits.
# -----------------------------------------------------------------------------
get_login_token() {
local session_data
local token
# First try the original password
session_data=$(curl --insecure -sk -X POST "$LOCAL_SERVER_URL/rest/v3/login/sessions" \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d "{\"username\":\"$LOCAL_LOGIN\",\"password\":\"$LOCAL_PASSWORD\",\"setCookie\":true}") || {
echo "Error: Failed to perform initial login request."
exit 1
}
token=$(echo "$session_data" | jq -r '.token')
# If token is null, it means the original password didn't work,
# so try the new password.
if [ "$token" == "null" ]; then
session_data=$(curl --insecure -sk -X POST "$LOCAL_SERVER_URL/rest/v3/login/sessions" \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d "{\"username\":\"$LOCAL_LOGIN\",\"password\":\"$LOCAL_PASSWORD_NEW\",\"setCookie\":true}") || {
echo "Error: Failed to perform login request with new password."
exit 1
}
token=$(echo "$session_data" | jq -r '.token')
fi
# If token is still null, authentication has failed.
if [ "$token" == "null" ]; then
echo "Error: Authentication with Nx server failed."
exit 1
fi
# Echo the token so it can be captured in a calling function.
echo "$token"
}
# -----------------------------------------------------------------------------
# logout_session
# Logs out the specified session token from the Nx server.
# -----------------------------------------------------------------------------
logout_session() {
local token="$1"
curl --insecure -sk -X DELETE "$LOCAL_SERVER_URL/rest/v3/login/sessions/$token" >/dev/null 2>&1 || {
echo "Warning: Failed to log out session with token $token."
}
curl --insecure -sk -X DELETE "$LOCAL_SERVER_URL/rest/v3/login/sessions" >/dev/null 2>&1 || {
echo "Warning: Failed to delete all login sessions."
}
}
# -----------------------------------------------------------------------------
# system_setup
# Configures the Nx system with a new name and sets the local admin password
# via Nx's /rest/v3/system/setup endpoint.
# -----------------------------------------------------------------------------
system_setup() {
echo ""
echo "=== Configuring Nx system ==="
local token="$1"
local setup_payload
setup_payload="{\"name\":\"$NX_SYSTEM_NAME\",\"settingsPreset\":\"compatibility\",\"settings\":{},\"local\":{\"password\":\"$LOCAL_PASSWORD_NEW\",\"userAgent\":\"\"}}"
local setup_response
setup_response=$(curl --insecure -sk -X POST "$LOCAL_SERVER_URL/rest/v3/system/setup" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-H "x-runtime-guid: $token" \
-d "$setup_payload") || {
echo "Error: Failed to execute system setup request."
exit 1
}
local error_message
error_message=$(echo "$setup_response" | jq -r '.errorString // empty')
if [ -n "$error_message" ]; then
# This often occurs if the system was already initialized.
if [[ "$error_message" == *"Setup is only allowed for the new System"* ]]; then
echo "Info: $error_message"
else
echo "Error: $error_message"
exit 1
fi
else
echo "System setup completed successfully."
fi
}
# -----------------------------------------------------------------------------
# system_registration
# Registers the Nx system using the provided license ID.
# -----------------------------------------------------------------------------
system_registration() {
local token="$1"
local license_id="$2"
if [ -z "$license_id" ]; then
echo "Error: License ID is required."
exit 1
fi
echo ""
echo "=== Registering Nx system with license ID: $license_id ==="
local reg_payload='{
"licenseBlock": ""
}'
local reg_response
reg_response=$(curl --insecure -sk -X PUT "$LOCAL_SERVER_URL/rest/v3/licenses/$license_id?_strict=false" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-H "x-runtime-guid: $token" \
-d "$reg_payload") || {
echo "Error: Failed to execute license registration request."
exit 1
}
local error_message
error_message=$(echo "$reg_response" | jq -r '.errorString // empty')
# Check if the system is already registered or any other error has occurred.
if [ -n "$error_message" ]; then
if [[ "$error_message" == *"Reg is only allowed for unregistered System"* ]]; then
echo "Info: $error_message"
else
echo "Error: $reg_response"
echo "Token: $token"
exit 1
fi
else
echo "System registration completed successfully."
fi
}
###############################################################################
# NxAI Plugin Installation and Configuration
###############################################################################
# -----------------------------------------------------------------------------
# install_plugin
# Calls a remote install script for NxAI plugin, downloads NxAI Manager,
# and restarts Nx Media Server.
# -----------------------------------------------------------------------------
install_plugin() {
local token="$1"
echo ""
echo "=== Installing NxAI Plugin ==="
NX_AI_PLUGIN_VERSION_STRING=""
if [ -z "$NX_AI_PLUGIN_VERSION" ]; then
echo "Version: Latest release."
else
echo "Version: ${NX_AI_PLUGIN_VERSION}."
NX_AI_PLUGIN_VERSION_STRING="package=${NX_AI_PLUGIN_VERSION}"
fi
# The remote install script is executed silently, with output suppressed.
bash -c "$(curl -fsSL "$NX_AI_PLUGIN_INSTALL_URL")" -- "$NX_AI_PLUGIN_VERSION_STRING" || {
echo "Error: Failed to install NxAI plugin from $NX_AI_PLUGIN_INSTALL_URL"
exit 1
}
echo "Downloading NxAI Manager plugin..."
curl -fsSL -o ./nx_ai_plugin.tar.gz "$NX_AI_PLUGIN_DOWNLOAD_URL" || {
echo "Error: Failed to download NxAI Manager plugin from $NX_AI_PLUGIN_DOWNLOAD_URL"
exit 1
}
echo "Extracting NxAI Manager plugin..."
tar -xf ./nx_ai_plugin.tar.gz \
-C /opt/networkoptix-metavms/mediaserver/bin/plugins/nxai_plugin || {
echo "Error: Failed to extract NxAI Manager plugin tarball."
exit 1
}
rm -f ./nx_ai_plugin.tar.gz
# Restart the Nx server to ensure it picks up the new plugin.
restart_nx_media_server
}
###############################################################################
# Nx Cloud Connection
###############################################################################
# The functions below handle connecting the local Nx system to Nx Cloud.
# -----------------------------------------------------------------------------
# cloud_connect
# Sends a request to Nx Cloud to create or retrieve a system "connect" token.
# Returns an HTTP status code and response body in the format "<status>:<body>"
# -----------------------------------------------------------------------------
cloud_connect() {
local token="$1"
local cloud_credentials
# Build a JSON object using jq -nc for a new system in Nx cloud
cloud_credentials=$(jq -nc \
--arg name "$NX_SYSTEM_NAME" \
--arg email "$NX_CLOUD_USER" \
--arg password "$NX_CLOUD_PASS" \
'{"name":$name,"email":$email,"password":$password}')
local curl_output
curl_output=$(curl --insecure -sk -w "%{http_code}" -X POST "$NX_CLOUD_URL/api/systems/connect" \
-H "Content-Type: application/json" \
-d "$cloud_credentials" || true)
# Extract the last 3 characters (HTTP code) and everything before that as body
local len=${#curl_output}
if (( len < 3 )); then
echo "Error: Unexpectedly short response from cloud_connect: '$curl_output'"
exit 1
fi
local status_code="${curl_output: -3}"
local response_body="${curl_output::len-3}"
echo "$status_code:$response_body"
}
# -----------------------------------------------------------------------------
# cloud_bind
# Binds the local Nx system to the Nx Cloud system ID using the authKey/owner.
# -----------------------------------------------------------------------------
cloud_bind() {
local token="$1"
local system_id="$2"
local auth_key="$3"
local owner="$4"
local bind_payload
bind_payload=$(jq -nc \
--arg systemId "$system_id" \
--arg authKey "$auth_key" \
--arg owner "$owner" \
'{"systemId":$systemId,"authKey":$authKey,"owner":$owner}')
local bind_response
bind_response=$(curl --insecure -sk -w "%{http_code}" -X POST "$LOCAL_SERVER_URL/rest/v3/system/cloud/bind" \
-H "Content-Type: application/json" \
-H "x-runtime-guid: $token" \
-d "$bind_payload" || true)
local len=${#bind_response}
if (( len < 3 )); then
echo "Error: Unexpectedly short response from cloud_bind: '$bind_response'"
exit 1
fi
local bind_status="${bind_response: -3}"
local bind_body="${bind_response::len-3}"
# Return a concatenation of JSON response plus the HTTP status code
echo "${bind_body}${bind_status}"
}
# -----------------------------------------------------------------------------
# connect_to_cloud
# Orchestrates the cloud connect and bind processes,
# including handling various HTTP statuses.
# -----------------------------------------------------------------------------
connect_to_cloud() {
echo ""
echo "=== Connecting the Nx system to the cloud ==="
local token="$1"
local connect_response
connect_response=$(cloud_connect "$token")
# The format is <status_code>:<response_body>
local status_code="${connect_response%%:*}"
local response_body="${connect_response#*:}"
# Validate that we have a 3-digit HTTP status code
if [[ ! "$status_code" =~ ^[0-9]{3}$ ]]; then
echo "Error: Could not parse a valid HTTP status code from '$connect_response'"
exit 1
fi
if [ "$status_code" -eq 200 ]; then
# Retrieve necessary fields from the response for binding
local system_id
system_id=$(echo "$response_body" | jq -r '.id')
local auth_key
auth_key=$(echo "$response_body" | jq -r '.authKey')
local owner
owner=$(echo "$response_body" | jq -r '.ownerAccountEmail')
# Validate that all required fields were returned
if [ -z "$system_id" ] || [ -z "$auth_key" ] || [ -z "$owner" ] ||
[ "$system_id" == "null" ] || [ "$auth_key" == "null" ] || [ "$owner" == "null" ]; then
echo "Error: Cloud connect response did not contain valid systemId/authKey/owner."
exit 1
fi
local bind_response
bind_response=$(cloud_bind "$token" "$system_id" "$auth_key" "$owner")
local bind_status="${bind_response: -3}"
local bind_body="${bind_response::${#bind_response}-3}"
if [ "$bind_status" -eq 200 ]; then
echo -e "\e[32mSuccessfully connected $NX_SYSTEM_NAME to the cloud as $NX_CLOUD_USER.\e[0m"
else
# Check if the system was already bound or if there's another error
local error_string
error_string=$(echo "$bind_body" | jq -r '.errorString // empty')
if [[ "$error_string" == *"already bound"* ]]; then
echo "Info: System $NX_SYSTEM_NAME is already bound to the cloud."
else
echo -e "\e[31mCould not bind $NX_SYSTEM_NAME to the cloud. Response: $bind_response.\e[0m"
fi
fi
elif [ "$status_code" -eq 401 ]; then
echo -e "\e[31mCloud authentication failed. Check username/password.\e[0m"
else
echo -e "\e[31mFailed to connect $NX_SYSTEM_NAME to the cloud. Status: $status_code, Response: $response_body\e[0m"
fi
}
###############################################################################
# Test Camera Setup
###############################################################################
# -----------------------------------------------------------------------------
# install_testcamera
# Creates a movie directory, downloads a sample MP4, and runs Nx testcamera.
# -----------------------------------------------------------------------------
install_testcamera() {
echo ""
echo "=== Setting up the test camera ==="
echo "Creating movie directory: $MOVIE_DIR..."
mkdir -p "$MOVIE_DIR" || {
echo "Error: Failed to create movie directory."
exit 1
}
chmod 775 "$MOVIE_DIR" || {
echo "Error: Failed to set permissions on movie directory."
exit 1
}
echo "Downloading sample movie from $MOVIE_URL..."
curl -fsSL -o "$MOVIE_DIR/$MOVIE_FILE" "$MOVIE_URL" || {
echo "Error: Failed to download sample movie from $MOVIE_URL"
exit 1
}
chmod 775 "$MOVIE_DIR/$MOVIE_FILE" || {
echo "Error: Failed to set permissions on the downloaded movie file."
exit 1
}
echo "Starting test camera service..."
# The testcamera process simulates a video stream from the downloaded MP4.
nohup /opt/networkoptix-metavms/mediaserver/bin/testcamera --fps=24 -S "files=$MOVIE_DIR/$MOVIE_FILE" </dev/null >/dev/null 2>&1 &
echo "Test camera started successfully."
}
# -----------------------------------------------------------------------------
# install_test_stream
# Creates a movie directory, downloads a sample MP4, and runs Nx testcamera.
# -----------------------------------------------------------------------------
install_test_stream() {
echo ""
echo "=== Setting up the test stream ==="
local token="$1"
echo "Searching for $STREAM_URL"
local dev_enable_payload="{
\"credentials\": {
\"user\": \"$STREAM_USER\",
\"password\": \"$STREAM_PASS\"
},
\"mode\": \"addFoundDevices\",
\"target\": {
\"ip\": \"$STREAM_URL\"
}
}"
local dev_enable_response
dev_enable_response=$(curl --insecure -sk -X POST "$LOCAL_SERVER_URL/rest/v3/devices/*/searches" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-H "x-runtime-guid: $token" \
-d "$dev_enable_payload") || {
echo "Error: Failed to enable stream."
exit 1
}
local error_message
error_message=$(echo "$dev_enable_response" | jq -r '.errorString // empty')
if [ -n "$error_message" ]; then
echo "Failed to enable stream."
else
echo "Stream enabled successfully."
fi
}
###############################################################################
# OAAX Runtime Installation
###############################################################################
# This runtime is needed for Nx AI analytics to function properly.
# -----------------------------------------------------------------------------
# install_oaax_runtime
# Downloads and extracts the runtime tar.gz for Nx AI analytics,
# then restarts Nx Media Server.
# -----------------------------------------------------------------------------
install_oaax_runtime() {
local token="$1"
echo ""
echo "=== Installing OAAX runtime ==="
echo "Downloading OAAX runtime from $NX_AI_OAAX_RUNTIME_DOWNLOAD_URL..."
curl -fsSL -o ./runtime.tar.gz "$NX_AI_OAAX_RUNTIME_DOWNLOAD_URL" || {
echo "Error: Failed to download OAAX runtime from $NX_AI_OAAX_RUNTIME_DOWNLOAD_URL"
exit 1
}
echo "Extracting OAAX runtime..."
tar -xf ./runtime.tar.gz \
-C /opt/networkoptix-metavms/mediaserver/bin/plugins/nxai_plugin/nxai_manager/bin || {
echo "Error: Failed to extract OAAX runtime."
exit 1
}
# Write a simple identifier file for this runtime
echo "Nx CPU" | tee /opt/networkoptix-metavms/mediaserver/bin/plugins/nxai_plugin/nxai_manager/bin/installed_runtime.txt >/dev/null || {
echo "Error: Failed to write runtime identifier file."
exit 1
}
rm -f ./runtime.tar.gz
restart_nx_media_server
}
###############################################################################
# Enable/Disable NxAI Plugin on Detected Devices
###############################################################################
# -----------------------------------------------------------------------------
# enable_device_recording
# Enables basic continuous recording on a given device.
# -----------------------------------------------------------------------------
enable_device_recording() {
local token="$1"
local device_id="$2"
if [ -z "$device_id" ]; then
echo "Error: Device ID is required."
exit 1
fi
echo "Enabling basic recording for device: $device_id"
# Minimal schedule that always records at the lowest stream quality
local dev_enable_payload='{
"parameters": {},
"schedule": {
"isEnabled": true,
"tasks": [
{
"dayOfWeek": 1,
"startTime": 0,
"endTime": 86400,
"recordingType": "always",
"streamQuality": "low",
"fps": 15,
"bitrateKbps": 0,
"metadataTypes": "none"
},
{
"dayOfWeek": 2,
"startTime": 0,
"endTime": 86400,
"recordingType": "always",
"streamQuality": "low",
"fps": 15,
"bitrateKbps": 0,
"metadataTypes": "none"
},
{
"dayOfWeek": 3,
"startTime": 0,
"endTime": 86400,
"recordingType": "always",
"streamQuality": "low",
"fps": 15,
"bitrateKbps": 0,
"metadataTypes": "none"
},
{
"dayOfWeek": 4,
"startTime": 0,
"endTime": 86400,
"recordingType": "always",
"streamQuality": "low",
"fps": 15,
"bitrateKbps": 0,
"metadataTypes": "none"
},
{
"dayOfWeek": 5,
"startTime": 0,
"endTime": 86400,
"recordingType": "always",
"streamQuality": "low",
"fps": 15,
"bitrateKbps": 0,
"metadataTypes": "none"
},
{
"dayOfWeek": 6,
"startTime": 0,
"endTime": 86400,
"recordingType": "always",
"streamQuality": "low",
"fps": 15,
"bitrateKbps": 0,
"metadataTypes": "none"
},
{
"dayOfWeek": 7,
"startTime": 0,
"endTime": 86400,
"recordingType": "always",
"streamQuality": "low",
"fps": 15,
"bitrateKbps": 0,
"metadataTypes": "none"
}
]
}
}'
local dev_enable_response
dev_enable_response=$(curl --insecure -sk -X PATCH "$LOCAL_SERVER_URL/rest/v3/devices/$device_id?_strict=false" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-H "x-runtime-guid: $token" \
-d "$dev_enable_payload") || {
echo "Error: Failed to enable device recording."
exit 1
}
local error_message
error_message=$(echo "$dev_enable_response" | jq -r '.errorString // empty')
if [ -n "$error_message" ]; then
if [[ "$error_message" == *"Reg is only allowed for unregistered System"* ]]; then
echo "Info: $error_message"
else
echo "Error: $error_message"
exit 1
fi
else
echo "Device recording enabled successfully."
fi
}
# -----------------------------------------------------------------------------
# enable_plugin
# Retrieves the Nx AI Manager plugin ID, waits for devices to appear,
# and either enables or disables the NxAI plugin on those devices
# based on ENABLE_AI_PLUGIN.
# -----------------------------------------------------------------------------
enable_plugin() {
local token="$1"
echo ""
echo "=== Adjusting NxAI Plugin availability on devices ==="
local base_url="$LOCAL_SERVER_URL/rest/v3"
echo "Retrieving Nx AI Manager plugin ID..."
local plugin_response
plugin_response=$(curl -sk -H "Authorization: Bearer $token" "$base_url/analytics/engines") || {
echo "Error: Failed to retrieve analytics engines."
exit 1