Repository navigation
Expand file tree
/
Copy pathcloudstack_install.sh
More file actions
1052 lines (854 loc) · 30.9 KB
/
Copy pathcloudstack_install.sh
File metadata and controls
1052 lines (854 loc) · 30.9 KB
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
#!/bin/bash
# CloudStack 4.20.0 Installation Script
# Author: ParrotXray
# Date: $(date)
# Support OS: Ubuntu 24.04
set -e # Exit on any error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
LIGHT_CYAN='\033[1;36m'
NC='\033[0m' # No Color
# Global variables for architecture
OS_VERSION="24.04"
ARCH=""
SYSTEMVM_URL=""
IS_UEFI=false
BOOT_TYPE=""
# Logging function
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}
error() {
echo -e "${RED}[ERROR] $1${NC}" >&2
}
warning() {
echo -e "${YELLOW}[WARNING] $1${NC}"
}
info() {
echo -e "${BLUE}[INFO] $1${NC}"
}
# Check if running as root
check_root() {
if [[ $EUID -ne 0 ]]; then
error "This script must be run as root"
exit 1
fi
}
# General package installation function
ensure_packages() {
local packages_needed=()
local all_packages=("$@")
if [ ${#all_packages[@]} -eq 0 ]; then
warning "No packages specified for installation check"
return 1
fi
log "Checking packages: ${all_packages[*]}"
for package in "${all_packages[@]}"; do
if ! dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "install ok installed"; then
packages_needed+=("$package")
info "Package '$package' needs to be installed"
else
info "Package '$package' already installed"
fi
done
if [ ${#packages_needed[@]} -gt 0 ]; then
log "Installing missing packages: ${packages_needed[*]}"
if ! apt update; then
error "Failed to update package list"
return 1
fi
if apt install "${packages_needed[@]}" -y; then
log "Packages installed successfully: ${packages_needed[*]}"
local failed_packages=()
for package in "${packages_needed[@]}"; do
if ! dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "install ok installed"; then
failed_packages+=("$package")
fi
done
if [ ${#failed_packages[@]} -gt 0 ]; then
error "Installation verification failed for: ${failed_packages[*]}"
return 1
fi
else
error "Failed to install packages: ${packages_needed[*]}"
return 1
fi
else
log "All requested packages are already installed, skipping..."
fi
return 0
}
# Function to prompt for user input
prompt_input() {
local prompt="$1"
local var_name="$2"
local default="$3"
if [ -n "$default" ]; then
read -p "${prompt} (default: ${default}): " input
if [ -z "$input" ]; then
input="$default"
fi
else
read -p "${prompt}: " input
while [ -z "$input" ]; do
read -p "This field is required. ${prompt}: " input
done
fi
eval "$var_name='$input'"
}
# Check UEFI/BIOS/U-Boot boot mode
check_boot_mode() {
log "Detecting boot mode (BIOS/UEFI/U-Boot)..."
# Check for U-Boot first (ARM devices often use U-Boot)
if [ -f "/proc/device-tree/chosen/bootargs" ] || [ -d "/proc/device-tree" ]; then
IS_UEFI=false
BOOT_TYPE="U-Boot"
log "U-Boot boot mode detected (Device Tree found)"
# Check /sys/firmware/efi directory for UEFI
elif [ -d "/sys/firmware/efi" ]; then
IS_UEFI=true
BOOT_TYPE="UEFI"
log "UEFI boot mode detected"
else
IS_UEFI=false
BOOT_TYPE="Legacy BIOS"
log "Legacy BIOS boot mode detected"
fi
info "Boot Type: $BOOT_TYPE"
}
check_system_requirements() {
log "Checking system requirements..."
# OS Check
if [ -f /etc/debian_version ]; then
ensure_packages lsb-release
local distro=$(lsb_release -i -s)
local version=$(lsb_release -r -s)
local codename=$(lsb_release -c -s)
if [[ "$distro" != "Ubuntu" ]]; then
error "Distribution '$distro' is not supported"
echo "Required: Ubuntu $OS_VERSION"
exit 1
fi
if [[ "$version" != "$OS_VERSION" ]]; then
error "Ubuntu $version is not supported"
echo "Required: Ubuntu $OS_VERSION"
echo "Current: Ubuntu $version ($codename)"
exit 1
fi
log "Ubuntu $version ($codename) - supported"
elif [ -f /etc/redhat-release ]; then
local redhat_version=$(cat /etc/redhat-release)
error "Red Hat-based system detected: $redhat_version"
echo "This script requires Ubuntu $OS_VERSION"
exit 1
else
error "Unsupported operating system"
echo "This script requires Ubuntu $OS_VERSION"
exit 1
fi
log "System requirements check passed"
}
# Check CPU architecture and set appropriate variables
check_architecture() {
log "Detecting CPU architecture..."
arch_output=$(uname -m)
os_arch=$(dpkg --print-architecture)
info "System architecture: $arch_output"
info "OS architecture: $os_arch"
case "$arch_output" in
x86_64|amd64)
ARCH="amd64"
SYSTEMVM_URL="https://download.cloudstack.org/systemvm/4.20/systemvmtemplate-4.20.1-x86_64-kvm.qcow2.bz2"
log "Detected AMD64/x86_64 architecture"
;;
aarch64|arm64)
ARCH="aarch64"
SYSTEMVM_URL="https://download.cloudstack.org/systemvm/4.20/systemvmtemplate-4.20.1-aarch64-kvm.qcow2.bz2"
log "Detected AArch64/ARM64 architecture"
;;
armv7l|armhf)
error "ARM 32-bit (ARMv7) is not supported by CloudStack 4.20"
echo "Supported architectures: AMD64/x86_64, AArch64/ARM64"
exit 1
;;
*)
error "Unsupported architecture: $arch_output"
echo "Supported architectures: AMD64/x86_64, AArch64/ARM64"
echo "Current architecture: $arch_output"
exit 1
;;
esac
# Verify architecture compatibility with OS
case "$os_arch" in
amd64)
if [ "$ARCH" != "amd64" ]; then
error "Architecture mismatch detected. OS: $os_arch, Hardware: $arch_output"
exit 1
fi
;;
arm64)
if [ "$ARCH" != "aarch64" ]; then
error "Architecture mismatch detected. OS: $os_arch, Hardware: $arch_output"
exit 1
fi
;;
esac
info "Using SystemVM template: $SYSTEMVM_URL"
}
# Check network interface exists
check_network_interface() {
if ! ip link show "$NATNIC" >/dev/null 2>&1; then
error "Network interface '$NATNIC' does not exist!"
echo -e "\nAvailable interfaces:"
ip link show | grep -E '^[0-9]+:' | awk '{print $2}' | sed 's/://' | grep -v lo
return 1
fi
return 0
}
select_interface() {
echo -e "\n${BLUE}Available network interfaces:${NC}"
ip link show | grep -E '^[0-9]+:' | awk '{print $2}' | sed 's/://' | grep -v lo
while true; do
prompt_input "Enter your network interface name (e.g., eth0, ens33)" "NATNIC"
if check_network_interface; then
break
fi
done
}
collect_network_ip_info() {
# Show current IP configuration
echo -e "\n${BLUE}Current configuration of $NATNIC:${NC}"
ip addr show $NATNIC
prompt_input "Enter your desired IP address (e.g., 192.168.4.100)" "LANIP"
prompt_input "Enter CIDR notation (e.g., /24, /21)" "CIDR"
prompt_input "Enter gateway IP (e.g., 192.168.4.1)" "GATEWAY"
prompt_input "Enter DNS1 (e.g., 8.8.8.8)" "DNS1"
prompt_input "Enter DNS2 (e.g., 8.8.4.4)" "DNS2"
# Validate IP addresses
if ! [[ $LANIP =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
error "Invalid IP address format: $LANIP"
exit 1
fi
if ! [[ $GATEWAY =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
error "Invalid gateway IP format: $GATEWAY"
exit 1
fi
if ! [[ $DNS1 =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
error "Invalid DNS1 format: $DNS1"
exit 1
fi
if ! [[ $DNS2 =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
error "Invalid DNS2 format: $DNS2"
exit 1
fi
log "Network configuration collected:"
info "Interface: $NATNIC"
info "IP: $LANIP$CIDR"
info "Gateway: $GATEWAY"
info "DNS1: $DNS1"
info "DNS2: $DNS2"
}
# Collect network configuration
collect_network_config() {
log "Collecting network configuration..."
select_interface
collect_network_ip_info
}
# Generate secure key
generate_secure_key() {
local prefix="$1"
echo "${prefix}-$(date +%Y%m%d)-$(openssl rand -hex 8)"
}
# Collect MySQL passwords
collect_mysql_config() {
log "Collecting MySQL configuration..."
prompt_input "Enter MySQL root password" "MYSQL_ROOT_PASSWORD"
prompt_input "Enter MySQL cloud user password" "MYSQL_CLOUD_PASSWORD"
# Generate suggested keys
SUGGESTED_MGT_KEY=$(generate_secure_key "CS-MGT")
SUGGESTED_DB_KEY=$(generate_secure_key "CS-DB")
echo -e "\n${YELLOW}Important: Management and Database keys are used for encryption and security.${NC}"
echo -e "${YELLOW}Suggested secure keys have been generated for you.${NC}"
prompt_input "Enter CloudStack management server key" "MANAGEMENT_SERVER_KEY" "$SUGGESTED_MGT_KEY"
prompt_input "Enter CloudStack database key" "DATABASE_KEY" "$SUGGESTED_DB_KEY"
# Save keys to file for reference
cat > /root/cloudstack_keys.txt << EOF
CloudStack Installation Keys - $(date)
=====================================
Management Server Key: ${MANAGEMENT_SERVER_KEY}
Database Key: ${DATABASE_KEY}
MySQL Root Password: ${MYSQL_ROOT_PASSWORD}
MySQL Cloud Password: ${MYSQL_CLOUD_PASSWORD}
IMPORTANT: Keep this file secure and backed up!
These keys are required for CloudStack operation.
EOF
chmod 600 /root/cloudstack_keys.txt
warning "Keys saved to /root/cloudstack_keys.txt - Please backup this file!"
}
# Install basic requirements
install_requirements() {
log "Installing basic requirements..."
apt update
ensure_packages vim openntpd
log "Basic requirements installed successfully"
}
# Install and configure SSH
install_ssh() {
log "Installing and configuring SSH..."
ensure_packages openssh-server
# Backup original sshd_config
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
# Configure SSH
cat >> /etc/ssh/sshd_config << EOF
# CloudStack SSH Configuration
PermitRootLogin yes
KexAlgorithms=+diffie-hellman-group-exchange-sha1
PubkeyAcceptedKeyTypes=+ssh-dss
HostKeyAlgorithms=+ssh-dss
KexAlgorithms=+diffie-hellman-group1-sha1
EOF
systemctl restart ssh
log "SSH configured successfully"
}
# Configure network
configure_network() {
log "Configuring network..."
ensure_packages net-tools bridge-utils
# Get current netplan files
NETPLAN_FILES=($(ls /etc/netplan/*.yaml 2>/dev/null || echo))
# Backup existing netplan configurations
for file in "${NETPLAN_FILES[@]}"; do
if [ -f "$file" ]; then
cp "$file" "${file}.backup.$(date +%Y%m%d_%H%M%S)"
log "Backed up $file"
fi
done
# Remove existing configurations to avoid conflicts
rm -f /etc/netplan/*.yaml
# Create new netplan configuration
NETPLAN_CONFIG="/etc/netplan/01-network-manager-all.yaml"
cat > $NETPLAN_CONFIG << EOF
network:
version: 2
renderer: networkd
ethernets:
${NATNIC}:
dhcp4: false
dhcp6: false
optional: true
bridges:
cloudbr0:
dhcp4: false
dhcp6: false
interfaces: [${NATNIC}]
addresses: [${LANIP}${CIDR}]
routes:
- to: default
via: ${GATEWAY}
nameservers:
addresses: [${DNS1}, ${DNS2}]
parameters:
stp: false
forward-delay: 0
EOF
# Set correct permissions
chmod 600 $NETPLAN_CONFIG
chown root:root $NETPLAN_CONFIG
log "Network configuration created with correct permissions"
# Validate configuration
if ! netplan generate; then
error "Network configuration validation failed"
exit 1
fi
warning "Network configuration will be applied. This may temporarily disconnect your connection."
warning "Make sure you have physical access to the server!"
echo -e "\nCurrent network configuration:"
cat $NETPLAN_CONFIG
read -p "Press Enter to apply the configuration (Ctrl+C to abort)..."
# Apply configuration directly (skip try as it may not work with bridges)
if netplan apply; then
log "Network configuration applied successfully"
# Wait for network to stabilize
sleep 5
# Verify bridge is created
if ip addr show cloudbr0 >/dev/null 2>&1; then
log "Bridge cloudbr0 created successfully"
ip addr show cloudbr0
else
error "Bridge cloudbr0 was not created properly"
exit 1
fi
# Test connectivity
if ping -c 3 $GATEWAY >/dev/null 2>&1; then
log "Network connectivity verified"
else
error "Cannot ping gateway. Please check network configuration."
exit 1
fi
else
error "Failed to apply network configuration"
# Try to restore backup
if [ ${#NETPLAN_FILES[@]} -gt 0 ]; then
warning "Attempting to restore original configuration..."
rm -f $NETPLAN_CONFIG
for file in "${NETPLAN_FILES[@]}"; do
backup_file="${file}.backup.$(date +%Y%m%d)_*"
if ls $backup_file >/dev/null 2>&1; then
latest_backup=$(ls -t ${backup_file} | head -1)
cp "$latest_backup" "$file"
fi
done
netplan apply
fi
exit 1
fi
}
# Install and configure NFS
install_nfs() {
log "Installing and configuring NFS..."
ensure_packages nfs-kernel-server nfs-common
# Create NFS directories
mkdir -p /export
mkdir -m 777 /export/primary
mkdir -m 777 /export/secondary
mkdir -m 777 /mnt/primary
mkdir -m 777 /mnt/secondary
# Configure NFS exports
echo "/export/secondary *(rw,async,no_root_squash,no_subtree_check)" >> /etc/exports
echo "/export/primary *(rw,async,no_root_squash,no_subtree_check)" >> /etc/exports
# Configure NFS kernel server
cat >> /etc/default/nfs-kernel-server << EOF
LOCKD_TCPPORT=32803
LOCKD_UDPPORT=32769
MOUNTD_PORT=892
RQUOTAD_PORT=875
STATD_PORT=662
STATD_OUTGOING_PORT=2020
EOF
systemctl enable nfs-kernel-server
systemctl restart nfs-kernel-server
exportfs -a
# Configure fstab for auto-mounting
echo "${LANIP}:/export/primary /mnt/primary nfs defaults 0 0" >> /etc/fstab
echo "${LANIP}:/export/secondary /mnt/secondary nfs defaults 0 0" >> /etc/fstab
systemctl daemon-reload
mount -a
log "NFS configured successfully"
}
# Install and configure MySQL
install_mysql() {
log "Installing and configuring MySQL..."
ensure_packages mysql-server
# Configure MySQL for CloudStack
cat > /etc/mysql/mysql.conf.d/mysqld.cnf << EOF
[mysqld]
server-id=master-01
sql-mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,ERROR_FOR_DIVISION_BY_ZERO,NO_ZERO_DATE,NO_ZERO_IN_DATE,NO_ENGINE_SUBSTITUTION"
innodb_rollback_on_timeout=1
innodb_lock_wait_timeout=600
max_connections=1000
log-bin=mysql-bin
binlog-format = 'ROW'
EOF
systemctl enable mysql.service
systemctl start mysql.service
# Set MySQL root password
mysql -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password by '${MYSQL_ROOT_PASSWORD}';"
log "MySQL configured successfully"
}
# Run MySQL secure installation
secure_mysql() {
log "Running MySQL secure installation..."
# Use expect to automate mysql_secure_installation
ensure_packages expect
expect -c "
spawn mysql_secure_installation
expect \"Enter password for user root:\"
send \"${MYSQL_ROOT_PASSWORD}\r\"
expect \"Press y|Y for Yes, any other key for No:\"
send \"n\r\"
expect \"Change the password for root ?\"
send \"n\r\"
expect \"Remove anonymous users?\"
send \"n\r\"
expect \"Disallow root login remotely?\"
send \"y\r\"
expect \"Remove test database and access to it?\"
send \"y\r\"
expect \"Reload privilege tables now?\"
send \"y\r\"
expect eof
"
log "MySQL secured successfully"
}
# Install CloudStack Management
install_cloudstack_management() {
log "Installing CloudStack Management..."
# Add CloudStack repository
# echo "deb http://download.cloudstack.org/ubuntu noble 4.20" > /etc/apt/sources.list.d/cloudstack.list
# wget -O - http://download.cloudstack.org/release.asc | apt-key add -
mkdir -p /etc/apt/keyrings
wget -O- http://download.cloudstack.org/release.asc | gpg --dearmor | sudo tee /etc/apt/keyrings/cloudstack.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/cloudstack.gpg] http://download.cloudstack.org/ubuntu noble 4.20" | sudo tee /etc/apt/sources.list.d/cloudstack.list
apt update
ensure_packages cloudstack-management cloudstack-usage
# Setup CloudStack database
cloudstack-setup-databases cloud:${MYSQL_CLOUD_PASSWORD}@localhost \
--deploy-as=root:${MYSQL_ROOT_PASSWORD} \
-e file \
-m ${MANAGEMENT_SERVER_KEY} \
-k ${DATABASE_KEY} \
-i ${LANIP}
# Stop the automatic start after install
systemctl stop cloudstack-management cloudstack-usage
log "CloudStack Management installed successfully"
}
# Install SystemVM template
install_systemvm() {
log "Installing SystemVM template..."
/usr/share/cloudstack-common/scripts/storage/secondary/cloud-install-sys-tmplt \
-m /mnt/secondary \
-u ${SYSTEMVM_URL} \
-h kvm \
-s ${MANAGEMENT_SERVER_KEY} \
-F
# Configure sudoers
echo "Defaults:cloud !requiretty" >> /etc/sudoers
log "SystemVM template installed successfully"
}
# Install CloudStack Agent
install_cloudstack_agent() {
log "Installing CloudStack Agent..."
ensure_packages cloudstack-agent
systemctl enable cloudstack-agent.service
# Stop the automatic start after install
systemctl stop cloudstack-agent
cp /etc/libvirt/qemu.conf /etc/libvirt/qemu.conf.backup
# Configure QEMU
sed -i 's/#vnc_listen = "0.0.0.0"/vnc_listen = "0.0.0.0"/' /etc/libvirt/qemu.conf
# Configure NVRAM settings based on boot mode and architecture
if [ "$IS_UEFI" = true ]; then
log "Configuring QEMU for UEFI support..."
if [ "$ARCH" = "amd64" ]; then
# Configure NVRAM for x86_64 UEFI
sed -i '/^#nvram = \[/,/^#\]/c\
nvram = [\
"/usr/share/OVMF/OVMF_CODE_4M.fd:/usr/share/OVMF/OVMF_VARS_4M.fd",\
"/usr/share/OVMF/OVMF_CODE_4M.secboot.fd:/usr/share/OVMF/OVMF_VARS_4M.fd",\
"/usr/share/OVMF/OVMF_CODE_4M.ms.fd:/usr/share/OVMF/OVMF_VARS_4M.ms.fd"\
]' /etc/libvirt/qemu.conf
elif [ "$ARCH" = "aarch64" ]; then
# Configure NVRAM for ARM64 UEFI
sed -i '/^#nvram = \[/,/^#\]/c\
nvram = [\
"/usr/share/AAVMF/AAVMF_CODE.fd:/usr/share/AAVMF/AAVMF_VARS.fd",\
"/usr/share/AAVMF/AAVMF_CODE.secboot.fd:/usr/share/AAVMF/AAVMF_VARS.fd",\
"/usr/share/AAVMF/AAVMF_CODE.ms.fd:/usr/share/AAVMF/AAVMF_VARS.ms.fd",\
"/usr/share/AAVMF/AAVMF_CODE.no-secboot.fd:/usr/share/AAVMF/AAVMF_VARS.fd"\
]' /etc/libvirt/qemu.conf
fi
info "QEMU configured for UEFI boot support"
else
info "QEMU configured for Legacy BIOS boot support"
fi
# Configure libvirtd
sed -i 's/#listen_tls = 0/listen_tls = 0/' /etc/libvirt/libvirtd.conf
sed -i 's/#listen_tcp = 1/listen_tcp = 1/' /etc/libvirt/libvirtd.conf
sed -i 's/#tcp_port = "16509"/tcp_port = "16509"/' /etc/libvirt/libvirtd.conf
sed -i 's/#auth_tcp = "sasl"/auth_tcp = "none"/' /etc/libvirt/libvirtd.conf
sed -i 's/#mdns_adv = 1/mdns_adv = 0/' /etc/libvirt/libvirtd.conf
# Configure libvirtd args
sed -i 's/#LIBVIRTD_ARGS="--listen"/LIBVIRTD_ARGS="--listen"/' /etc/default/libvirtd
# Mask libvirt sockets
systemctl mask libvirtd.socket libvirtd-ro.socket \
libvirtd-admin.socket libvirtd-tls.socket libvirtd-tcp.socket
# Add remote_mode="legacy" configuration
echo 'remote_mode="legacy"' >> /etc/libvirt/libvirt.conf
systemctl restart libvirtd
# Disable AppArmor
ln -s /etc/apparmor.d/usr.sbin.libvirtd /etc/apparmor.d/disable/ 2>/dev/null || true
ln -s /etc/apparmor.d/usr.lib.libvirt.virt-aa-helper /etc/apparmor.d/disable/ 2>/dev/null || true
apparmor_parser -R /etc/apparmor.d/usr.sbin.libvirtd 2>/dev/null || true
apparmor_parser -R /etc/apparmor.d/usr.lib.libvirt.virt-aa-helper 2>/dev/null || true
# Create the UEFI properties file
UEFI_PROPS_FILE="/etc/cloudstack/agent/uefi.properties"
if [ "$IS_UEFI" = true ] && [ "$ARCH" = "amd64" ]; then
log "Creating UEFI properties for x86_64 architecture..."
cat > "$UEFI_PROPS_FILE" << 'EOF'
# CloudStack Agent UEFI Configuration
# This file configures UEFI boot support for virtual machines
# Secure boot mode with Microsoft keys (for Windows 11, modern Linux with Secure Boot)
guest.nvram.template.secure=/usr/share/OVMF/OVMF_VARS_4M.ms.fd
guest.loader.secure=/usr/share/OVMF/OVMF_CODE_4M.ms.fd
# Secure boot mode without Microsoft keys (generic secure boot)
guest.nvram.template.secboot=/usr/share/OVMF/OVMF_VARS_4M.fd
guest.loader.secboot=/usr/share/OVMF/OVMF_CODE_4M.secboot.fd
# Legacy UEFI mode (standard UEFI without Secure Boot)
guest.nvram.template.legacy=/usr/share/OVMF/OVMF_VARS_4M.fd
guest.loader.legacy=/usr/share/OVMF/OVMF_CODE_4M.fd
# NVRAM storage path (where VM-specific UEFI variables are stored)
guest.nvram.path=/var/lib/libvirt/qemu/nvram/
EOF
elif [ "$IS_UEFI" = true ] && [ "$ARCH" = "aarch64" ]; then
log "Creating UEFI properties for ARM64 architecture..."
cat > "$UEFI_PROPS_FILE" << 'EOF'
# CloudStack Agent UEFI Configuration for ARM64
# This file configures UEFI boot support for ARM64 virtual machines
# Secure boot mode with Microsoft keys
guest.nvram.template.secure=/usr/share/AAVMF/AAVMF_VARS.ms.fd
guest.loader.secure=/usr/share/AAVMF/AAVMF_CODE.ms.fd
# Secure boot mode without Microsoft keys
guest.nvram.template.secboot=/usr/share/AAVMF/AAVMF_VARS.fd
guest.loader.secboot=/usr/share/AAVMF/AAVMF_CODE.secboot.fd
# Standard UEFI mode (default)
guest.nvram.template.legacy=/usr/share/AAVMF/AAVMF_VARS.fd
guest.loader.legacy=/usr/share/AAVMF/AAVMF_CODE.fd
# No secure boot mode (explicitly disabled)
guest.nvram.template.nosecboot=/usr/share/AAVMF/AAVMF_VARS.fd
guest.loader.nosecboot=/usr/share/AAVMF/AAVMF_CODE.no-secboot.fd
# NVRAM storage path
guest.nvram.path=/var/lib/libvirt/qemu/nvram/
EOF
else
log "Creating minimal UEFI properties for Legacy BIOS mode..."
cat > "$UEFI_PROPS_FILE" << 'EOF'
# CloudStack Agent UEFI Configuration (Legacy BIOS Mode)
# Host is running in Legacy BIOS mode - UEFI VMs not supported
# NVRAM storage path (not used in BIOS mode but kept for compatibility)
guest.nvram.path=/var/lib/libvirt/qemu/nvram/
# Note: UEFI VM creation will not be available on this host
# Only Legacy BIOS VMs are supported
EOF
fi
log "CloudStack Agent installed successfully"
}
# Start your cloudstack
launch_cloudstack(){
cloudstack-setup-management
}
# Display final information
display_final_info() {
log "CloudStack installation completed!"
echo -e "\n${YELLOW}=== Important Security Information ===${NC}"
echo "Your CloudStack keys have been saved to: /root/cloudstack_keys.txt"
echo -e "${LIGHT_CYAN}CRITICAL: Backup this file! Without these keys, you cannot recover your CloudStack installation.${NC}"
echo -e "\n${GREEN}=== Installation Summary ===${NC}"
info "CloudStack Management Server: http://${LANIP}:8080"
info "Default login: admin/password"
info "Keys file: /root/cloudstack_keys.txt"
echo -e "\n${YELLOW}=== Next Steps ===${NC}"
echo "1. Open browser and navigate to: http://${LANIP}:8080"
echo "2. Login with admin/password"
echo "3. Follow the zone setup wizard"
echo "4. Use the following settings in the wizard:"
echo " - Addresses: ${LANIP}${CIDR}"
echo " - Primary Storage: nfs://${LANIP}/export/primary"
echo " - Secondary Storage: nfs://${LANIP}/export/secondary"
echo -e "\n${GREEN}Installation completed successfully!${NC}"
}
# Troubleshooting function
fix_secondary_not_found() {
check_root
log "Fixing 'Secondary not found' issue..."
systemctl restart nfs-server.service
exportfs -a
mount -a
systemctl restart cloudstack-agent.service
log "Secondary storage issue fixed. Please restart Secondary SystemVM in CloudStack Management."
}
# Select network configuration mode
select_network_mode() {
echo -e "${BLUE}"
echo "======================================="
echo " NETWORK CONFIGURATION OPTIONS "
echo "======================================="
echo -e "${NC}"
echo ""
echo "1) Static IP Configuration (Recommended)"
echo " - Best for production environments"
echo ""
echo "2) DHCP Bridge Configuration"
echo " - Good for testing/development"
echo ""
while true; do
read -p "Select network configuration option (1-2): " choice
case $choice in
1)
NETWORK_MODE="static"
log "Selected: Static IP Configuration"
break
;;
2)
NETWORK_MODE="dhcp"
log "Selected: DHCP Bridge Configuration"
break
;;
*)
error "Invalid choice. Please select 1-2."
;;
esac
done
}
set_network_mode () {
ensure_packages net-tools bridge-utils
select_interface
# Get current netplan files
NETPLAN_FILES=($(ls /etc/netplan/*.yaml 2>/dev/null || echo))
# Backup existing netplan configurations
for file in "${NETPLAN_FILES[@]}"; do
if [ -f "$file" ]; then
cp "$file" "${file}.backup.$(date +%Y%m%d_%H%M%S)"
log "Backed up $file"
fi
done
# Remove existing configurations to avoid conflicts
rm -f /etc/netplan/*.yaml
# Create new netplan configuration
NETPLAN_CONFIG="/etc/netplan/01-network-manager-all.yaml"
if [ "$NETWORK_MODE" = "static" ]; then
collect_network_ip_info
log "Configuring network..."
cat > $NETPLAN_CONFIG << EOF
network:
version: 2
renderer: networkd
ethernets:
${NATNIC}:
addresses: [${LANIP}${CIDR}]
routes:
- to: default
via: ${GATEWAY}
nameservers:
addresses: [${DNS1}, ${DNS2}]
EOF
elif [ "$NETWORK_MODE" = "dhcp" ]; then
log "Configuring network..."
cat > $NETPLAN_CONFIG << EOF
network:
version: 2
renderer: networkd
ethernets:
${NATNIC}:
dhcp4: true
dhcp6: false
optional: true
EOF
fi
# Set correct permissions
chmod 600 $NETPLAN_CONFIG
chown root:root $NETPLAN_CONFIG
log "Network configuration created with correct permissions"
# Validate configuration
if ! netplan generate; then
error "Network configuration validation failed"
exit 1
fi
warning "Network configuration will be applied. This may temporarily disconnect your connection."
warning "Make sure you have physical access to the server!"
echo -e "\nCurrent network configuration:"
cat $NETPLAN_CONFIG
read -p "Press Enter to apply the configuration (Ctrl+C to abort)..."
# Apply configuration directly (skip try as it may not work with bridges)
if netplan apply; then
log "Network configuration applied successfully"
# Wait for network to stabilize
sleep 5
# Test connectivity
if ping -c 3 "google.com" >/dev/null 2>&1; then
log "Network connectivity verified"
else
error "Cannot ping gateway. Please check network configuration."
exit 1
fi
else
error "Failed to apply network configuration"
# Try to restore backup
if [ ${#NETPLAN_FILES[@]} -gt 0 ]; then
warning "Attempting to restore original configuration..."
rm -f $NETPLAN_CONFIG
for file in "${NETPLAN_FILES[@]}"; do
backup_file="${file}.backup.$(date +%Y%m%d)_*"
if ls $backup_file >/dev/null 2>&1; then
latest_backup=$(ls -t ${backup_file} | head -1)
cp "$latest_backup" "$file"
fi
done
netplan apply
fi
exit 1
fi
}
network_settings() {
clear
check_root
select_network_mode
set_network_mode
}
# Set root password
set_root_password() {
log "Please set the system root password first..."
while true; do
prompt_input "Enter new root password" "NEW_ROOT_PASSWORD"
prompt_input "Confirm root password" "CONFIRM_ROOT_PASSWORD"
if [ "$NEW_ROOT_PASSWORD" == "$CONFIRM_ROOT_PASSWORD" ]; then
echo "root:${NEW_ROOT_PASSWORD}" | chpasswd
log "Root password updated successfully"
break
else
warning "Passwords do not match. Please try again."
fi
done
}
# Main installation function
main() {
clear
log "Starting CloudStack 4.20.0 installation..."
check_root