Compare commits
84 Commits
move-to-do
...
main
Author | SHA1 | Date |
---|---|---|
|
0e4024353e | |
|
6983bba294 | |
|
561232ee9f | |
|
3f9265cf69 | |
|
6217bc71eb | |
|
324d47823a | |
|
7afae7db6d | |
|
59367aacaa | |
|
a038748ff5 | |
|
65a086064b | |
|
8110058303 | |
|
30506275af | |
|
4789cddbfc | |
|
1a54a31fe0 | |
|
ee6c7c2003 | |
|
36b8ee0250 | |
|
92e24117e0 | |
|
45938bbe2b | |
|
4ca2252f2d | |
|
148a78f408 | |
|
d4f0aed18d | |
|
9151520b93 | |
|
5d056feaf1 | |
|
3d355ec4a4 | |
|
3b2b5d8702 | |
|
8a634a5f71 | |
|
b717e2b7ee | |
|
c38fe4e26f | |
|
3344157d6b | |
|
22650060af | |
|
4634061f41 | |
|
20a338a0d6 | |
|
3fe3fc9a5c | |
|
105340b165 | |
|
b652142aaa | |
|
625c464ff5 | |
|
a39258f978 | |
|
8e235f526d | |
|
00a96b0a84 | |
|
ee28a98157 | |
|
69bdd32f74 | |
|
0096a5b942 | |
|
8f08e35f42 | |
|
5ef82a4d4f | |
|
f6fe1615f4 | |
|
b8f53f91e8 | |
|
77053c4142 | |
|
cbf1f0715d | |
|
3fbbb3b340 | |
|
30ae35c6f1 | |
|
9e7cc4e22d | |
|
858763c473 | |
|
237ccf6675 | |
|
b7c3523b10 | |
|
033ecb17e7 | |
|
176b0cba2e | |
|
3b00609c1e | |
|
0a0bd59f8d | |
|
4916d15736 | |
|
412334bc23 | |
|
82effbb989 | |
|
3f49623417 | |
|
c5257913c0 | |
|
6acebd38a4 | |
|
bb379b7c68 | |
|
1b6535ffee | |
|
e53fc605bb | |
|
278564d35f | |
|
a1cf2b59dd | |
|
b2305740b2 | |
|
bc024b08c1 | |
|
9450a61467 | |
|
23c28e4534 | |
|
b40fc19e55 | |
|
12ca1e75fd | |
|
520b2861fc | |
|
52e1c2f5a8 | |
|
1fbbdb657c | |
|
d6d2df9855 | |
|
50f2c60d31 | |
|
dda794f328 | |
|
a09814c113 | |
|
5a7952efe3 | |
|
8162f5b3c3 |
|
@ -0,0 +1,9 @@
|
|||
chroot
|
||||
image
|
||||
*.log
|
||||
*.tmp
|
||||
ubuntu_chroot
|
||||
create_end_squashfs.sh
|
||||
ogCore_config.json
|
||||
ogGui_config.json
|
||||
test.sh
|
|
@ -0,0 +1,3 @@
|
|||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt -y update && apt -y install debootstrap xorriso squashfs-tools grub-pc-bin grub-efi-amd64-bin mtools dosfstools
|
|
@ -0,0 +1,20 @@
|
|||
pipeline {
|
||||
agent {
|
||||
label 'ogAdministrator'
|
||||
}
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh "echo root > root_passwd"
|
||||
sh 'docker run --privileged -v /dev:/dev -v /run:/run -v $(pwd):/installer -w /installer --rm opengnsys/oginstallerbuilder ./builder.sh'
|
||||
}
|
||||
}
|
||||
stage ('Upload Image to NFS')
|
||||
{
|
||||
steps {
|
||||
sh "cp ubuntu-from-scratch.iso /mnt/srv/artefactos/oginstaller/oginstaller-${BRANCH_NAME}_${BUILD_NUMBER}.iso"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 13 KiB |
|
@ -0,0 +1,145 @@
|
|||
#!/usr/bin/bash
|
||||
|
||||
set -x
|
||||
set -e
|
||||
DIR=/ubuntu_scratch
|
||||
CHROOT_DIR=$DIR/chroot
|
||||
UBUNTU_CHROOT_DIR=$DIR/ubuntu_chroot
|
||||
DIST=noble
|
||||
VARIANT=minbase
|
||||
CLEAN=${CLEAN:-1}
|
||||
CLEAN_CHROOT=${CLEAN_CHROOT:-1}
|
||||
OUTPUT_DIR=/installer
|
||||
IMAGE_DIR=$DIR/image
|
||||
DEBOOT_STRAP_URL=http://mirror.raiolanetworks.com/ubuntu/
|
||||
|
||||
mkdir -p $IMAGE_DIR
|
||||
mkdir -p $CHROOT_DIR
|
||||
# # Copio el script chroot_setup.sh al directorio chroot
|
||||
cp chroot_setup.sh $CHROOT_DIR
|
||||
cp buildlib.sh $CHROOT_DIR
|
||||
cp root_passwd $CHROOT_DIR
|
||||
chmod +x $CHROOT_DIR/chroot_setup.sh
|
||||
|
||||
# # Deploy boot strap
|
||||
if [ $CLEAN_CHROOT -eq 1 ];
|
||||
then
|
||||
debootstrap --arch=amd64 --variant=$VARIANT $DIST $CHROOT_DIR $DEBOOT_STRAP_URL
|
||||
fi
|
||||
|
||||
mkdir -p $CHROOT_DIR/usr/share/calamares/branding/
|
||||
mkdir -p $CHROOT_DIR/etc/calamares/modules/
|
||||
cp calamares/settings.conf $CHROOT_DIR/etc/calamares/
|
||||
cp calamares/modules/*.conf $CHROOT_DIR/etc/calamares/modules/
|
||||
cp -r calamares/src/branding/opengnsys $CHROOT_DIR/usr/share/calamares/branding/
|
||||
chmod 755 $CHROOT_DIR/usr/share/calamares/branding/opengnsys
|
||||
chmod 644 $CHROOT_DIR/usr/share/calamares/branding/opengnsys/*
|
||||
chmod 755 $CHROOT_DIR/usr/share/calamares/branding/opengnsys/lang
|
||||
chmod 644 $CHROOT_DIR/usr/share/calamares/branding/opengnsys/lang/*
|
||||
mkdir -p $CHROOT_DIR/usr/share/opengnsys/images
|
||||
cp art/* $CHROOT_DIR/usr/share/opengnsys/images/
|
||||
mkdir -p $CHROOT_DIR/usr/local/bin
|
||||
cp openGnsys-installer.sh $CHROOT_DIR/usr/local/bin/
|
||||
chmod +x $CHROOT_DIR/usr/local/bin/openGnsys-installer.sh
|
||||
|
||||
|
||||
# # Create mount points
|
||||
mount --bind /dev/ $CHROOT_DIR/dev
|
||||
mount --bind /run/ $CHROOT_DIR/run
|
||||
|
||||
|
||||
# Execute chroot_setup.sh
|
||||
chroot $CHROOT_DIR /chroot_setup.sh
|
||||
cp conf/pcmanfm.conf $CHROOT_DIR/etc/xdg/pcmanfm/default/pcmanfm.conf
|
||||
|
||||
# Clean up the environment
|
||||
umount $CHROOT_DIR/dev
|
||||
umount $CHROOT_DIR/run
|
||||
|
||||
# Compress the chroot deploy
|
||||
mkdir -p $IMAGE_DIR
|
||||
mv $CHROOT_DIR/image $DIR/
|
||||
|
||||
|
||||
# Create squashfs imagesudo
|
||||
mksquashfs $CHROOT_DIR $IMAGE_DIR/casper/filesystem.squashfs \
|
||||
-noappend -no-duplicates -no-recovery \
|
||||
-wildcards \
|
||||
-comp xz -b 1M -Xdict-size 100% \
|
||||
-e "var/cache/apt/archives/*" -e "root/*" -e "root/.*" -e "tmp/*" -e "tmp/.*" -e "swapfile"
|
||||
|
||||
printf $(du -sx --block-size=1 $CHROOT_DIR | cut -f1) | tee $IMAGE_DIR/casper/filesystem.size
|
||||
|
||||
# Deboot strap real Ubuntu image
|
||||
debootstrap --arch=amd64 --variant=minbase noble $UBUNTU_CHROOT_DIR $DEBOOT_STRAP_URL
|
||||
rm -f $UBUNTU_CHROOT_DIR/etc/resolv.conf
|
||||
echo 'nameserver 8.8.8.8' >$UBUNTU_CHROOT_DIR/etc/resolv.conf
|
||||
cp setup_chroot_ubuntu.sh $UBUNTU_CHROOT_DIR
|
||||
chroot $UBUNTU_CHROOT_DIR chmod +x /setup_chroot_ubuntu.sh
|
||||
cp buildlib.sh $UBUNTU_CHROOT_DIR
|
||||
mkdir -p $UBUNTU_CHROOT_DIR/opengnsys-installer
|
||||
cp component-installer.sh $UBUNTU_CHROOT_DIR/opengnsys-installer/
|
||||
chmod +x $UBUNTU_CHROOT_DIR/opengnsys-installer/component-installer.sh
|
||||
cp provision_ogcore.sh $UBUNTU_CHROOT_DIR/opengnsys-installer/
|
||||
chmod +x $UBUNTU_CHROOT_DIR/opengnsys-installer/provision_ogcore.sh
|
||||
cp provision_oggui.sh $UBUNTU_CHROOT_DIR/opengnsys-installer/
|
||||
chmod +x $UBUNTU_CHROOT_DIR/opengnsys-installer/provision_oggui.sh
|
||||
cp pat.txt $UBUNTU_CHROOT_DIR/opengnsys-installer/
|
||||
|
||||
|
||||
# Setup the chroot for ubuntu
|
||||
mount --bind /dev/ $UBUNTU_CHROOT_DIR/dev
|
||||
mount --bind /run/ $UBUNTU_CHROOT_DIR/run
|
||||
|
||||
chroot $UBUNTU_CHROOT_DIR /setup_chroot_ubuntu.sh
|
||||
|
||||
|
||||
# Clean up the environment
|
||||
umount $UBUNTU_CHROOT_DIR/dev
|
||||
umount $UBUNTU_CHROOT_DIR/run
|
||||
|
||||
|
||||
# Compress the ububtu chroot
|
||||
mksquashfs $UBUNTU_CHROOT_DIR $IMAGE_DIR/casper/filesystem.ubuntu.sqfs \
|
||||
-noappend -no-duplicates -no-recovery \
|
||||
-wildcards \
|
||||
-comp xz -b 1M -Xdict-size 100% \
|
||||
-e "var/cache/apt/archives/*" -e "root/*" -e "root/.*" -e "tmp/*" -e "tmp/.*" -e "swapfile"
|
||||
|
||||
# Create ISO imageq
|
||||
cd $IMAGE_DIR && \
|
||||
xorriso \
|
||||
-as mkisofs \
|
||||
-iso-level 3 \
|
||||
-full-iso9660-filenames \
|
||||
-J -joliet-long \
|
||||
-volid "Ubuntu_scratch" \
|
||||
-output $OUTPUT_DIR/ubuntu-from-scratch.iso \
|
||||
-eltorito-boot isolinux/bios.img \
|
||||
-no-emul-boot \
|
||||
-boot-load-size 4 \
|
||||
-boot-info-table \
|
||||
--eltorito-catalog boot.catalog \
|
||||
--grub2-boot-info \
|
||||
--grub2-mbr ../chroot/usr/lib/grub/i386-pc/boot_hybrid.img \
|
||||
-partition_offset 16 \
|
||||
--mbr-force-bootable \
|
||||
-eltorito-alt-boot \
|
||||
-no-emul-boot \
|
||||
-e isolinux/efiboot.img \
|
||||
-append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b isolinux/efiboot.img \
|
||||
-appended_part_as_gpt \
|
||||
-iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7 \
|
||||
-m "isolinux/efiboot.img" \
|
||||
-m "isolinux/bios.img" \
|
||||
-e '--interval:appended_partition_2:::' \
|
||||
-exclude isolinux \
|
||||
-graft-points \
|
||||
"/EFI/boot/bootx64.efi=isolinux/bootx64.efi" \
|
||||
"/EFI/boot/mmx64.efi=isolinux/mmx64.efi" \
|
||||
"/EFI/boot/grubx64.efi=isolinux/grubx64.efi" \
|
||||
"/EFI/ubuntu/grub.cfg=isolinux/grub.cfg" \
|
||||
"/isolinux/bios.img=isolinux/bios.img" \
|
||||
"/isolinux/efiboot.img=isolinux/efiboot.img" \
|
||||
"."
|
||||
cd -
|
|
@ -0,0 +1,120 @@
|
|||
#!/usr/bin/bash
|
||||
|
||||
function set_root_passwd(){
|
||||
echo "root:root" | chpasswd
|
||||
}
|
||||
|
||||
function setup_sources_list () {
|
||||
echo "Setting up sources.list"
|
||||
cat <<EOF > $CHROOT_DIR/etc/apt/sources.list
|
||||
deb $DEBOOT_STRAP_URL noble main restricted universe multiverse
|
||||
deb-src $DEBOOT_STRAP_URL noble main restricted universe multiverse
|
||||
|
||||
deb $DEBOOT_STRAP_URL noble-security main restricted universe multiverse
|
||||
deb-src $DEBOOT_STRAP_URL noble-security main restricted universe multiverse
|
||||
|
||||
deb $DEBOOT_STRAP_URL noble-updates main restricted universe multiverse
|
||||
deb-src $DEBOOT_STRAP_URL noble-updates main restricted universe multiverse
|
||||
EOF
|
||||
apt update -y
|
||||
apt-get -y upgrade
|
||||
}
|
||||
|
||||
function configure_divert() {
|
||||
echo "Configuring divert"
|
||||
dbus-uuidgen > /etc/machine-id
|
||||
mkdir -p /var/lib/dbus/
|
||||
ln -fs /etc/machine-id /var/lib/dbus/machine-id
|
||||
dpkg-divert --local --rename --add /sbin/initctl
|
||||
ln -s /bin/true /sbin/initctl
|
||||
}
|
||||
|
||||
function mount_proc_sys_dev() {
|
||||
echo "Mounting proc, sys and dev"
|
||||
mount -t proc proc /proc
|
||||
mount -t sysfs sys /sys
|
||||
mount -t devpts devpts /dev/pts
|
||||
}
|
||||
|
||||
function install_packages() {
|
||||
echo "Installing packages"
|
||||
apt install -y $@
|
||||
}
|
||||
|
||||
|
||||
function install_non_interactive() {
|
||||
echo "Installing packages non interactive"
|
||||
apt install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" $@
|
||||
}
|
||||
|
||||
function install_no_recommends() {
|
||||
echo "Installing packages without recommends"
|
||||
apt install -y --no-install-recommends $@
|
||||
}
|
||||
|
||||
function setup_chroot() {
|
||||
echo "Setting up chroot"
|
||||
dbus-uuidgen > /etc/machine-id
|
||||
apt update -y
|
||||
ln -fs /etc/machine-id /var/lib/dbus/machine-id
|
||||
ln -s /bin/true /sbin/initctl
|
||||
}
|
||||
|
||||
function remove_unneeded_packages() {
|
||||
echo "Removing unneeded packages"
|
||||
apt remove -y --purge --auto-remove snapd
|
||||
}
|
||||
|
||||
function reconfigure_locales() {
|
||||
echo "Configuring locales"
|
||||
sed -i 's/# es_ES.UTF-8 UTF-8/es_ES.UTF-8 UTF-8/' /etc/locale.gen
|
||||
sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
|
||||
locale-gen
|
||||
ln -sf /usr/share/zoneinfo/Europe/Madrid /etc/localtime
|
||||
echo "Europe/Madrid" > /etc/timezone
|
||||
dpkg-reconfigure -f noninteractive tzdata
|
||||
}
|
||||
|
||||
|
||||
function reconfigure_network_manager() {
|
||||
echo "Configuring network manager"
|
||||
mkdir -p /etc/NetworkManager
|
||||
cat <<EOF > /etc/NetworkManager/NetworkManager.conf
|
||||
[main]
|
||||
plugins=keyfile
|
||||
|
||||
[keyfile]
|
||||
unmanaged-devices=none
|
||||
EOF
|
||||
|
||||
mkdir -p /etc/NetworkManager/system-connections/
|
||||
cat <<EOL > /etc/NetworkManager/system-connections/dhcp-all.nmconnection
|
||||
[connection]
|
||||
id=Cable
|
||||
type=ethernet
|
||||
autoconnect=true
|
||||
|
||||
[ipv4]
|
||||
method=auto
|
||||
EOL
|
||||
|
||||
systemctl enable NetworkManager
|
||||
}
|
||||
|
||||
function clean (){
|
||||
truncate -s 0 /etc/machine-id
|
||||
rm /sbin/initctl
|
||||
dpkg-divert --rename --remove /sbin/initctl
|
||||
apt-get clean
|
||||
rm -rf /tmp/* ~/.bash_history
|
||||
umount /proc
|
||||
umount /sys
|
||||
umount /dev/pts
|
||||
export HISTSIZE=0
|
||||
}
|
||||
|
||||
|
||||
function user_add() {
|
||||
useradd -m -s /bin/bash -G sudo og
|
||||
echo "og:og" | chpasswd
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
efiBootLoader: "grub"
|
||||
kernelSearchPath: "/boot/"
|
||||
kernelInstallPath: "/boot/"
|
||||
kernelPattern: "^vmlinuz.*"
|
||||
loaderEntries:
|
||||
- "timeout 5"
|
||||
- "console-mode keep"
|
||||
|
||||
kernelParams: [ "quiet" ]
|
||||
|
||||
grubInstall: "grub-install"
|
||||
grubMkconfig: "grub-mkconfig"
|
||||
grubCfg: "/boot/grub/grub.cfg"
|
||||
grubProbe: "grub-probe"
|
||||
efiBootMgr: "efibootmgr"
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
efiBootLoader: "grub"
|
||||
kernelSearchPath: "/boot/"
|
||||
kernelInstallPath: "/boot/"
|
||||
kernelPattern: "^vmlinuz.*"
|
||||
loaderEntries:
|
||||
- "timeout 5"
|
||||
- "console-mode keep"
|
||||
|
||||
kernelParams: [ "quiet" ]
|
||||
|
||||
grubInstall: "grub-install"
|
||||
grubMkconfig: "grub-mkconfig"
|
||||
grubCfg: "/boot/grub/grub.cfg"
|
||||
grubProbe: "grub-probe"
|
||||
efiBootMgr: "efibootmgr"
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
extraMounts:
|
||||
- device: proc
|
||||
fs: proc
|
||||
mountPoint: /proc
|
||||
- device: sys
|
||||
fs: sysfs
|
||||
mountPoint: /sys
|
||||
- device: /dev
|
||||
mountPoint: /dev
|
||||
options: [ bind ]
|
||||
- device: tmpfs
|
||||
fs: tmpfs
|
||||
mountPoint: /run
|
||||
- device: /run/dev
|
||||
mountPoint: /run/dev
|
||||
options: [ bind ]
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
extraMounts:
|
||||
- device: proc
|
||||
fs: proc
|
||||
mountPoint: /proc
|
||||
- device: sys
|
||||
fs: sysfs
|
||||
mountPoint: /sys
|
||||
- device: /dev
|
||||
mountPoint: /dev
|
||||
options: [ bind ]
|
||||
- device: tmpfs
|
||||
fs: tmpfs
|
||||
mountPoint: /run
|
||||
- device: /run/dev
|
||||
mountPoint: /run/dev
|
||||
options: [ bind ]
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
|
||||
update_db: true
|
||||
backend: apt
|
||||
operations:
|
||||
- install:
|
||||
- vim
|
||||
- git
|
||||
- isc-dhcp-client
|
||||
- jq
|
||||
- network-manager
|
||||
- openssh-server
|
||||
- systemd-resolved
|
|
@ -0,0 +1 @@
|
|||
../packages.conf
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
|
||||
dontChroot: false
|
||||
verbose: true
|
||||
script:
|
||||
- "/opengnsys-installer/component-installer.sh"
|
||||
timeout: 600
|
||||
verbose: true
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
|
||||
dontChroot: true
|
||||
verbose: true
|
||||
script:
|
||||
- mkdir -p ${ROOT}/opengnsys-installer
|
||||
- cp /tmp/opengnsys-installer-configs/* ${ROOT}/opengnsys-installer
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
|
||||
dontChroot: false
|
||||
verbose: true
|
||||
script:
|
||||
- /opengnsys-installer/component-installer.sh
|
||||
timeout: 600
|
||||
verbose: true
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
|
||||
dontChroot: true
|
||||
verbose: true
|
||||
script:
|
||||
- mkdir -p ${ROOT}/opengnsys-installer
|
||||
- cp /tmp/opengnsys-installer-configs/* ${ROOT}/opengnsys-installer
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
unpack:
|
||||
- source: "/cdrom/casper/filesystem.ubuntu.sqfs"
|
||||
sourcefs: squashfs
|
||||
destination: "/"
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
unpack:
|
||||
- source: "/home/narenas/filesystem.ubuntu.sqfs"
|
||||
sourcefs: squashfs
|
||||
destination: "/"
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
defaultGroups:
|
||||
- name: users
|
||||
system: true
|
||||
- dip:
|
||||
system: true
|
||||
- plugdev:
|
||||
system: true
|
||||
- lxd:
|
||||
system: true
|
||||
- ogadmin:
|
||||
|
||||
|
||||
sudoersGroup: ogadmin
|
||||
setRootPassword: true
|
||||
doReusePassword: true
|
||||
passwordRequirements:
|
||||
minLength: -1 # Password at least this many characters
|
||||
maxLength: -1 # Password at most this many characters
|
||||
libpwquality:
|
||||
- minlen=0
|
||||
- minclass=0
|
||||
|
||||
user:
|
||||
shell: /bin/bash
|
||||
forbidden_names: [ root ]
|
||||
home_permissions: "0700"
|
||||
|
||||
|
||||
hostname:
|
||||
location: EtcFile
|
||||
writeHostsFile: true
|
||||
template: "ogServer"
|
||||
forbidden_names: [ localhost ]
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
defaultGroups:
|
||||
- name: users
|
||||
system: true
|
||||
- dip:
|
||||
system: true
|
||||
- plugdev:
|
||||
system: true
|
||||
- lxd:
|
||||
system: true
|
||||
- ogadmin:
|
||||
|
||||
|
||||
sudoersGroup: ogadmin
|
||||
setRootPassword: true
|
||||
doReusePassword: true
|
||||
passwordRequirements:
|
||||
minLength: -1 # Password at least this many characters
|
||||
maxLength: -1 # Password at most this many characters
|
||||
libpwquality:
|
||||
- minlen=0
|
||||
- minclass=0
|
||||
|
||||
user:
|
||||
shell: /bin/bash
|
||||
forbidden_names: [ root ]
|
||||
home_permissions: "0700"
|
||||
|
||||
|
||||
hostname:
|
||||
location: EtcFile
|
||||
writeHostsFile: true
|
||||
template: "ogServer"
|
||||
forbidden_names: [ localhost ]
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
|
||||
modules-search:
|
||||
- local
|
||||
- /usr/lib/x86_64-linux-gnu/calamares/modules
|
||||
|
||||
instances:
|
||||
- id: shell_nonchroot
|
||||
module: shellprocess
|
||||
config: shell_nonchroot.conf
|
||||
- id: shell_chroot
|
||||
module: shellprocess
|
||||
config: shell_chroot.conf
|
||||
|
||||
sequence:
|
||||
- show:
|
||||
- welcome
|
||||
- locale
|
||||
- keyboard
|
||||
- partition
|
||||
- users
|
||||
- summary
|
||||
- exec:
|
||||
- partition
|
||||
- mount
|
||||
- unpackfs
|
||||
- fstab
|
||||
- packages
|
||||
- machineid
|
||||
- locale
|
||||
- keyboard
|
||||
- users
|
||||
- hwclock
|
||||
- shellprocess@shell_nonchroot
|
||||
- shellprocess@shell_chroot
|
||||
- networkcfg
|
||||
- initramfs
|
||||
- bootloader
|
||||
- umount
|
||||
- show:
|
||||
- finished
|
||||
|
||||
|
||||
branding: opengnsys
|
||||
prompt-install: false
|
||||
dont-chroot: false
|
||||
oem-setup: false
|
||||
disable-cancel: false
|
||||
hide-back-and-next-during-exec: false
|
||||
disable-cancel-during-exec: false
|
||||
quit-at-end: false
|
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 4.1 KiB |
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
|
@ -0,0 +1,239 @@
|
|||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# Product branding information. This influences some global
|
||||
# user-visible aspects of Calamares, such as the product
|
||||
# name, window behavior, and the slideshow during installation.
|
||||
#
|
||||
# Additional styling can be done using the stylesheet.qss
|
||||
# file, also in the branding directory.
|
||||
---
|
||||
componentName: opengnsys
|
||||
|
||||
|
||||
### WELCOME / OVERALL WORDING
|
||||
#
|
||||
# These settings affect some overall phrasing and looks,
|
||||
# which are most visible in the welcome page.
|
||||
|
||||
# This selects between different welcome texts. When false, uses
|
||||
# the traditional "Welcome to the %1 installer.", and when true,
|
||||
# uses "Welcome to the Calamares installer for %1." This allows
|
||||
# to distinguish this installer from other installers for the
|
||||
# same distribution.
|
||||
welcomeStyleCalamares: false
|
||||
|
||||
# Should the welcome image (productWelcome, below) be scaled
|
||||
# up beyond its natural size? If false, the image does not grow
|
||||
# with the window but remains the same size throughout (this
|
||||
# may have surprising effects on HiDPI monitors).
|
||||
welcomeExpandingLogo: true
|
||||
|
||||
### WINDOW CONFIGURATION
|
||||
#
|
||||
# The settings here affect the placement of the Calamares
|
||||
# window through hints to the window manager and initial
|
||||
# sizing of the Calamares window.
|
||||
|
||||
# Size and expansion policy for Calamares.
|
||||
# - "normal" or unset, expand as needed, use *windowSize*
|
||||
# - "fullscreen", start as large as possible, ignore *windowSize*
|
||||
# - "noexpand", don't expand automatically, use *windowSize*
|
||||
windowExpanding: normal
|
||||
|
||||
# Size of Calamares window, expressed as w,h. Both w and h
|
||||
# may be either pixels (suffix px) or font-units (suffix em).
|
||||
# e.g. "800px,600px"
|
||||
# "60em,480px"
|
||||
# This setting is ignored if "fullscreen" is selected for
|
||||
# *windowExpanding*, above. If not set, use constants defined
|
||||
# in CalamaresUtilsGui, 800x520.
|
||||
windowSize: 800px,520px
|
||||
|
||||
# Placement of Calamares window. Either "center" or "free".
|
||||
# Whether "center" actually works does depend on the window
|
||||
# manager in use (and only makes sense if you're not using
|
||||
# *windowExpanding* set to "fullscreen").
|
||||
windowPlacement: center
|
||||
|
||||
### PANELS CONFIGURATION
|
||||
#
|
||||
# Calamares has a main content area, and two panels (navigation
|
||||
# and progress / sidebar). The panels can be controlled individually,
|
||||
# or switched off. If both panels are switched off, the layout of
|
||||
# the main content area loses its margins, on the assumption that
|
||||
# you're doing something special.
|
||||
|
||||
# Kind of sidebar (panel on the left, showing progress).
|
||||
# - "widget" or unset, use traditional sidebar (logo, items)
|
||||
# - "none", hide it entirely
|
||||
# - "qml", use calamares-sidebar.qml from branding folder
|
||||
# In addition, you **may** specify a side, separated by a comma,
|
||||
# from the kind. Valid sides are:
|
||||
# - "left" (if not specified, uses this)
|
||||
# - "right"
|
||||
# - "top"
|
||||
# - "bottom"
|
||||
# For instance, "widget,right" is valid; so is "qml", which defaults
|
||||
# to putting the sidebar on the left. Also valid is "qml,top".
|
||||
# While "widget,top" is valid, the widgets code is **not** flexible
|
||||
# and results will be terrible.
|
||||
sidebar: widget
|
||||
|
||||
# Kind of navigation (button panel on the bottom).
|
||||
# - "widget" or unset, use traditional navigation
|
||||
# - "none", hide it entirely
|
||||
# - "qml", use calamares-navigation.qml from branding folder
|
||||
# In addition, you **may** specify a side, separated by a comma,
|
||||
# from the kind. The same sides are valid as for *sidebar*,
|
||||
# except the default is *bottom*.
|
||||
navigation: widget
|
||||
|
||||
|
||||
### STRINGS, IMAGES AND COLORS
|
||||
#
|
||||
# This section contains the "branding proper" of names
|
||||
# and images, rather than global-look settings.
|
||||
|
||||
# These are strings shown to the user in the user interface.
|
||||
# There is no provision for translating them -- since they
|
||||
# are names, the string is included as-is.
|
||||
#
|
||||
# The four Url strings are the Urls used by the buttons in
|
||||
# the welcome screen, and are not shown to the user. Clicking
|
||||
# on the "Support" button, for instance, opens the link supportUrl.
|
||||
# If a Url is empty, the corresponding button is not shown.
|
||||
#
|
||||
# bootloaderEntryName is how this installation / distro is named
|
||||
# in the boot loader (e.g. in the GRUB menu).
|
||||
#
|
||||
# These strings support substitution from /etc/os-release
|
||||
# if KDE Frameworks 5.58 are available at build-time. When
|
||||
# enabled, ${varname} is replaced by the equivalent value
|
||||
# from os-release. All the supported var-names are in all-caps,
|
||||
# and are listed on the FreeDesktop.org site,
|
||||
# https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||
# Note that ANSI_COLOR and CPE_NAME don't make sense here, and
|
||||
# are not supported (the rest are). Remember to quote the string
|
||||
# if it contains substitutions, or you'll get YAML exceptions.
|
||||
#
|
||||
# The *Url* entries are used on the welcome page, and they
|
||||
# are visible as buttons there if the corresponding *show* keys
|
||||
# are set to "true" (they can also be overridden).
|
||||
strings:
|
||||
productName: "${NAME}"
|
||||
shortProductName: Generic
|
||||
version: 2023.3 LTS
|
||||
shortVersion: 2023.3
|
||||
versionedName: Fancy GNU/Linux 2023.3 LTS "Venomous Vole"
|
||||
shortVersionedName: FancyGL 2023.3
|
||||
bootloaderEntryName: FancyGL
|
||||
productUrl: https://calamares.io/
|
||||
supportUrl: https://github.com/calamares/calamares/wiki
|
||||
knownIssuesUrl: https://github.com/calamares/calamares/issues
|
||||
releaseNotesUrl: https://calamares.io/news/
|
||||
donateUrl: https://kde.org/community/donations/index.php
|
||||
|
||||
# These images are loaded from the branding module directory.
|
||||
#
|
||||
# productBanner is an optional image, which if present, will be shown
|
||||
# on the welcome page of the application, above the welcome text.
|
||||
# It is intended to have a width much greater than height.
|
||||
# It is displayed at 64px height (also on HiDPI).
|
||||
# Recommended size is 64px tall, and up to 460px wide.
|
||||
# productIcon is used as the window icon, and will (usually) be used
|
||||
# by the window manager to represent the application. This image
|
||||
# should be square, and may be displayed by the window manager
|
||||
# as small as 16x16 (but possibly larger).
|
||||
# productLogo is used as the logo at the top of the left-hand column
|
||||
# which shows the steps to be taken. The image should be square,
|
||||
# and is displayed at 80x80 pixels (also on HiDPI).
|
||||
# productWallpaper is an optional image, which if present, will replace
|
||||
# the normal solid background on every page of the application.
|
||||
# It can be any size and proportion,
|
||||
# and will be tiled to fit the entire window.
|
||||
# For a non-tiled wallpaper, the size should be the same as
|
||||
# the overall window, see *windowSize* above (800x520).
|
||||
# productWelcome is shown on the welcome page of the application in
|
||||
# the middle of the window, below the welcome text. It can be
|
||||
# any size and proportion, and will be scaled to fit inside
|
||||
# the window. Use `welcomeExpandingLogo` to make it non-scaled.
|
||||
# Recommended size is 320x150.
|
||||
#
|
||||
# These filenames can also use substitutions from os-release (see above).
|
||||
images:
|
||||
# productBanner: "banner.png"
|
||||
productIcon: "squid.png"
|
||||
productLogo: "squid.png"
|
||||
# productWallpaper: "wallpaper.png"
|
||||
productWelcome: "languages.png"
|
||||
|
||||
# Colors for text and background components.
|
||||
#
|
||||
# - SidebarBackground is the background of the sidebar
|
||||
# - SidebarText is the (foreground) text color
|
||||
# - SidebarBackgroundCurrent sets the background of the current step.
|
||||
# Optional, and defaults to the application palette.
|
||||
# - SidebarTextCurrent is the text color of the current step.
|
||||
#
|
||||
# These colors can **also** be set through the stylesheet, if the
|
||||
# branding component also ships a stylesheet.qss. Then they are
|
||||
# the corresponding CSS attributes of #sidebarApp.
|
||||
style:
|
||||
SidebarBackground: "#292F34"
|
||||
SidebarText: "#FFFFFF"
|
||||
SidebarTextCurrent: "#292F34"
|
||||
SidebarBackgroundCurrent: "#D35400"
|
||||
|
||||
### SLIDESHOW
|
||||
#
|
||||
# The slideshow is displayed during execution steps (e.g. when the
|
||||
# installer is actually writing to disk and doing other slow things).
|
||||
|
||||
# The slideshow can be a QML file (recommended) which can display
|
||||
# arbitrary things -- text, images, animations, or even play a game --
|
||||
# during the execution step. The QML **is** abruptly stopped when the
|
||||
# execution step is done, though, so maybe a game isn't a great idea.
|
||||
#
|
||||
# The slideshow can also be a sequence of images (not recommended unless
|
||||
# you don't want QML at all in your Calamares). The images are displayed
|
||||
# at a rate of 1 every 2 seconds during the execution step.
|
||||
#
|
||||
# To configure a QML file, list a single filename:
|
||||
# slideshow: "show.qml"
|
||||
# To configure images, like the filenames (here, as an inline list):
|
||||
# slideshow: [ "/etc/calamares/slideshow/0.png", "/etc/logo.png" ]
|
||||
slideshow: "show.qml"
|
||||
|
||||
# There are two available APIs for a QML slideshow:
|
||||
# - 1 (the default) loads the entire slideshow when the installation-
|
||||
# slideshow page is shown and starts the QML then. The QML
|
||||
# is never stopped (after installation is done, times etc.
|
||||
# continue to fire).
|
||||
# - 2 loads the slideshow on startup and calls onActivate() and
|
||||
# onLeave() in the root object. After the installation is done,
|
||||
# the show is stopped (first by calling onLeave(), then destroying
|
||||
# the QML components).
|
||||
#
|
||||
# An image slideshow does not need to have the API defined.
|
||||
slideshowAPI: 2
|
||||
|
||||
|
||||
# These options are to customize online uploading of logs to pastebins:
|
||||
# - type : Defines the kind of pastebin service to be used. Currently
|
||||
# it accepts two values:
|
||||
# - none : disables the pastebin functionality
|
||||
# - fiche : use fiche pastebin server
|
||||
# - url : Defines the address of pastebin service to be used.
|
||||
# Takes string as input. Important bits are the host and port,
|
||||
# the scheme is not used.
|
||||
# - sizeLimit : Defines maximum size limit (in KiB) of log file to be pasted.
|
||||
# The option must be set, to have the log option work.
|
||||
# Takes integer as input. If < 0, no limit will be forced,
|
||||
# else only last (approximately) 'n' KiB of log file will be pasted.
|
||||
# Please note that upload size may be slightly over the limit (due
|
||||
# to last minute logging), so provide a suitable value.
|
||||
uploadServer :
|
||||
type : "fiche"
|
||||
url : "http://termbin.com:9999"
|
||||
sizeLimit : -1
|
After Width: | Height: | Size: 55 KiB |
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: 2015 Teo Mrnjavac <teo@kde.org>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
After Width: | Height: | Size: 54 KiB |
|
@ -0,0 +1,73 @@
|
|||
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2015 Teo Mrnjavac <teo@kde.org>
|
||||
* SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
*
|
||||
*/
|
||||
|
||||
import QtQuick 2.0;
|
||||
import calamares.slideshow 1.0;
|
||||
|
||||
Presentation
|
||||
{
|
||||
id: presentation
|
||||
|
||||
function nextSlide() {
|
||||
console.log("QML Component (default slideshow) Next slide");
|
||||
presentation.goToNextSlide();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: advanceTimer
|
||||
interval: 1000
|
||||
running: presentation.activatedInCalamares
|
||||
repeat: true
|
||||
onTriggered: nextSlide()
|
||||
}
|
||||
|
||||
Slide {
|
||||
|
||||
Image {
|
||||
id: background
|
||||
source: "Opengnsys-wallpaper.jpg"
|
||||
width: 200; height: 200
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
Text {
|
||||
anchors.horizontalCenter: background.horizontalCenter
|
||||
anchors.top: background.bottom
|
||||
text: "This is a customizable QML slideshow for Opengnsys project.<br/>"
|
||||
wrapMode: Text.WordWrap
|
||||
width: presentation.width
|
||||
horizontalAlignment: Text.Center
|
||||
}
|
||||
}
|
||||
|
||||
Slide {
|
||||
centeredText: qsTr("Opengnsys is and incredible project.")
|
||||
}
|
||||
|
||||
Slide {
|
||||
centeredText: qsTr("Look at the marvelous of Opengnsys Project.")
|
||||
}
|
||||
|
||||
// When this slideshow is loaded as a V1 slideshow, only
|
||||
// activatedInCalamares is set, which starts the timer (see above).
|
||||
//
|
||||
// In V2, also the onActivate() and onLeave() methods are called.
|
||||
// These example functions log a message (and re-start the slides
|
||||
// from the first).
|
||||
function onActivate() {
|
||||
console.log("QML Component (default slideshow) activated");
|
||||
presentation.currentSlide = 0;
|
||||
}
|
||||
|
||||
function onLeave() {
|
||||
console.log("QML Component (default slideshow) deactivated");
|
||||
}
|
||||
|
||||
}
|
After Width: | Height: | Size: 6.7 KiB |
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: no
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
/*
|
||||
A branding component can ship a stylesheet (like this one)
|
||||
which is applied to parts of the Calamares user-interface.
|
||||
In principle, all parts can be styled through CSS.
|
||||
Missing parts should be filed as issues.
|
||||
|
||||
The IDs are based on the object names in the C++ code.
|
||||
You can use the Debug Dialog to find out object names:
|
||||
- Open the debug dialog
|
||||
- Choose tab *Tools*
|
||||
- Click *Widget Tree* button
|
||||
The list of object names is printed in the log.
|
||||
|
||||
Documentation for styling Qt Widgets through a stylesheet
|
||||
can be found at
|
||||
https://doc.qt.io/qt-5/stylesheet-examples.html
|
||||
https://doc.qt.io/qt-5/stylesheet-reference.html
|
||||
In Calamares, styling widget classes is supported (e.g.
|
||||
using `QComboBox` as a selector).
|
||||
|
||||
This example stylesheet has all the actual styling commented out.
|
||||
The examples are not exhaustive.
|
||||
|
||||
*/
|
||||
|
||||
/*** Generic Widgets.
|
||||
*
|
||||
* You can style **all** widgets of a given class by selecting
|
||||
* the class name. Some widgets have specialized sub-selectors.
|
||||
*/
|
||||
|
||||
/*
|
||||
QPushButton { background-color: green; }
|
||||
*/
|
||||
|
||||
/*** Main application window.
|
||||
*
|
||||
* The main application window has the sidebar, which in turn
|
||||
* contains a logo and a list of items -- note that the list
|
||||
* can **not** be styled, since it has its own custom C++
|
||||
* delegate code.
|
||||
*/
|
||||
|
||||
/*
|
||||
#mainApp { }
|
||||
#sidebarApp { }
|
||||
#logoApp { }
|
||||
*/
|
||||
|
||||
/*** Welcome module.
|
||||
*
|
||||
* There are plenty of parts, but the buttons are the most interesting
|
||||
* ones (donate, release notes, ...). The little icon image can be
|
||||
* styled through *qproperty-icon*, which is a little obscure.
|
||||
* URLs can reference the QRC paths of the Calamares application
|
||||
* or loaded via plugins or within the filesystem. There is no
|
||||
* comprehensive list of available icons, though.
|
||||
*/
|
||||
|
||||
/*
|
||||
QPushButton#aboutButton { qproperty-icon: url(:/data/images/release.svg); }
|
||||
#donateButton,
|
||||
#supportButton,
|
||||
#releaseNotesButton,
|
||||
#knownIssuesButton { qproperty-icon: url(:/data/images/help.svg); }
|
||||
*/
|
||||
|
||||
/*** Partitioning module.
|
||||
*
|
||||
* Many moving parts, which you will need to experiment with.
|
||||
*/
|
||||
|
||||
/*
|
||||
#bootInfoIcon { }
|
||||
#bootInfoLable { }
|
||||
#deviceInfoIcon { }
|
||||
#defineInfoLabel { }
|
||||
#scrollAreaWidgetContents { }
|
||||
#partitionBarView { }
|
||||
*/
|
||||
|
||||
/*** Licensing module.
|
||||
*
|
||||
* The licensing module paints individual widgets for each of
|
||||
* the licenses. The item can be collapsed or expanded.
|
||||
*/
|
||||
|
||||
/*
|
||||
#licenseItem { }
|
||||
#licenseItemFullText { }
|
||||
*/
|
|
@ -0,0 +1 @@
|
|||
../modules/
|
|
@ -0,0 +1,236 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
set -e
|
||||
export HOME=/root
|
||||
export LC_ALL=C
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
export DEBOOT_STRAP_URL=http://mirror.raiolanetworks.com/ubuntu/
|
||||
|
||||
source buildlib.sh
|
||||
|
||||
function configure_sddm_and_openbox() {
|
||||
# Allow root login
|
||||
sed -ie 's/^\(.*root quiet_success\)$/#\1/' /etc/pam.d/sddm
|
||||
|
||||
# Start pcman for the Desktop
|
||||
|
||||
#mkdir -p /root/.config/autostart/
|
||||
# cat <<EOF > /root/.config/autostart/pcmanfm.desktop
|
||||
mkdir -p /etc/xdg/openbox/
|
||||
cat <<EOF > /etc/xdg/openbox/autostart
|
||||
pcmanfm --desktop &
|
||||
EOF
|
||||
|
||||
# Create desktop launcher for calamares
|
||||
# mkdir -p /root/Desktop
|
||||
# cat <<EOF > /root/Desktop/opengnsys-installer.desktop
|
||||
mkdir -p /etc/skel/Desktop
|
||||
cat <<EOF > /etc/skel/Desktop/opengnsys-installer.desktop
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Install OpenGnsys
|
||||
Comment=Este es un acceso directo para ejecutar un script como root
|
||||
Exec=/usr/local/bin/openGnsys-installer.sh
|
||||
Icon=/usr/share/opengnsys/images/logo_ogw.png
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;
|
||||
|
||||
EOF
|
||||
# chmod +x /root/Desktop/opengnsys-installer.desktop
|
||||
chmod +x /etc/skel/Desktop/opengnsys-installer.desktop
|
||||
|
||||
# Configure ssdm autologin
|
||||
mkdir -p /etc/sddm.conf.d/
|
||||
cat <<EOF > /etc/sddm.conf.d/autologin.conf
|
||||
[Autologin]
|
||||
User=og
|
||||
Session=openbox
|
||||
EOF
|
||||
|
||||
}
|
||||
|
||||
function set_pkg_configuration() {
|
||||
echo "keyboard-configuration keyboard-configuration/layout select Spanish" | debconf-set-selections
|
||||
echo "keyboard-configuration keyboard-configuration/variant select Spanish" | debconf-set-selections
|
||||
echo "keyboard-configuration keyboard-configuration/xkb-keymap select es" | debconf-set-selections
|
||||
}
|
||||
|
||||
function reconfigure_packages() {
|
||||
reconfigure_locales
|
||||
reconfigure_network_manager
|
||||
}
|
||||
|
||||
function create_image() {
|
||||
mkdir -p /image/{casper,isolinux,install}
|
||||
cp /boot/vmlinuz-**-**-generic /image/casper/vmlinuz
|
||||
cp /boot/initrd.img-**-**-generic /image/casper/initrd
|
||||
wget --progress=dot https://memtest.org/download/v7.00/mt86plus_7.00.binaries.zip -O /image/install/memtest86.zip
|
||||
unzip -p /image/install/memtest86.zip memtest64.bin > /image/install/memtest86+.bin
|
||||
unzip -p /image/install/memtest86.zip memtest64.efi > /image/install/memtest86+.efi
|
||||
rm -f /image/install/memtest86.zip
|
||||
}
|
||||
|
||||
|
||||
function configure_grub_menu() {
|
||||
touch /image/ubuntu
|
||||
cat <<EOF > /image/isolinux/grub.cfg
|
||||
|
||||
search --set=root --file /ubuntu
|
||||
|
||||
insmod all_video
|
||||
|
||||
set default="0"
|
||||
set timeout=30
|
||||
|
||||
menuentry "Install Ubuntu FS" {
|
||||
linux /casper/vmlinuz boot=casper quiet splash ---
|
||||
initrd /casper/initrd
|
||||
}
|
||||
|
||||
menuentry "Check disc for defects" {
|
||||
linux /casper/vmlinuz boot=casper integrity-check quiet splash ---
|
||||
initrd /casper/initrd
|
||||
}
|
||||
|
||||
grub_platform
|
||||
if [ "\$grub_platform" = "efi" ]; then
|
||||
menuentry 'UEFI Firmware Settings' {
|
||||
fwsetup
|
||||
}
|
||||
|
||||
menuentry "Test memory Memtest86+ (UEFI)" {
|
||||
linux /install/memtest86+.efi
|
||||
}
|
||||
else
|
||||
menuentry "Test memory Memtest86+ (BIOS)" {
|
||||
linux16 /install/memtest86+.bin
|
||||
}
|
||||
fi
|
||||
EOF
|
||||
# Generate manifest
|
||||
dpkg-query -W --showformat='${Package} ${Version}\n' | sudo tee /image/casper/filesystem.manifest
|
||||
cp -v /image/casper/filesystem.manifest /image/casper/filesystem.manifest-desktop
|
||||
sed -i '/ubiquity/d' /image/casper/filesystem.manifest-desktop
|
||||
sed -i '/casper/d' /image/casper/filesystem.manifest-desktop
|
||||
sed -i '/discover/d' /image/casper/filesystem.manifest-desktop
|
||||
sed -i '/laptop-detect/d' /image/casper/filesystem.manifest-desktop
|
||||
sed -i '/os-prober/d' /image/casper/filesystem.manifest-desktop
|
||||
|
||||
# Create diskdefines
|
||||
cat <<EOF > /image/README.diskdefines
|
||||
#define DISKNAME Ubuntu from scratch
|
||||
#define TYPE binary
|
||||
#define TYPEbinary 1
|
||||
#define ARCH amd64
|
||||
#define ARCHamd64 1
|
||||
#define DISKNUM 1
|
||||
#define DISKNUM1 1
|
||||
#define TOTALNUM 0
|
||||
#define TOTALNUM0 1
|
||||
EOF
|
||||
|
||||
}
|
||||
|
||||
function create_image2(){
|
||||
cp /usr/lib/shim/shimx64.efi.signed.previous /image/isolinux/bootx64.efi
|
||||
cp /usr/lib/shim/mmx64.efi /image/isolinux/mmx64.efi
|
||||
cp /usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed /image/isolinux/grubx64.efi
|
||||
cd /image/isolinux && \
|
||||
dd if=/dev/zero of=efiboot.img bs=1M count=10 && \
|
||||
mkfs.vfat -F 16 efiboot.img && \
|
||||
LC_CTYPE=C mmd -i efiboot.img efi efi/ubuntu efi/boot && \
|
||||
LC_CTYPE=C mcopy -i efiboot.img ./bootx64.efi ::efi/boot/bootx64.efi && \
|
||||
LC_CTYPE=C mcopy -i efiboot.img ./mmx64.efi ::efi/boot/mmx64.efi && \
|
||||
LC_CTYPE=C mcopy -i efiboot.img ./grubx64.efi ::efi/boot/grubx64.efi && \
|
||||
LC_CTYPE=C mcopy -i efiboot.img ./grub.cfg ::efi/ubuntu/grub.cfg && \
|
||||
# Create a grub bios images
|
||||
cd /image && \
|
||||
grub-mkstandalone \
|
||||
--format=i386-pc \
|
||||
--output=isolinux/core.img \
|
||||
--install-modules="linux16 linux normal iso9660 biosdisk memdisk search tar ls" \
|
||||
--modules="linux16 linux normal iso9660 biosdisk search" \
|
||||
--locales="" \
|
||||
--fonts="" \
|
||||
"boot/grub/grub.cfg=isolinux/grub.cfg"
|
||||
|
||||
cat /usr/lib/grub/i386-pc/cdboot.img isolinux/core.img > isolinux/bios.img
|
||||
# find . -type f -print0 | xargs -0 md5sum | grep -v -e 'isolinux' > md5sum.txt
|
||||
|
||||
}
|
||||
|
||||
function install_mesa_repo() {
|
||||
add-apt-repository ppa:kisak/kisak-mesa
|
||||
apt -y update
|
||||
}
|
||||
|
||||
|
||||
function user_add() {
|
||||
useradd -m -k /etc/skel -s /bin/bash -G sudo og
|
||||
echo "og:og" | chpasswd
|
||||
}
|
||||
|
||||
function set_root_passwd() {
|
||||
ROOT_PASSWORD=$(cat /root_passwd)
|
||||
echo "root:$ROOT_PASSWORD" | chpasswd
|
||||
}
|
||||
|
||||
function add_og_to_sudoers() {
|
||||
echo "Adding og to sudoers"
|
||||
mkdir -p /etc/sudoers.d/
|
||||
echo "og ALL=(ALL) NOPASSWD: /usr/bin/calamares" > /etc/sudoers.d/og
|
||||
chmod 440 /etc/sudoers.d/og
|
||||
}
|
||||
|
||||
function add_auditd_rules() {
|
||||
echo "Adding auditd rules"
|
||||
cat << EOF > /etc/audit/rules.d/audit.rules
|
||||
-w /etc/sddm.conf -p wa -k sddm_watch
|
||||
EOF
|
||||
}
|
||||
|
||||
function delete_sddm_conf() {
|
||||
cat <<EOF > /etc/systemd/system/delete-sddm-conf.service
|
||||
[Unit]
|
||||
Description=Eliminar /etc/sddm.conf antes de iniciar SDDM
|
||||
Before=sddm.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/bin/rm -f /etc/sddm.conf
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable delete-sddm-conf
|
||||
|
||||
}
|
||||
|
||||
## Main
|
||||
echo "ubuntu-fs-live" > /etc/hostname
|
||||
mount_proc_sys_dev
|
||||
setup_sources_list
|
||||
install_packages libterm-readline-gnu-perl systemd-sysv software-properties-common
|
||||
setup_chroot
|
||||
set_pkg_configuration
|
||||
install_mesa_repo
|
||||
install_non_interactive
|
||||
install_packages sudo ubuntu-standard sudo casper dbus-bin mesa-utils mesa-vulkan-drivers discover isc-dhcp-client laptop-detect unzip os-prober network-manager net-tools wireless-tools wpagui locales grub-common grub-gfxpayload-lists grub-pc grub-pc-bin grub2-common grub-efi-amd64-signed shim-signed mtools binutils virtualbox-guest-utils virtualbox-guest-x11 python3-xdg
|
||||
install_non_interactive sddm openbox plymouth calamares terminator pcmanfm yad qml-module*
|
||||
install_no_recommends linux-image-generic auditd
|
||||
set_root_passwd
|
||||
configure_divert
|
||||
remove_unneeded_packages
|
||||
reconfigure_packages
|
||||
configure_sddm_and_openbox
|
||||
user_add
|
||||
add_og_to_sudoers
|
||||
add_auditd_rules
|
||||
delete_sddm_conf
|
||||
create_image
|
||||
configure_grub_menu
|
||||
create_image2
|
||||
clean
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
[config]
|
||||
bm_open_method=0
|
||||
|
||||
[volume]
|
||||
mount_on_startup=1
|
||||
mount_removable=1
|
||||
autorun=1
|
||||
|
||||
[desktop]
|
||||
wallpaper_mode=fit
|
||||
wallpaper=/usr/share/opengnsys/images/Opengnsys-wallpaper.jpg
|
||||
desktop_bg=#000000
|
||||
desktop_fg=#ffffff
|
||||
desktop_shadow=#000000
|
||||
show_wm_menu=0
|
||||
|
||||
|
||||
[ui]
|
||||
win_width=640
|
||||
win_height=480
|
||||
splitter_pos=150
|
||||
side_pane_mode=1
|
||||
view_mode=0
|
||||
show_hidden=0
|
||||
sort_type=0
|
||||
sort_by=2
|
||||
max_tab_chars=32
|
|
@ -0,0 +1 @@
|
|||
62d16790e5fd693e3b1cd21a0fb4fe11ab9851bb
|
|
@ -0,0 +1,50 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
set -x
|
||||
|
||||
cd /opt/opengnsys/ogCore/repo/
|
||||
|
||||
# Preparar el fichero .yaml
|
||||
CONF_DIR=/opt/opengnsys/ogCore/etc/
|
||||
mkdir -p $CONF_DIR
|
||||
|
||||
# Copiar el fichero de configuración a CONF_DIR
|
||||
cp docker-compose-deploy.yml $CONF_DIR/
|
||||
|
||||
if [ -f /opt/opengnsys/ogCore/installer/.deployed ]; then
|
||||
echo "ogCore ya instalado"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
while ! docker compose -f $CONF_DIR/docker-compose-deploy.yml ps --format json |jq -r '"\(.Name) \(.State)"' |grep -q 'ogcore-php running'; do
|
||||
sleep 2
|
||||
done
|
||||
|
||||
adminuser=$(jq -r '.username' /opt/opengnsys/ogCore/installer/config.json)
|
||||
adminpass=$(jq -r '.password' /opt/opengnsys/ogCore/installer/config.json)
|
||||
|
||||
docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php composer install
|
||||
docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php php bin/console lexik:jwt:generate-keypair --overwrite
|
||||
docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php php bin/console doctrine:migrations:migrate --no-interaction
|
||||
## TODO we need to feed $adminuser and $adminpass to doctrine:fixtures:load somehow
|
||||
docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php php bin/console doctrine:fixtures:load --no-interaction
|
||||
|
||||
|
||||
# Provision user admin
|
||||
bearer=$(curl -k -X 'POST' 'https://localhost:8443/auth/login' -H 'accept: application/json' -H 'Content-Type: application/json' -d "{ \"username\": \"ogadmin\", \"password\": \"12345678\" }" | jq .token | sed 's/"//g' )
|
||||
|
||||
|
||||
if [ $adminuser == "ogadmin" ]; then
|
||||
echo "Cambiando password a ogadmin no puede ser el usuario administrador"
|
||||
ogadmin_uuid=$(curl -q -k -L https://localhost:8443/users/?username=ogadmin -H 'accept: application/json' -H "Authorization: Bearer $bearer" | jq .[0].uuid | sed 's/"//g')
|
||||
curl -k -L -X PUT "https://localhost:8443/users/$ogadmin_uuid/reset-password" -H 'accept: application/ld+json' -H 'Content-Type: application/ld+json' -d "{\"currentPassword\": \"12345678\", \"newPassword\": \"$adminpass\", \"repeatNewPassword\": \"$adminpass\"}" -H "Authorization: Bearer $bearer"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
curl -k -L --location 'https://localhost:8443/users' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "Authorization: Bearer $bearer" \
|
||||
--data "{ \"username\": \"$adminuser\", \"password\": \"$adminpass\", \"roles\": [\"ROLE_SUPER_ADMIN\"] }"
|
||||
|
||||
touch /opt/opengnsys/ogCore/installer/.deployed
|
||||
exit 0
|
|
@ -0,0 +1,42 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
set -x
|
||||
|
||||
# preparar el fichero .env
|
||||
ENV_DIR=/opt/opengnsys/ogGui/etc/
|
||||
ENV_FILE=$ENV_DIR/.env
|
||||
mkdir -p $ENV_DIR
|
||||
|
||||
# Comprobar si ya se ha instalado ogCore
|
||||
#if [ -f /opt/opengnsys/ogGui/installer/.deployed ]; then
|
||||
# echo "ogCore ya instalado"
|
||||
# exit 0
|
||||
#fi
|
||||
|
||||
# Sacar la IP del ogCore de la configuración
|
||||
ogcore_ip=$(jq -r '.ogcore_ip' /opt/opengnsys/ogGui/installer/config.json)
|
||||
export OGCORE_IP="$ogcore_ip"
|
||||
|
||||
# Si no se ha configurado la IP del ogCore, se intenta obtener de la interfaz de red
|
||||
if [ -z "$ogcore_ip" ]; then
|
||||
# Obtiene el nombre del interfaz asociado a la ruta por defecto
|
||||
interface=$(ip route | grep default | awk '{print $5}')
|
||||
|
||||
# Si se encuentra el interfaz, obtiene su dirección IP
|
||||
if [ -n "$interface" ]; then
|
||||
ip_address=$(ip -o -4 addr show "$interface" | awk '{print $4}' | cut -d'/' -f1)
|
||||
ogcore_ip=$ip_address
|
||||
# Si no se ha configurado la IP del ogCore, se escribe en el fichero .env
|
||||
echo "NG_APP_BASE_API_URL=https://$ogcore_ip:8443" > $ENV_FILE
|
||||
exit 0
|
||||
else
|
||||
echo "No se pudo determinar el interfaz asociado a la ruta por defecto."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# Si se ha configurado la IP del ogCore, se escribe en el fichero .env
|
||||
echo "NG_APP_BASE_API_URL=$OGCORE_IP" > $ENV_FILE
|
||||
|
||||
touch /opt/opengnsys/ogGui/installer/.deployed
|
|
@ -0,0 +1 @@
|
|||
root
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/bash
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
set -e
|
||||
export HOME=/root
|
||||
export LC_ALL=C
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
export DEBOOT_STRAP_URL=http://mirror.raiolanetworks.com/ubuntu/
|
||||
|
||||
source buildlib.sh
|
||||
|
||||
## MAIN
|
||||
|
||||
set_root_passwd
|
||||
mount_proc_sys_dev
|
||||
setup_sources_list
|
||||
install_no_recommends ubuntu-minimal dbus-bin grub-common grub-gfxpayload-lists grub-pc grub-pc-bin grub2-common grub-efi-amd64-signed shim-signed initramfs-tools
|
||||
configure_divert
|
||||
install_no_recommends linux-image-generic
|
||||
clean
|
|
@ -0,0 +1,150 @@
|
|||
#!/usr/bin/bash
|
||||
|
||||
# Paso 1: Seleccionar los componentes
|
||||
# Los componentes a instalar se encuentran en el directorio /tmp/opengnsys-installer-configs
|
||||
|
||||
# Set configuration
|
||||
|
||||
function install_docker() {
|
||||
apt-get -y update
|
||||
apt-get -y install ca-certificates curl
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
|
||||
chmod a+r /etc/apt/keyrings/docker.asc
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
||||
tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt-get -y update
|
||||
apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
systemctl enable docker
|
||||
}
|
||||
|
||||
function install_ogcore_docker() {
|
||||
cat <<EOF > /etc/systemd/system/ogcore.service
|
||||
[Unit]
|
||||
Description=Servicio para ejecutar Docker Compose de ogCore
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/opt/opengnsys/ogCore/repo/
|
||||
ExecStart=/usr/bin/docker compose -f /opt/opengnsys/ogCore/etc/docker-compose-deploy.yml up
|
||||
ExecStartPost=/opengnsys-installer/provision_ogcore.sh
|
||||
ExecStop=/usr/bin/docker compose -f /opt/opengnsys/ogCore/etc/docker-compose-deploy.yml stop
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now ogcore
|
||||
}
|
||||
|
||||
|
||||
|
||||
function install_oggui_docker() {
|
||||
# Sacar la IP del ogCore de la configuración
|
||||
oggui_version=$(jq -r '.container_version' /opt/opengnsys/ogGui/installer/config.json)
|
||||
# Exportar los valores como variables de entorno
|
||||
ENV_DIR=/opt/opengnsys/ogGui/etc/
|
||||
ENV_FILE=$ENV_DIR/.env
|
||||
|
||||
cat <<EOF > /etc/systemd/system/oggui-app.service
|
||||
[Unit]
|
||||
Description=Servicio para contenedor Docker de OgGui
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
ExecStartPre=/opengnsys-installer/provision_oggui.sh
|
||||
ExecStart=/usr/bin/docker run --rm --name ogGui-app -p 4200:4200 -v $ENV_FILE:/app/.env opengnsys/oggui:$oggui_version
|
||||
ExecStop=/usr/bin/docker stop ogGui-app
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now oggui-app
|
||||
|
||||
}
|
||||
|
||||
|
||||
COMPONENTS="ogCore ogGui ogDhcp ogBoot ogRepository"
|
||||
CONFIGS_DIR=/opengnsys-installer/
|
||||
PAT_FILE=/opengnsys-installer/pat.txt
|
||||
PAT=$(cat $PAT_FILE | tr -d '\n\r\t')
|
||||
|
||||
OPENGNSYS_BASE_URL="https://$PAT@ognproject.evlt.uma.es/gitea/opengnsys"
|
||||
|
||||
OGBOOT_REPO="$OPENGNSYS_BASE_URL/ogboot.git"
|
||||
OGCORE_REPO="$OPENGNSYS_BASE_URL/ogcore.git"
|
||||
OGDHCP_REPO="$OPENGNSYS_BASE_URL/ogdhcp.git"
|
||||
OGGUI_REPO="$OPENGNSYS_BASE_URL/oggui.git"
|
||||
OGREPOSITORY_REPO="$OPENGNSYS_BASE_URL/ogrepository.git"
|
||||
|
||||
export GIT_SSL_NO_VERIFY=1
|
||||
echo ======================================== > /etc/issue
|
||||
echo "OpenGnSys Installer" >> /etc/issue
|
||||
echo "Componentes instalados:" >> /etc/issue
|
||||
|
||||
|
||||
|
||||
for component in $COMPONENTS
|
||||
do
|
||||
config_file="config_${component}.json"
|
||||
if [ -f $CONFIGS_DIR/$config_file ]; then
|
||||
echo "Componente $component seleccionado, instalando configuración..."
|
||||
component_dir=/opt/opengnsys/$component
|
||||
mkdir -p $component_dir/installer
|
||||
mkdir -p $component_dir/repo
|
||||
cp $CONFIGS_DIR/$config_file /opt/opengnsys/$component/installer/config.json
|
||||
|
||||
case $component in
|
||||
ogCore)
|
||||
echo "Instalando ogCore..."
|
||||
OGCORE_BRANCH=main
|
||||
container_version=$(jq -r '.container_version' /opt/opengnsys/ogCore/installer/config.json)
|
||||
git clone --branch "$OGCORE_BRANCH" "$OGCORE_REPO" "$component_dir/repo"
|
||||
# Copy the docker-compose-deploy.yml file to /opt/opengnsys/ogCore/etc/
|
||||
mkdir -p $component_dir/etc/
|
||||
cp $component_dir/repo/docker-compose-deploy.yml $component_dir/etc/
|
||||
sed -i "s/static/$container_version/g" $component_dir/repo/docker-compose-deploy.yml
|
||||
echo - ogCore >> /etc/issue
|
||||
install_docker
|
||||
install_ogcore_docker
|
||||
;;
|
||||
ogGui)
|
||||
echo "Instalando ogGui..."
|
||||
OGGUI_BRANCH=main
|
||||
git clone --branch "$OGGUI_BRANCH" "$OGGUI_REPO" "$component_dir/repo"
|
||||
echo - ogGui >> /etc/issue
|
||||
install_docker
|
||||
install_oggui_docker
|
||||
;;
|
||||
ogDhcp)
|
||||
echo "Instalando ogDhcp..."
|
||||
git clone "$OGDHCP_REPO" "$component_dir/repo"
|
||||
echo - ogDhcp >> /etc/issue
|
||||
;;
|
||||
ogBoot)
|
||||
echo "Instalando ogBoot..."
|
||||
git clone "$OGBOOT_REPO" "$component_dir/repo"
|
||||
echo - ogBoot >> /etc/issue
|
||||
;;
|
||||
ogRepository)
|
||||
echo "Instalando ogRepository..."
|
||||
git clone "$OGREPOSITORY_REPO" "$component_dir/repo"
|
||||
echo - ogRepository >> /etc/issue
|
||||
;;
|
||||
*)
|
||||
echo "Componente $component no reconocido"
|
||||
;;
|
||||
esac
|
||||
continue
|
||||
fi
|
||||
done
|
||||
|
||||
echo ======================================== >> /etc/issue
|
||||
rm -f $PAT_FILE
|
|
@ -0,0 +1,198 @@
|
|||
#!/usr/bin/bash
|
||||
set -x
|
||||
# Paso 1: Seleccionar los componentes
|
||||
# Los componentes a instalar se encuentran en el directorio /tmp/opengnsys-installer-configs
|
||||
# Set configuration
|
||||
|
||||
|
||||
COMPONENTS="ogCore ogGui ogDhcp ogBoot ogRepository"
|
||||
CONFIGS_DIR=/tmp/oginstall
|
||||
# PAT_FILE=/opengnsys-installer/pat.txt
|
||||
# PAT=$(cat $PAT_FILE | tr -d '\n\r\t')
|
||||
|
||||
# OPENGNSYS_BASE_URL="https://$PAT@ognproject.evlt.uma.es/gitea/opengnsys"ls
|
||||
OPENGNSYS_BASE_URL="https://ognproject.evlt.uma.es/gitea/opengnsys"
|
||||
|
||||
OGBOOT_REPO="$OPENGNSYS_BASE_URL/ogboot.git"
|
||||
OGCORE_REPO="$OPENGNSYS_BASE_URL/ogcore.git"
|
||||
OGDHCP_REPO="$OPENGNSYS_BASE_URL/ogdhcp.git"
|
||||
OGGUI_REPO="$OPENGNSYS_BASE_URL/oggui.git"
|
||||
OGREPOSITORY_REPO="$OPENGNSYS_BASE_URL/ogrepository.git"
|
||||
export GIT_SSL_NO_VERIFY=1
|
||||
|
||||
|
||||
# Creamos el usuario opengnsys
|
||||
|
||||
useradd -m -d /opt/opengnsys -r -s /bin/bash opengnsys
|
||||
|
||||
## Functions
|
||||
|
||||
function install_docker() {
|
||||
apt-get -y update
|
||||
apt-get -y install ca-certificates curl
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
|
||||
chmod a+r /etc/apt/keyrings/docker.asc
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
||||
tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt-get -y update
|
||||
apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
systemctl enable docker
|
||||
}
|
||||
|
||||
function install_ogcore_docker() {
|
||||
cat <<EOF > /etc/systemd/system/ogcore.service
|
||||
[Unit]
|
||||
Description=Servicio para ejecutar Docker Compose de ogCore
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/opt/opengnsys/ogCore/repo/
|
||||
ExecStart=/usr/bin/docker compose -f /opt/opengnsys/ogCore/etc/docker-compose-deploy.yml up
|
||||
ExecStartPost=/opt/opengnsys/ogCore/bin/provision_ogcore.sh
|
||||
ExecStop=/usr/bin/docker compose -f /opt/opengnsys/ogCore/etc/docker-compose-deploy.yml stop
|
||||
Restart=always
|
||||
TimeoutStartSec=600
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now ogcore
|
||||
}
|
||||
|
||||
|
||||
function install_oggui_docker() {
|
||||
# Sacar la IP del ogCore de la configuración
|
||||
oggui_version=$(jq -r '.release' /opt/opengnsys/ogGui/installer/config.json)
|
||||
# Exportar los valores como variables de entorno
|
||||
ENV_DIR=/opt/opengnsys/ogGui/etc/
|
||||
ENV_FILE=$ENV_DIR/.env
|
||||
|
||||
cat <<EOF > /etc/systemd/system/oggui-app.service
|
||||
[Unit]
|
||||
Description=Servicio para contenedor Docker de OgGui
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
ExecStartPre=/opt/opengnsys/ogGui/bin/provision_oggui.sh
|
||||
ExecStart=/usr/bin/docker run --rm --name ogGui-app -p 4200:4200 -v $ENV_FILE:/app/.env opengnsys/oggui:$oggui_version
|
||||
ExecStop=/usr/bin/docker stop ogGui-app
|
||||
TimeoutStartSec=600
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now oggui-app
|
||||
|
||||
}
|
||||
|
||||
function git_checkout_release() {
|
||||
git clone --no-checkout "$1" "$2"
|
||||
cd "$2" || exit
|
||||
git checkout tags/"$3"
|
||||
cd - || exit
|
||||
}
|
||||
|
||||
|
||||
|
||||
echo ======================================== > /etc/issue
|
||||
echo "OpenGnSys Installer" >> /etc/issue
|
||||
echo "Componentes instalados:" >> /etc/issue
|
||||
|
||||
|
||||
|
||||
for component in $COMPONENTS
|
||||
do
|
||||
config_file="config_${component}.json"
|
||||
if [ -f $CONFIGS_DIR/$config_file ]; then
|
||||
echo "Componente $component seleccionado, instalando configuración..."
|
||||
component_dir=/opt/opengnsys/$component
|
||||
mkdir -p $component_dir/installer
|
||||
mkdir -p $component_dir/repo
|
||||
cp $CONFIGS_DIR/$config_file /opt/opengnsys/$component/installer/config.json
|
||||
|
||||
case $component in
|
||||
ogCore)
|
||||
echo "Instalando ogCore..."
|
||||
OGCORE_BRANCH=$(jq -r '.release' /opt/opengnsys/ogCore/installer/config.json)
|
||||
container_version=$(jq -r '.release' /opt/opengnsys/ogCore/installer/config.json)
|
||||
git_checkout_release "$OGCORE_REPO" "$component_dir/repo" "$OGCORE_BRANCH"
|
||||
mkdir -p $component_dir/etc
|
||||
cp $component_dir/repo/.env $component_dir/etc/
|
||||
cp $component_dir/repo/env.json $component_dir/etc/
|
||||
chown 82:82 $component_dir/etc/env.json
|
||||
mkdir -p $component_dir/bin/
|
||||
cp $CONFIGS_DIR/provision_ogcore.sh $component_dir/bin/
|
||||
chmod 755 $component_dir/bin/provision_ogcore.sh
|
||||
cp $component_dir/repo/docker-compose-deploy.yml $component_dir/etc/
|
||||
sed -i "s/static/$container_version/g" $component_dir/etc/docker-compose-deploy.yml
|
||||
cat $component_dir/etc/docker-compose-deploy.yml
|
||||
echo - ogCore >> /etc/issue
|
||||
install_docker
|
||||
install_ogcore_docker
|
||||
;;
|
||||
ogGui)
|
||||
echo "Instalando ogGui..."
|
||||
OGGUI_BRANCH=$(jq -r '.container_version' /opt/opengnsys/ogGui/installer/config.json)
|
||||
mkdir -p $component_dir/bin
|
||||
cp $CONFIGS_DIR/provision_oggui.sh $component_dir/bin/
|
||||
chmod 755 $component_dir/bin/provision_oggui.sh
|
||||
git_checkout_release "$OGGUI_REPO" "$component_dir/repo" "$OGGUI_BRANCH"
|
||||
echo - ogGui >> /etc/issue
|
||||
install_docker
|
||||
install_oggui_docker
|
||||
;;
|
||||
ogDhcp)
|
||||
echo "Instalando ogDhcp..."
|
||||
OGCORE_BRANCH=$(jq -r '.release' /opt/opengnsys/ogDhcp/installer/config.json)
|
||||
git_checkout_release "$OGDHCP_REPO" "$component_dir/repo" "$OGCORE_BRANCH"
|
||||
cp $CONFIGS_DIR/$config_file $component_dir/repo/installer/config_ogdhcp.json
|
||||
cd $component_dir/repo/installer || exit
|
||||
chmod 755 ogdhcp_installer.sh
|
||||
./ogdhcp_installer.sh
|
||||
cd - || exit
|
||||
echo - ogDhcp >> /etc/issue
|
||||
;;
|
||||
ogBoot)
|
||||
OGCORE_BRANCH=$(jq -r '.release' /opt/opengnsys/ogBoot/installer/config.json)
|
||||
echo "Instalando ogBoot..."
|
||||
git_checkout_release "$OGBOOT_REPO" "$component_dir/repo" "$OGCORE_BRANCH"
|
||||
cp $CONFIGS_DIR/$config_file $component_dir/repo/installer/config.json
|
||||
apt install -y python3 git vim
|
||||
cd $component_dir/repo/installer || exit
|
||||
python3 ogboot_installer.py
|
||||
cd - || exit
|
||||
echo - ogBoot >> /etc/issue
|
||||
;;
|
||||
ogRepository)
|
||||
echo "Instalando ogRepository..."
|
||||
OGCORE_BRANCH=$(jq -r '.release' /opt/opengnsys/ogRepository/installer/config.json)
|
||||
git_checkout_release "$OGREPOSITORY_REPO" "$component_dir/repo" "$OGCORE_BRANCH"
|
||||
cp $CONFIGS_DIR/$config_file $component_dir/installer/config.json
|
||||
REPO_IP=$(jq -r '.ogrepository_ip' $component_dir/installer/config.json)
|
||||
CORE_IP=$(jq -r '.ogcore_server_ip' $component_dir/installer/config.json)
|
||||
OGUSER=$(jq -r '.ogrepository_samba_user' $component_dir/installer/config.json)
|
||||
OGPASS=$(jq -r '.ogrepository_samba_pass' $component_dir/installer/config.json)
|
||||
mkdir -p $component_dir/bin
|
||||
cp $CONFIGS_DIR/provision_ogrepository.sh $component_dir/bin/
|
||||
chmod 755 $component_dir/bin/provision_ogrepository.sh
|
||||
$component_dir/bin/provision_ogrepository.sh $REPO_IP $CORE_IP $OGUSER $OGPASS $component_dir/repo
|
||||
echo - ogRepository >> /etc/issue
|
||||
;;
|
||||
*)
|
||||
echo "Componente $component no reconocido"
|
||||
;;
|
||||
esac
|
||||
continue
|
||||
fi
|
||||
done
|
||||
|
||||
echo ======================================== >> /etc/issue
|
||||
# rm -f $PAT_FILE
|
|
@ -0,0 +1,56 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
set -x
|
||||
|
||||
CONF_DIR=/opt/opengnsys/ogCore/etc/
|
||||
cd /opt/opengnsys/ogCore/repo/ || exit
|
||||
|
||||
|
||||
if [ -f /opt/opengnsys/ogCore/installer/.deployed ]; then
|
||||
echo "ogCore ya instalado"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
while ! docker compose -f $CONF_DIR/docker-compose-deploy.yml ps --format json |jq -r '"\(.Name) \(.State)"' |grep -q 'ogcore-php running'; do
|
||||
sleep 2
|
||||
done
|
||||
|
||||
adminuser=$(jq -r '.username' /opt/opengnsys/ogCore/installer/config.json)
|
||||
adminpass=$(jq -r '.password' /opt/opengnsys/ogCore/installer/config.json)
|
||||
|
||||
# Despliega la aplicación
|
||||
docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php composer install
|
||||
# Genera las claves de JWT
|
||||
docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php php bin/console lexik:jwt:generate-keypair --overwrite
|
||||
# Crea/actualiza la base de datos
|
||||
docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php php bin/console doctrine:migrations:migrate --no-interaction
|
||||
# Carga los datos por defecto: usuario
|
||||
docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php php bin/console doctrine:fixtures:load --no-interaction
|
||||
# Carga los datos por defecto: roles
|
||||
docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php bin/console app:load-default-user-groups
|
||||
# Carga los datos por defecto: comandos
|
||||
docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php php bin/console app:load-default-commands
|
||||
# Carga los datos por defecto: menú
|
||||
docker compose -f $CONF_DIR/docker-compose-deploy.yml exec php php bin/console opengnsys:load-default-menu
|
||||
|
||||
|
||||
# Provision user admin
|
||||
bearer=$(curl -k -X 'POST' 'https://localhost:8443/auth/login' -H 'accept: application/json' -H 'Content-Type: application/json' -d "{ \"username\": \"ogadmin\", \"password\": \"12345678\" }" | jq .token | sed 's/"//g' )
|
||||
|
||||
|
||||
if [ $adminuser == "ogadmin" ]; then
|
||||
echo "Cambiando password a ogadmin no puede ser el usuario administrador"
|
||||
ogadmin_uuid=$(curl -q -k -L https://localhost:8443/users/?username=ogadmin -H 'accept: application/json' -H "Authorization: Bearer $bearer" | jq .[0].uuid | sed 's/"//g')
|
||||
curl -k -L -X PUT "https://localhost:8443/users/$ogadmin_uuid/reset-password" -H 'accept: application/ld+json' -H 'Content-Type: application/ld+json' -d "{\"currentPassword\": \"12345678\", \"newPassword\": \"$adminpass\", \"repeatNewPassword\": \"$adminpass\"}" -H "Authorization: Bearer $bearer"
|
||||
touch /opt/opengnsys/ogCore/installer/.deployed
|
||||
exit 0
|
||||
fi
|
||||
|
||||
curl -k -L --location 'https://localhost:8443/users' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "Authorization: Bearer $bearer" \
|
||||
--data "{ \"username\": \"$adminuser\", \"password\": \"$adminpass\", \"roles\": [\"ROLE_SUPER_ADMIN\"] }"
|
||||
|
||||
|
||||
touch /opt/opengnsys/ogCore/installer/.deployed
|
||||
exit 0
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
set -x
|
||||
|
||||
# preparar el fichero .env
|
||||
ENV_DIR=/opt/opengnsys/ogGui/etc/
|
||||
ENV_FILE=$ENV_DIR/.env
|
||||
mkdir -p $ENV_DIR
|
||||
|
||||
# Comprobar si ya se ha instalado ogCore
|
||||
if [ -f /opt/opengnsys/ogGui/installer/.deployed ]; then
|
||||
echo "ogCore ya instalado"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Sacar la IP del ogCore de la configuración
|
||||
ogcore_url=$(jq -r '.ogcore_ip' /opt/opengnsys/ogGui/installer/config.json)
|
||||
mercure_ip=$(jq -r '.mercure_ip' /opt/opengnsys/ogGui/installer/config.json)
|
||||
export OGCORE_URL="$ogcore_url"
|
||||
export MERCURE_URL="$mercure_url"
|
||||
|
||||
|
||||
# Si se ha configurado la IP del ogCore, se escribe en el fichero .env
|
||||
echo "NG_APP_BASE_API_URL=$OGCORE_URL" > $ENV_FILE
|
||||
echo "NG_APP_OGCORE_MERCURE_BASE_URL=https://$mercure_ip:3000/.well-known/mercure" >> $ENV_FILE
|
||||
|
||||
touch /opt/opengnsys/ogGui/installer/.deployed
|
|
@ -0,0 +1,113 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
|
||||
REPO_IP=${1:-"127.0.0.1"}
|
||||
CORE_IP=${2:-"127.0.0.1"}
|
||||
OGUSER=${3:-"opengnsys"}
|
||||
OGPASS=${4:-"og"}
|
||||
INSTALL_DIR=/opt/opengnsys/ogrepository
|
||||
DOWNLOAD_DIR=${5:-"/tmp/ogrepository"}
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
OGUSER_HOME=/opt/opengnsys
|
||||
export DEBIAN_FRONTEND
|
||||
export GIT_SSL_NO_VERIFY
|
||||
|
||||
|
||||
check_root() {
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
echo "This script must be run as root" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
install_uftp() {
|
||||
apt install uftp -y
|
||||
systemctl stop uftp
|
||||
systemctl disable uftp
|
||||
}
|
||||
|
||||
install_updcast () {
|
||||
apt install $DOWNLOAD_DIR/packets/udpcast_20230924_amd64.deb
|
||||
}
|
||||
|
||||
add_user_ogrepository() {
|
||||
if ! id "$OGUSER" &>/dev/null; then
|
||||
echo "User ogrepository does not exist, creating it"
|
||||
useradd -m -d $OGUSER_HOME -r -s /bin/bash $OGUSER
|
||||
fi
|
||||
if [ ! -d $OGUSER_HOME/.ssh ] ; then
|
||||
mkdir -p $OGUSER_HOME/.ssh
|
||||
cp /tmp/oginstall/ssh-keys/opengnsys $OGUSER_HOME/.ssh/id_ed25519
|
||||
cp /tmp/oginstall/ssh-keys/opengnsys.pub $OGUSER_HOME/.ssh/id_ed25519.pub
|
||||
cat /tmp/oginstall/ssh-keys/opengnsys.pub >> $OGUSER_HOME/.ssh/authorized_keys
|
||||
chown -R $OGUSER:$OGUSER $OGUSER_HOME/.ssh
|
||||
chmod 0600 $OGUSER_HOME/.ssh/*
|
||||
fi
|
||||
if [ ! -f /etc/sudoers.d/$OGUSER ]; then
|
||||
echo "User $OGUSER does not have sudo permissions, adding it"
|
||||
echo "$OGUSER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/"$OGUSER"
|
||||
fi
|
||||
|
||||
|
||||
}
|
||||
|
||||
create_directories() {
|
||||
mkdir -p $INSTALL_DIR
|
||||
mkdir -p $INSTALL_DIR/images $INSTALL_DIR/images_trash/ $INSTALL_DIR/bin/ $INSTALL_DIR/etc/ $INSTALL_DIR/log/ $INSTALL_DIR/api/ $INSTALL_DIR/images_virtual
|
||||
chown -R $OGUSER:$OGUSER $INSTALL_DIR
|
||||
}
|
||||
|
||||
install_dependencies() {
|
||||
apt update -y
|
||||
apt install -y git python3 python3-pip python3-flask python3-paramiko python3-psutil python3-flasgger debian-archive-keyring samba gunicorn wakeonlan lzop partclone qemu-utils
|
||||
}
|
||||
|
||||
install_ext_repo() {
|
||||
cp $DOWNLOAD_DIR/installer/files/ctorrent.sources /etc/apt/sources.list.d/ctorrent.sources
|
||||
apt update -y
|
||||
}
|
||||
|
||||
install_external_packages() {
|
||||
apt install -y bittorrent bittornado ctorrent
|
||||
}
|
||||
|
||||
install_ogrepo-api_service() {
|
||||
cp -r $DOWNLOAD_DIR/installer/files/ogrepo-api.service /etc/systemd/system/ogrepo-api.service
|
||||
sed -i "s/%%OGREPOSITORY_USER%%/$OGUSER/g" /etc/systemd/system/ogrepo-api.service
|
||||
systemctl enable --now ogrepo-api
|
||||
}
|
||||
|
||||
install_files() {
|
||||
cp -pr $DOWNLOAD_DIR/bin/* $INSTALL_DIR/bin/
|
||||
cp -pr $DOWNLOAD_DIR/etc/* $INSTALL_DIR/etc/
|
||||
cp -pr $DOWNLOAD_DIR/api/* $INSTALL_DIR/api/
|
||||
chown -R $OGUSER:$OGUSER $INSTALL_DIR
|
||||
chmod 755 $INSTALL_DIR/bin/*
|
||||
echo IPlocal="$REPO_IP" > $INSTALL_DIR/etc/ogAdmRepo.cfg
|
||||
echo IPcore="$CORE_IP" >> $INSTALL_DIR/etc/ogAdmRepo.cfg
|
||||
sudo chown $OGUSER:$OGUSER $INSTALL_DIR/etc/ogAdmRepo.cfg
|
||||
}
|
||||
|
||||
configure_samba() {
|
||||
echo "include = /etc/samba/smb.conf.ogrepository" >> /etc/samba/smb.conf
|
||||
cp $DOWNLOAD_DIR/installer/files/ogrepo-smb.conf /etc/samba/smb.conf.ogrepository
|
||||
sed -i "s/%%OGREPOSITORY_USER%%/$OGUSER/g" /etc/samba/smb.conf.ogrepository
|
||||
systemctl restart smbd
|
||||
# Create default user ogrepository
|
||||
(echo $OGPASS; echo $OGPASS) | smbpasswd -s -a $OGUSER
|
||||
}
|
||||
|
||||
## Main program
|
||||
|
||||
install_dependencies
|
||||
add_user_ogrepository
|
||||
install_ext_repo
|
||||
install_external_packages
|
||||
install_uftp
|
||||
install_updcast
|
||||
create_directories
|
||||
install_files
|
||||
install_ogrepo-api_service
|
||||
configure_samba
|
|
@ -0,0 +1,7 @@
|
|||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACBs7Wbqztq5ixPGFL+1DlTa0T6QUBMiLq6KxZnCJ5rofQAAAJD7Xj89+14/
|
||||
PQAAAAtzc2gtZWQyNTUxOQAAACBs7Wbqztq5ixPGFL+1DlTa0T6QUBMiLq6KxZnCJ5rofQ
|
||||
AAAEC4UmYDisgl5jNR6SUwRA80k6Qc06cBHg1mW3+2NU6SfmztZurO2rmLE8YUv7UOVNrR
|
||||
PpBQEyIurorFmcInmuh9AAAABm5vbmFtZQECAwQFBgc=
|
||||
-----END OPENSSH PRIVATE KEY-----
|
|
@ -0,0 +1 @@
|
|||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGztZurO2rmLE8YUv7UOVNrRPpBQEyIurorFmcInmuh9 noname
|
|
@ -0,0 +1,54 @@
|
|||
import os
|
||||
from git import Repo
|
||||
from packaging.version import Version
|
||||
|
||||
def get_highest_remote_tag(repo_path):
|
||||
try:
|
||||
# Abre el repositorio local
|
||||
repo = Repo(repo_path)
|
||||
|
||||
# Asegúrate de que el repositorio tiene un remoto
|
||||
if not repo.remotes:
|
||||
return None # No hay remotos configurados
|
||||
|
||||
# Obtén el remoto por defecto (origin o el primero disponible)
|
||||
remote = repo.remotes.origin
|
||||
|
||||
# Recupera los tags remotos
|
||||
remote.fetch(tags=True)
|
||||
remote_tags = [ref.name.split('/')[-1] for ref in repo.references if ref.path.startswith('refs/tags/')]
|
||||
if not remote_tags:
|
||||
return None # No hay tags remotos
|
||||
|
||||
# Ordena los tags remotos por versión
|
||||
tags_sorted = sorted(remote_tags, key=lambda t: Version(t) if t.replace('.', '').isdigit() else Version('0.0.0'), reverse=True)
|
||||
return tags_sorted[0] if tags_sorted else None
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error al procesar el repositorio {repo_path}: {e}")
|
||||
return None
|
||||
|
||||
def process_selected_repositories(base_path, repo_names):
|
||||
repo_highest_tags = {}
|
||||
for repo_name in repo_names:
|
||||
repo_path = os.path.join(base_path, repo_name)
|
||||
if os.path.exists(repo_path) and os.path.isdir(os.path.join(repo_path, '.git')):
|
||||
highest_tag = get_highest_remote_tag(repo_path)
|
||||
repo_highest_tags[repo_name] = highest_tag
|
||||
else:
|
||||
repo_highest_tags[repo_name] = "No es un repositorio Git válido"
|
||||
return repo_highest_tags
|
||||
|
||||
# Ruta base donde están los repositorios locales
|
||||
base_path = "../"
|
||||
|
||||
# Lista de nombres de repositorios específicos
|
||||
repo_names = [ "ogcore" , "oggui" , "ogboot" , "ogdhcp" , "ogrepository" ]
|
||||
|
||||
result = process_selected_repositories(base_path, repo_names)
|
||||
|
||||
# Muestra los resultados
|
||||
for repo_name, tag in result.items():
|
||||
print(f'{repo_name}:{tag}')
|
||||
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
import npyscreen
|
||||
import json
|
||||
import os
|
||||
from git import Repo
|
||||
|
||||
CONFIGS_DIR = "/tmp/oginstall"
|
||||
os.makedirs(CONFIGS_DIR, exist_ok=True)
|
||||
|
||||
REPO_URL = "https://ognproject.evlt.uma.es/gitea/opengnsys/ogcore.git"
|
||||
|
||||
def get_git_tags():
|
||||
try:
|
||||
repo_path = os.path.join(CONFIGS_DIR, "opengnsys_repo")
|
||||
if not os.path.exists(repo_path):
|
||||
print("Clonando el repositorio...")
|
||||
Repo.clone_from(REPO_URL, repo_path)
|
||||
else:
|
||||
print("Usando repositorio existente en", repo_path)
|
||||
|
||||
repo = Repo(repo_path)
|
||||
tags = [tag.name for tag in repo.tags if tag.name.startswith("opengnsys")]
|
||||
return tags
|
||||
except Exception as e:
|
||||
print("Error al obtener los tags:", str(e))
|
||||
return []
|
||||
|
||||
class ComponentSelectionForm(npyscreen.ActionForm):
|
||||
def create(self):
|
||||
self.components = self.add(npyscreen.TitleMultiSelect, max_height=6, name="Selecciona los componentes",
|
||||
values=["ogCore", "ogGui", "ogDhcp", "ogBoot", "ogRepository"], scroll_exit=True)
|
||||
self.tags = get_git_tags()
|
||||
self.tag = self.add(npyscreen.TitleSelectOne, max_height=10, name="Selecciona el tag",
|
||||
values=self.tags, scroll_exit=True)
|
||||
|
||||
def beforeEditing(self):
|
||||
npyscreen.blank_terminal()
|
||||
|
||||
def on_ok(self):
|
||||
npyscreen.blank_terminal()
|
||||
selected_components = [self.components.values[i] for i in self.components.value]
|
||||
if not selected_components or not self.tag.value:
|
||||
npyscreen.notify_confirm("Debes seleccionar al menos un componente y un tag.", title="Error")
|
||||
return
|
||||
selected_tag = self.tags[self.tag.value[0]]
|
||||
self.parentApp.selected_components = selected_components
|
||||
self.parentApp.selected_tag = selected_tag
|
||||
self.parentApp.current_component_index = 0
|
||||
self.parentApp.switchForm(selected_components[0])
|
||||
|
||||
def on_cancel(self):
|
||||
if npyscreen.notify_yes_no("¿Estás seguro de que deseas salir?", title="Confirmación"):
|
||||
self.parentApp.setNextForm(None)
|
||||
|
||||
class ComponentForm(npyscreen.ActionForm):
|
||||
component_name = None
|
||||
|
||||
def create(self):
|
||||
self.fields = {}
|
||||
|
||||
def beforeEditing(self):
|
||||
npyscreen.blank_terminal()
|
||||
self.fields.clear()
|
||||
self._recreate_form()
|
||||
|
||||
def _recreate_form(self):
|
||||
"""Limpia y recrea los widgets del formulario."""
|
||||
self._clear_widgets()
|
||||
self.configure_fields()
|
||||
|
||||
def configure_fields(self):
|
||||
"""Método para definir los campos de configuración para cada componente"""
|
||||
pass
|
||||
|
||||
def _clear_widgets(self):
|
||||
"""Limpia todos los widgets del formulario."""
|
||||
self._widgets__ = []
|
||||
self._widgets_by_id__ = {}
|
||||
self._contained_widgets = []
|
||||
|
||||
def validate_fields(self):
|
||||
"""Validaciones personalizadas para contraseñas."""
|
||||
password_field = None
|
||||
confirmation_field = None
|
||||
|
||||
# Identificar los campos de contraseña y confirmación
|
||||
for key, field_data in self.fields.items():
|
||||
if field_data.get("is_password_field"):
|
||||
password_field = field_data["widget"]
|
||||
if field_data.get("is_password_confirmation"):
|
||||
confirmation_field = field_data["widget"]
|
||||
|
||||
# Validar contraseñas si ambos campos están definidos
|
||||
if password_field and confirmation_field:
|
||||
if password_field.value != confirmation_field.value:
|
||||
npyscreen.notify_confirm("Las contraseñas no coinciden. Por favor, revísalas.", title="Error")
|
||||
return False
|
||||
return True
|
||||
|
||||
def add_password_field(self, key, name, is_confirmation=False, default_value=""):
|
||||
"""Añade un campo de contraseña con metadatos."""
|
||||
widget = self.add(npyscreen.TitlePassword, name=name, value=default_value)
|
||||
self.fields[key] = {
|
||||
"widget": widget,
|
||||
"is_password_field": not is_confirmation,
|
||||
"is_password_confirmation": is_confirmation,
|
||||
}
|
||||
|
||||
def on_ok(self):
|
||||
if not self.validate_fields():
|
||||
return # Si las validaciones fallan, no proceder
|
||||
|
||||
npyscreen.blank_terminal()
|
||||
config_data = {"release": self.parentApp.selected_tag}
|
||||
for key, field_data in self.fields.items():
|
||||
config_data[key] = field_data["widget"].value
|
||||
|
||||
config_file = os.path.join(CONFIGS_DIR, f"config_{self.component_name}.json")
|
||||
with open(config_file, "w") as f:
|
||||
json.dump(config_data, f)
|
||||
npyscreen.notify_confirm(f"Configuración de {self.component_name} guardada en {config_file}", title="Confirmación")
|
||||
|
||||
self.parentApp.current_component_index += 1
|
||||
if self.parentApp.current_component_index < len(self.parentApp.selected_components):
|
||||
next_component = self.parentApp.selected_components[self.parentApp.current_component_index]
|
||||
self.parentApp.switchForm(next_component)
|
||||
else:
|
||||
self.parentApp.setNextForm(None)
|
||||
|
||||
def on_cancel(self):
|
||||
if npyscreen.notify_yes_no("¿Estás seguro de que deseas salir?", title="Confirmación"):
|
||||
self.parentApp.setNextForm(None)
|
||||
|
||||
class OgCoreForm(ComponentForm):
|
||||
component_name = "ogCore"
|
||||
|
||||
def configure_fields(self):
|
||||
self.fields["username"] = {"widget": self.add(npyscreen.TitleText, name="Usuario administrador (ogadmin):", value="ogadmin")}
|
||||
self.add_password_field("password", "Contraseña:" , default_value="12345678")
|
||||
self.add_password_field("confirm_password", "Confirmar Contraseña:", is_confirmation=True, default_value="12345678")
|
||||
|
||||
class OgGuiForm(ComponentForm):
|
||||
component_name = "ogGui"
|
||||
|
||||
def configure_fields(self):
|
||||
self.fields["ogcore_ip"] = {"widget": self.add(npyscreen.TitleText, name="URL Api OgCore (https://127.0.0.1:8443):", value="https://127.0.0.1:8443")}
|
||||
self.fields["mercure_ip"] = {"widget": self.add(npyscreen.TitleText, name="Mercue IP (127.0.0.1):", value="127.0.0.1")}
|
||||
class OgDhcpForm(ComponentForm):
|
||||
component_name = "ogDhcp"
|
||||
|
||||
def configure_fields(self):
|
||||
self.fields["ogbootIP"] = {"widget": self.add(npyscreen.TitleText, name="IP servidor de Boot (127.0.0.1):", value="127.0.0.1")}
|
||||
self.fields["ogDhcpIP"] = {"widget": self.add(npyscreen.TitleText, name="IP servidor de DHCP (127.0.0.1):", value="127.0.0.1")}
|
||||
self.fields["ogDhcp_Dir"] = {"widget": self.add(npyscreen.TitleText, name="Directorio de ogdhcp (/opt/opengnsys/ogdhcp):", value="/opt/opengnsys/ogdhcp")}
|
||||
self.fields["interfaces"] = {"widget": self.add(npyscreen.TitleText, name="Interfaces Boot (eth0,eth1):", value="eth0,eth1")}
|
||||
|
||||
def on_ok(self):
|
||||
if not self.validate_fields():
|
||||
return # Si las validaciones fallan, no proceder
|
||||
|
||||
npyscreen.blank_terminal()
|
||||
config_data = {"release": self.parentApp.selected_tag}
|
||||
for key, field_data in self.fields.items():
|
||||
if key == "interfaces":
|
||||
config_data[key] = [iface.strip() for iface in field_data["widget"].value.split(",")]
|
||||
else:
|
||||
config_data[key] = field_data["widget"].value
|
||||
|
||||
config_file = os.path.join(CONFIGS_DIR, f"config_{self.component_name}.json")
|
||||
with open(config_file, "w") as f:
|
||||
json.dump(config_data, f)
|
||||
npyscreen.notify_confirm(f"Configuración de {self.component_name} guardada en {config_file}", title="Confirmación")
|
||||
|
||||
self.parentApp.current_component_index += 1
|
||||
if self.parentApp.current_component_index < len(self.parentApp.selected_components):
|
||||
next_component = self.parentApp.selected_components[self.parentApp.current_component_index]
|
||||
self.parentApp.switchForm(next_component)
|
||||
else:
|
||||
self.parentApp.setNextForm(None)
|
||||
|
||||
class OgBootForm(ComponentForm):
|
||||
component_name = "ogBoot"
|
||||
|
||||
def configure_fields(self):
|
||||
self.fields["ogCore_ServerIP"] = {"widget": self.add(npyscreen.TitleText, name="ogCore IP:", value="")}
|
||||
self.fields["ogBoot_ServerIP"] = {"widget": self.add(npyscreen.TitleText, name="ogBoot Server IP:", value="")}
|
||||
self.fields["ogBoot_Dir"] = {"widget": self.add(npyscreen.TitleText, name="ogCore Dir (/opt/opengnsys/ogboot):", value="/opt/opengnsys/ogboot")}
|
||||
self.fields["ogLive_Default"] = {"widget": self.add(npyscreen.TitleText, name="ogLive por defecto:", value="https://ognproject.evlt.uma.es/oglive/ogLive-noble-6.8.0-31-generic-amd64-r20250116.538e3fa_20250120.iso")}
|
||||
self.fields["ogBootSambaUser"] = {"widget": self.add(npyscreen.TitleText, name="ogBoot Samba User (opengnsys):", value="opengnsys")}
|
||||
self.add_password_field("ogBootSambaPass", "ogBoot Samba Pass (og):", default_value="og")
|
||||
self.add_password_field("confirm_ogBootSambaPass", "Confirmar ogBoot Samba Pass (og):", is_confirmation=True, default_value="og")
|
||||
|
||||
|
||||
class OgRepositoryForm(ComponentForm):
|
||||
component_name = "ogRepository"
|
||||
|
||||
def configure_fields(self):
|
||||
self.fields["ogrepository_ip"] = {"widget": self.add(npyscreen.TitleText, name="ogRepository IP:", value="127.0.0.1")}
|
||||
self.fields["ogcore_server_ip"] = {"widget": self.add(npyscreen.TitleText, name="ogCoreserver IP(127.0.0.1):", value="127.0.0.1")}
|
||||
self.fields["ogrepository_samba_user"] = {"widget": self.add(npyscreen.TitleText, name="Samba User:", value="opengnsys")}
|
||||
self.add_password_field("ogrepository_samba_pass", "Samba Password:", default_value="og")
|
||||
self.add_password_field("confirm_repository_password", "Confirmar Samba Password:", is_confirmation=True, default_value="og")
|
||||
|
||||
class ConfigApp(npyscreen.NPSAppManaged):
|
||||
def onStart(self):
|
||||
self.addForm("MAIN", ComponentSelectionForm, name="Selección de Componentes")
|
||||
self.addForm("ogCore", OgCoreForm, name="Configuración de ogCore")
|
||||
self.addForm("ogGui", OgGuiForm, name="Configuración de ogGui")
|
||||
self.addForm("ogDhcp", OgDhcpForm, name="Configuración de ogDhcp")
|
||||
self.addForm("ogBoot", OgBootForm, name="Configuración de ogBoot")
|
||||
self.addForm("ogRepository", OgRepositoryForm, name="Configuración de ogRepository")
|
||||
self.selected_components = []
|
||||
self.selected_tag = ""
|
||||
self.current_component_index = 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = ConfigApp()
|
||||
app.run()
|
|
@ -0,0 +1,989 @@
|
|||
import npyscreen
|
||||
import os
|
||||
from git import Repo
|
||||
import subprocess # Importar el módulo subprocess
|
||||
import requests # Importar el módulo requests
|
||||
import time # Importar time para simular el progreso
|
||||
import threading # Importar threading para leer el log en tiempo real
|
||||
import socket
|
||||
import sys # Importar sys para leer los argumentos del script
|
||||
import logging # Importar el módulo logging
|
||||
import shutil # Importar para verificar el tamaño del terminal
|
||||
|
||||
CONFIGS_DIR = "/tmp/oginstall"
|
||||
LOGS_DIR = "/var/log/oginstaller"
|
||||
|
||||
REPO_URL = "https://ognproject.evlt.uma.es/gitea/opengnsys/ogcore.git"
|
||||
|
||||
# Configurar logging y directorio configuración
|
||||
try:
|
||||
if not os.path.exists(LOGS_DIR):
|
||||
os.makedirs(LOGS_DIR, exist_ok=True)
|
||||
LOG_FILE = os.path.join(LOGS_DIR, "oginstall.log")
|
||||
if not os.path.exists(CONFIGS_DIR):
|
||||
os.makedirs(CONFIGS_DIR, exist_ok=True)
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_FILE,
|
||||
level=logging.DEBUG,
|
||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||
)
|
||||
logging.debug("Inicio del programa") # Mensaje inicial para verificar que el log se crea
|
||||
except Exception as e:
|
||||
print(f"[ERROR] No se pudo configurar el logging: {e}")
|
||||
print(f"[ERROR] Verifica los permisos del directorio: {LOGS_DIR}")
|
||||
exit(1) # Salir si no se puede configurar el logging
|
||||
|
||||
def get_network_interfaces():
|
||||
"""Obtiene los nombres de las interfaces de red disponibles en el servidor."""
|
||||
try:
|
||||
# Listar las interfaces de red desde /sys/class/net
|
||||
interfaces = os.listdir('/sys/class/net')
|
||||
# Filtrar interfaces válidas (excluyendo interfaces virtuales como 'lo')
|
||||
valid_interfaces = [iface for iface in interfaces if not iface.startswith('lo')]
|
||||
return ','.join(valid_interfaces) # Devuelve las interfaces separadas por comas
|
||||
except Exception as e:
|
||||
# En caso de error, devolver un valor por defecto
|
||||
print(f"Error al obtener las interfaces de red: {e}")
|
||||
return "eth0" # Valor por defecto
|
||||
|
||||
def get_available_versions():
|
||||
"""Obtiene la lista de versiones desde el archivo JSON remoto."""
|
||||
try:
|
||||
# Validar si se pasa un argumento
|
||||
if len(sys.argv) > 1:
|
||||
arg = sys.argv[1].strip().lower()
|
||||
logging.debug(f"Argumento recibido: {arg}") # Usar logging en lugar de print
|
||||
|
||||
# Validar explícitamente los valores permitidos
|
||||
if arg == "devel":
|
||||
url = "https://ognproject.evlt.uma.es/debian-opengnsys/versions-dev.json"
|
||||
elif arg == "nightly":
|
||||
logging.debug("No hay versiones disponibles para nightly. Usando 'latest'.")
|
||||
return ["latest"] # Devolver solo la opción 'latest'
|
||||
else:
|
||||
logging.debug(f"Argumento no reconocido: {arg}. Usando versiones de producción.")
|
||||
url = "https://ognproject.evlt.uma.es/debian-opengnsys/versions-prod.json"
|
||||
else:
|
||||
logging.debug("No se pasó ningún argumento. Usando versiones de producción.")
|
||||
url = "https://ognproject.evlt.uma.es/debian-opengnsys/versions-prod.json"
|
||||
|
||||
# Realizar la solicitud HTTP
|
||||
logging.debug(f"Realizando solicitud HTTP a: {url}")
|
||||
response = requests.get(url, timeout=10)
|
||||
response.raise_for_status() # Lanza una excepción si la respuesta no es 200 OK
|
||||
|
||||
# Registrar el contenido de la respuesta para depuración
|
||||
logging.debug(f"Contenido de la respuesta: {response.text}")
|
||||
|
||||
# Intentar analizar el JSON
|
||||
try:
|
||||
data = response.json()
|
||||
except ValueError as e:
|
||||
logging.error(f"Error al analizar el JSON: {e}")
|
||||
raise RuntimeError(f"El contenido de la respuesta no es un JSON válido: {e}")
|
||||
|
||||
# Validar que el JSON contiene la clave "versions"
|
||||
versions = data.get("versions", [])
|
||||
if not versions:
|
||||
logging.warning("La lista de versiones está vacía.")
|
||||
logging.debug(f"Versiones obtenidas: {versions}")
|
||||
return versions
|
||||
except (requests.RequestException, ValueError, RuntimeError) as e:
|
||||
logging.error(f"No se pudo obtener la lista de versiones: {e}")
|
||||
raise RuntimeError(f"Error crítico: {e}") # Lanzar una excepción crítica
|
||||
|
||||
def get_default_ip():
|
||||
"""Obtiene la IP asociada a la interfaz por defecto del servidor."""
|
||||
logging.debug("Obteniendo la IP por defecto del servidor.")
|
||||
try:
|
||||
# Obtener la interfaz asociada a la ruta por defecto
|
||||
result = subprocess.run(
|
||||
["ip", "route", "get", "1.1.1.1"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
# Extraer la interfaz de la salida
|
||||
interface = next((line.split()[-1] for line in result.stdout.splitlines() if "dev" in line), None)
|
||||
if not interface:
|
||||
logging.error("No se pudo determinar la interfaz por defecto.")
|
||||
raise ValueError("No se pudo determinar la interfaz por defecto.")
|
||||
logging.debug(f"Interfaz por defecto: {interface}")
|
||||
|
||||
# Obtener la IP de la interfaz
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
||||
s.connect(("8.8.8.8", 80))
|
||||
ip_address = s.getsockname()[0]
|
||||
return ip_address
|
||||
except Exception as e:
|
||||
logging.error(f"Error al obtener la IP por defecto: {e}")
|
||||
return "192.168.2.2" # Valor por defecto
|
||||
|
||||
def get_oglive_list():
|
||||
"""Obtiene la lista de valores de oglives desde la URL."""
|
||||
try:
|
||||
# Realizar la solicitud HTTP
|
||||
response = requests.get("https://ognproject.evlt.uma.es/oglive/", timeout=10)
|
||||
response.raise_for_status() # Lanza una excepción si la respuesta no es 200 OK
|
||||
# Registrar el contenido de la respuesta para depuración
|
||||
|
||||
# Extraer los enlaces del contenido HTML
|
||||
from bs4 import BeautifulSoup
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
links = [a["href"] for a in soup.find_all("a", href=True) if "ogLive" in a["href"]]
|
||||
|
||||
# Ordenar los enlaces por la parte después del guion bajo
|
||||
sorted_links = sorted(links, key=lambda x: x.split("_")[1] if "_" in x else x, reverse=True)
|
||||
return sorted_links
|
||||
except Exception as e:
|
||||
logging.error(f"Error al obtener la lista de oglives: {e}")
|
||||
return [] # Devolver una lista vacía en caso de error
|
||||
|
||||
# Variable global para la IP por defecto
|
||||
DEFAULT_IP = get_default_ip()
|
||||
|
||||
class InstallationTypeForm(npyscreen.ActionForm):
|
||||
"""Formulario para seleccionar el tipo de instalación."""
|
||||
def create(self):
|
||||
self.installation_type = self.add(
|
||||
npyscreen.TitleSelectOne,
|
||||
name="Selecciona el tipo de instalación:",
|
||||
values=["Mononodo", "Multinodo"],
|
||||
scroll_exit=False, max_height=10
|
||||
)
|
||||
|
||||
def on_ok(self):
|
||||
"""Guardar la selección y pasar al formulario correspondiente."""
|
||||
logging.debug(f"Entrando en InstallationTypeForm")
|
||||
if self.installation_type.value is None:
|
||||
npyscreen.notify_confirm("Debes seleccionar un tipo de instalación.", title="Error")
|
||||
return
|
||||
|
||||
if self.installation_type.value == [0]: # Mononodo
|
||||
logging.debug("Instalación mononodo seleccionada.")
|
||||
self.parentApp.installation_type = "mononodo"
|
||||
self.parentApp.setNextForm("MONONODO_CONFIG")
|
||||
elif self.installation_type.value == [1]: # Multinodo
|
||||
logging.debug("Instalación multinodo seleccionada.")
|
||||
self.parentApp.installation_type = "multinodo"
|
||||
self.parentApp.setNextForm("MULTINODO_CONFIG")
|
||||
|
||||
def on_cancel(self):
|
||||
"""Salir de la aplicación."""
|
||||
if npyscreen.notify_yes_no("¿Estás seguro de que deseas salir?", title="Confirmación"):
|
||||
self.parentApp.setNextForm(None)
|
||||
|
||||
|
||||
class MononodoConfigForm(npyscreen.ActionForm):
|
||||
"""Formulario para configurar Mononodo."""
|
||||
def create(self):
|
||||
self.server_ip = self.add(
|
||||
npyscreen.TitleText,
|
||||
name="IP del servidor (mononodo):",
|
||||
value=get_default_ip()
|
||||
)
|
||||
|
||||
def on_ok(self):
|
||||
"""Guardar la configuración y pasar al siguiente formulario."""
|
||||
logging.debug(f"Entrando en MononodoConfigForm")
|
||||
self.parentApp.server_ip = self.server_ip.value
|
||||
self.parentApp.setNextForm("MAIN")
|
||||
|
||||
def on_cancel(self):
|
||||
"""Volver al formulario de selección de tipo de instalación."""
|
||||
self.parentApp.setNextForm("INSTALLATION_TYPE")
|
||||
|
||||
|
||||
class MultinodoConfigForm(npyscreen.ActionForm):
|
||||
"""Formulario para configurar Multinodo."""
|
||||
def create(self):
|
||||
self.repo_ip = self.add(
|
||||
npyscreen.TitleText,
|
||||
name="IP del servidor Repository:",
|
||||
value=get_default_ip()
|
||||
)
|
||||
self.dhcp_ip = self.add(
|
||||
npyscreen.TitleText,
|
||||
name="IP del servidor DHCP:",
|
||||
value=get_default_ip()
|
||||
)
|
||||
self.core_ip = self.add(
|
||||
npyscreen.TitleText,
|
||||
name="IP del servidor Core:",
|
||||
value=get_default_ip()
|
||||
)
|
||||
self.boot_ip = self.add(
|
||||
npyscreen.TitleText,
|
||||
name="IP del servidor Boot:",
|
||||
value=get_default_ip()
|
||||
)
|
||||
|
||||
def on_ok(self):
|
||||
"""Guardar la configuración y pasar al siguiente formulario."""
|
||||
self.parentApp.repo_ip = self.repo_ip.value
|
||||
self.parentApp.dhcp_ip = self.dhcp_ip.value
|
||||
self.parentApp.core_ip = self.core_ip.value
|
||||
self.parentApp.boot_ip = self.boot_ip.value
|
||||
self.parentApp.setNextForm("MAIN")
|
||||
|
||||
def on_cancel(self):
|
||||
"""Volver al formulario de selección de tipo de instalación."""
|
||||
self.parentApp.setNextForm("INSTALLATION_TYPE")
|
||||
|
||||
|
||||
class ComponentSelectionForm(npyscreen.ActionForm):
|
||||
def create(self):
|
||||
self.components = self.add(
|
||||
npyscreen.TitleMultiSelect,
|
||||
max_height=6,
|
||||
name="Selecciona los componentes",
|
||||
values=["ogCore", "ogGui", "ogDhcp", "ogBoot", "ogRepository"],
|
||||
scroll_exit=True
|
||||
)
|
||||
self.versions = get_available_versions() # Obtener las versiones desde el archivo JSON
|
||||
|
||||
# Si no hay versiones disponibles, usar "latest" como opción por defecto
|
||||
if not self.versions:
|
||||
self.versions = ["latest"]
|
||||
|
||||
self.tag = self.add(
|
||||
npyscreen.TitleSelectOne,
|
||||
max_height=10,
|
||||
name="Selecciona la versión",
|
||||
values=self.versions,
|
||||
scroll_exit=True
|
||||
)
|
||||
self.tag.value = [0] # Marcar "latest" (o la primera opción) por defecto
|
||||
|
||||
# Mostrar la IP del servidor si es mononodo
|
||||
if self.parentApp.installation_type == "mononodo":
|
||||
self.server_ip = self.add(
|
||||
npyscreen.TitleText,
|
||||
name="IP del servidor (mononodo):",
|
||||
value=self.parentApp.server_ip,
|
||||
editable=False
|
||||
)
|
||||
|
||||
# Agregar un cuadro de texto para mostrar el log
|
||||
self.log_box = self.add(
|
||||
npyscreen.BoxTitle,
|
||||
name="Log de depuración",
|
||||
max_height=10,
|
||||
scroll_exit=True
|
||||
)
|
||||
|
||||
def beforeEditing(self):
|
||||
"""Configurar los valores iniciales de los componentes según el tipo de instalación."""
|
||||
if self.parentApp.installation_type == "mononodo":
|
||||
# Seleccionar todos los componentes por defecto
|
||||
self.components.value = list(range(len(self.components.values)))
|
||||
else:
|
||||
# No seleccionar ningún componente por defecto
|
||||
self.components.value = []
|
||||
self.display()
|
||||
|
||||
def on_ok(self):
|
||||
# Validar selección obligatoria de componentes y versión
|
||||
if not self.components.value or len(self.components.value) == 0:
|
||||
npyscreen.notify_confirm("Debes seleccionar al menos un componente.", title="Error")
|
||||
return
|
||||
if not self.tag.value or len(self.tag.value) == 0:
|
||||
npyscreen.notify_confirm("Debes seleccionar una versión.", title="Error")
|
||||
return
|
||||
|
||||
if self.parentApp.installation_type == "mononodo":
|
||||
self.handle_mononodo()
|
||||
else:
|
||||
self.handle_multinodo()
|
||||
|
||||
def handle_mononodo(self):
|
||||
npyscreen.blank_terminal()
|
||||
selected_components = [self.components.values[i].lower() for i in self.components.value]
|
||||
selected_tag = self.versions[self.tag.value[0]]
|
||||
self.parentApp.selected_components = selected_components
|
||||
self.parentApp.selected_tag = selected_tag
|
||||
self.parentApp.current_component_index = 0
|
||||
self.parentApp.configurations = {}
|
||||
self.parentApp.switchForm(selected_components[0])
|
||||
|
||||
def handle_multinodo(self):
|
||||
selected_components = [self.components.values[i].lower() for i in self.components.value]
|
||||
selected_tag = self.versions[self.tag.value[0]]
|
||||
self.parentApp.selected_components = selected_components
|
||||
self.parentApp.selected_tag = selected_tag
|
||||
self.parentApp.current_component_index = 0
|
||||
self.parentApp.configurations = {}
|
||||
self.parentApp.switchForm(selected_components[0])
|
||||
|
||||
class ComponentForm(npyscreen.ActionForm):
|
||||
component_name = None
|
||||
|
||||
def create(self):
|
||||
# Agregar un título dinámico basado en el componente en la primera línea
|
||||
self.title = self.add(
|
||||
npyscreen.FixedText,
|
||||
value="",
|
||||
editable=False,
|
||||
color="STANDOUT",
|
||||
rely=0 # Forzar que el título esté en la primera línea
|
||||
)
|
||||
self.fields = {}
|
||||
|
||||
def beforeEditing(self):
|
||||
npyscreen.blank_terminal()
|
||||
# Actualizar el valor del título dinámico basado en el componente
|
||||
self.title.value = f"Configuración del componente: {self.component_name.upper()}"
|
||||
self.title.display()
|
||||
self._recreate_form()
|
||||
|
||||
def _recreate_form(self):
|
||||
"""Limpia y recrea los widgets del formulario, excepto el título."""
|
||||
# No eliminar el título al recrear los widgets
|
||||
self._widgets__ = [self.title]
|
||||
self._widgets_by_id__ = {id(self.title): self.title}
|
||||
self._contained_widgets = [self.title]
|
||||
self.configure_fields()
|
||||
|
||||
def configure_fields(self):
|
||||
"""Método para definir los campos de configuración para cada componente"""
|
||||
pass
|
||||
|
||||
def on_ok(self):
|
||||
npyscreen.blank_terminal()
|
||||
component_config = {}
|
||||
|
||||
for key, field_data in self.fields.items():
|
||||
component_config[key] = field_data["widget"].value
|
||||
|
||||
self.parentApp.configurations[self.component_name] = component_config
|
||||
|
||||
self.parentApp.current_component_index += 1
|
||||
if self.parentApp.current_component_index < len(self.parentApp.selected_components):
|
||||
next_component = self.parentApp.selected_components[self.parentApp.current_component_index]
|
||||
self.parentApp.switchForm(next_component)
|
||||
else:
|
||||
self.parentApp.generate_debconf()
|
||||
self.parentApp.setNextForm(None)
|
||||
|
||||
def on_cancel(self):
|
||||
if npyscreen.notify_yes_no("¿Estás seguro de que deseas salir?", title="Confirmación"):
|
||||
self.parentApp.setNextForm(None)
|
||||
|
||||
class OgCoreForm(ComponentForm):
|
||||
component_name = "ogcore"
|
||||
|
||||
def configure_fields(self):
|
||||
|
||||
self.add(npyscreen.FixedText, value="Usuario Administrador: ", editable=False, rely=2, relx=2, color="SAFE" , highlighted=True)
|
||||
self.fields["adminUser"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.Textfield,
|
||||
value="ogadmin",
|
||||
rely=3, # Línea siguiente
|
||||
relx=18,
|
||||
highlighted=True
|
||||
)
|
||||
}
|
||||
self.fields["adminPass"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.TitlePassword,
|
||||
name="Contraseña Administrador:",
|
||||
value="12345678",
|
||||
rely=6 , # Ajustar la posición vertical
|
||||
highlighted=True ,
|
||||
scroll_exit=True
|
||||
)
|
||||
}
|
||||
|
||||
class OgGuiForm(ComponentForm):
|
||||
component_name = "oggui"
|
||||
|
||||
def configure_fields(self):
|
||||
"""Configura los campos del formulario según el tipo de instalación."""
|
||||
if self.parentApp.installation_type == "mononodo":
|
||||
self.server_ip = self.parentApp.server_ip
|
||||
elif self.parentApp.installation_type == "multinodo":
|
||||
self.server_ip = self.parentApp.core_ip
|
||||
|
||||
self.add(npyscreen.FixedText, value="URL del servidor Core:", editable=False, rely=2, relx=2, color="SAFE" , highlighted=True)
|
||||
self.fields["ogcoreUrl"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.Textfield,
|
||||
value=f"https://{self.server_ip}:8443",
|
||||
rely=3 ,
|
||||
relx=18,
|
||||
highlighted=True# Ajustar la posición vertical
|
||||
)
|
||||
}
|
||||
self.add(npyscreen.FixedText, value="URL del servidor Mercure:", editable=False, rely=4, relx=2, color="SAFE" , highlighted=True)
|
||||
self.fields["ogmercureUrl"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.Textfield,
|
||||
value=f"https://{self.server_ip}:3000/.well-known/mercure",
|
||||
rely=6,
|
||||
relx=18# Ajustar la posición vertical
|
||||
)
|
||||
}
|
||||
|
||||
def on_ok(self):
|
||||
"""Guarda la configuración y pasa al siguiente formulario."""
|
||||
# Obtener la configuración del formulario
|
||||
component_config = {
|
||||
"ogcoreUrl": self.fields["ogcoreUrl"]["widget"].value,
|
||||
"ogmercureUrl": self.fields["ogmercureUrl"]["widget"].value,
|
||||
}
|
||||
|
||||
# Guardar la configuración en el diccionario global
|
||||
self.parentApp.configurations[self.component_name] = component_config
|
||||
|
||||
# Continuar con el siguiente formulario
|
||||
self.parentApp.current_component_index += 1
|
||||
if self.parentApp.current_component_index < len(self.parentApp.selected_components):
|
||||
next_component = self.parentApp.selected_components[self.parentApp.current_component_index]
|
||||
self.parentApp.switchForm(next_component)
|
||||
else:
|
||||
self.parentApp.generate_debconf()
|
||||
self.parentApp.setNextForm(None)
|
||||
|
||||
|
||||
class OgDhcpForm(ComponentForm):
|
||||
component_name = "ogdhcp"
|
||||
|
||||
def get_dhcp_ip(self):
|
||||
"""Obtiene la IP del servidor DHCP."""
|
||||
if self.parentApp.installation_type == "mononodo":
|
||||
return self.parentApp.server_ip
|
||||
elif self.parentApp.installation_type == "multinodo":
|
||||
return self.parentApp.dhcp_ip
|
||||
|
||||
def get_boot_ip(self):
|
||||
"""Obtiene la IP del servidor Boot."""
|
||||
if self.parentApp.installation_type == "mononodo":
|
||||
return self.parentApp.server_ip
|
||||
elif self.parentApp.installation_type == "multinodo":
|
||||
return self.parentApp.boot_ip
|
||||
|
||||
def configure_fields(self):
|
||||
# Obtener las interfaces de red disponibles
|
||||
available_interfaces = get_network_interfaces().split(",")
|
||||
|
||||
# Selector de interfaces con altura ajustada
|
||||
self.fields["dhcp_interfaces"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.TitleMultiSelect,
|
||||
name="Selecciona la interfaz de DHCP:",
|
||||
values=available_interfaces,
|
||||
scroll_exit=True,
|
||||
rely=2,
|
||||
max_height=5 # Reducir la altura para dejar espacio
|
||||
)
|
||||
}
|
||||
|
||||
# Campo para la IP del servidor DHCP
|
||||
self.fields["ip"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.TitleText,
|
||||
name="IP del servidor DHCP:",
|
||||
value=self.get_dhcp_ip(),
|
||||
rely=8 # Ajustar la posición vertical
|
||||
)
|
||||
}
|
||||
|
||||
# Campo para la IP del servidor Boot
|
||||
self.fields["ogbootIP"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.TitleText,
|
||||
name="IP del servidor Boot:",
|
||||
value=self.get_boot_ip(),
|
||||
rely=10 # Ajustar la posición vertical
|
||||
)
|
||||
}
|
||||
|
||||
def on_ok(self):
|
||||
available_interfaces = self.fields["dhcp_interfaces"]["widget"].values
|
||||
selected_indices = self.fields["dhcp_interfaces"]["widget"].value
|
||||
# Validar que al menos un interfaz esté seleccionado
|
||||
if not selected_indices or len(selected_indices) == 0:
|
||||
npyscreen.notify_confirm("Debes seleccionar al menos una interfaz de red para DHCP.", title="Error")
|
||||
return
|
||||
try:
|
||||
for i in selected_indices:
|
||||
if i < 0 or i >= len(available_interfaces):
|
||||
raise IndexError("Índice fuera de rango")
|
||||
selected_interfaces = [available_interfaces[i] for i in selected_indices]
|
||||
logging.debug(f"Interfaces seleccionadas: {selected_interfaces}")
|
||||
interfaces_string = ",".join(selected_interfaces)
|
||||
logging.debug(f"Interfaces seleccionadas: {interfaces_string}")
|
||||
except (IndexError, ValueError):
|
||||
npyscreen.notify_confirm("Selección inválida. Por favor, revisa los índices ingresados.", title="Error")
|
||||
return
|
||||
|
||||
# Guardar las configuraciones
|
||||
self.parentApp.configurations[self.component_name] = {
|
||||
"interfaces": interfaces_string,
|
||||
"ip": self.fields["ip"]["widget"].value,
|
||||
"ogbootIP": self.fields["ogbootIP"]["widget"].value,
|
||||
}
|
||||
|
||||
# Continuar con el siguiente formulario
|
||||
self.parentApp.current_component_index += 1
|
||||
if self.parentApp.current_component_index < len(self.parentApp.selected_components):
|
||||
next_component = self.parentApp.selected_components[self.parentApp.current_component_index]
|
||||
self.parentApp.switchForm(next_component)
|
||||
else:
|
||||
self.parentApp.generate_debconf()
|
||||
self.parentApp.setNextForm(None)
|
||||
|
||||
class OgBootForm(ComponentForm):
|
||||
component_name = "ogboot"
|
||||
download_url = "https://ognproject.evlt.uma.es/oglive/"
|
||||
def get_boot_ip(self):
|
||||
"""Obtiene la IP del servidor Boot."""
|
||||
if self.parentApp.installation_type == "mononodo":
|
||||
return self.parentApp.server_ip
|
||||
elif self.parentApp.installation_type == "multinodo":
|
||||
return self.parentApp.boot_ip
|
||||
|
||||
def get_core_ip(self):
|
||||
"""Obtiene la IP del servidor Core."""
|
||||
if self.parentApp.installation_type == "mononodo":
|
||||
return self.parentApp.server_ip
|
||||
elif self.parentApp.installation_type == "multinodo":
|
||||
return self.parentApp.core_ip
|
||||
|
||||
def configure_fields(self):
|
||||
# Obtener la lista de oglives
|
||||
oglives = get_oglive_list()
|
||||
if not oglives:
|
||||
oglives = ["https://ognproject.evlt.uma.es/oglive/ogLive-noble-6.8.0-31-generic-amd64-r20250116.538e3fa_20250120.iso"]
|
||||
npyscreen.notify_confirm("No se pudo obtener la lista de oglives. Usando un valor por defecto.", title="Error")
|
||||
|
||||
# Campo para seleccionar un oglive
|
||||
self.fields["ogliveUrl"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.TitleSelectOne,
|
||||
name="Selecciona un OgLive:",
|
||||
values=oglives,
|
||||
scroll_exit=True,
|
||||
max_height=6,
|
||||
rely=2 # Ajustar la posición vertical
|
||||
)
|
||||
}
|
||||
|
||||
# Otros campos
|
||||
#self.fields["ip"] = {"widget": self.add(npyscreen.TitleText, name="IP del servidor Boot:", value=self.get_boot_ip(), rely=14)}
|
||||
self.add(npyscreen.FixedText, value="IP del servidor Boot:", editable=False, rely=10, relx=2, color="SAFE" , highlighted=True)
|
||||
self.fields["ip"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.Textfield,
|
||||
value=self.get_boot_ip(),
|
||||
rely=11, # Línea siguiente
|
||||
relx=18,
|
||||
highlighted=True
|
||||
)
|
||||
}
|
||||
# self.fields["ogcoreUrl"] = {"widget": self.add(npyscreen.TitleText, name="URL OgCore:", value=f"https://{self.get_core_ip()}:8443", rely=18)}
|
||||
self.add(npyscreen.FixedText, value="IP del servidor Core:", editable=False, rely=12, relx=2, color="SAFE" , highlighted=True)
|
||||
self.fields["ogcoreUrl"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.Textfield,
|
||||
value=f"https://{self.get_core_ip()}:8443",
|
||||
rely=13, # Línea siguiente
|
||||
relx=18,
|
||||
highlighted=True
|
||||
)
|
||||
}
|
||||
# self.fields["sambaUser"] = {"widget": self.add(npyscreen.TitleText, name="Usuario Samba:", value="opengnsys", rely=20)}
|
||||
self.add(npyscreen.FixedText, value="Usuario Samba:", editable=False, rely=14, relx=2, color="SAFE" , highlighted=True)
|
||||
self.fields["sambaUser"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.Textfield,
|
||||
value="opengnsys",
|
||||
rely=15, # Línea siguiente
|
||||
relx=18,
|
||||
highlighted=True
|
||||
)
|
||||
}
|
||||
#self.fields["sambaUserPass"] = {"widget": self.add(npyscreen.TitlePassword, name="Contraseña Samba:", value="og", rely=22)}
|
||||
self.fields["sambaUserPass"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.TitlePassword,
|
||||
name="Contraseña Samba:",
|
||||
value="og",
|
||||
rely=16, # Línea siguiente
|
||||
)
|
||||
}
|
||||
#self.fields["port"] = {"widget": self.add(npyscreen.TitleText, name="Puerto Boot:", value="8082",hidden=True, rely=16)}
|
||||
self.add(npyscreen.FixedText, value="Puerto Boot:", editable=False, rely=12, relx=2, color="SAFE" , highlighted=True,hidden=True)
|
||||
self.fields["port"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.Textfield,
|
||||
value="8082",
|
||||
rely=13, # Línea siguiente
|
||||
relx=18,
|
||||
hidden=True
|
||||
)
|
||||
}
|
||||
|
||||
def on_ok(self):
|
||||
# Obtener el oglive seleccionado
|
||||
selected_oglive_index = self.fields["ogliveUrl"]["widget"].value
|
||||
if not selected_oglive_index or len(selected_oglive_index) == 0:
|
||||
npyscreen.notify_confirm("Debes seleccionar una imagen OgLive.", title="Error")
|
||||
return
|
||||
selected_oglive = self.fields["ogliveUrl"]["widget"].values[selected_oglive_index[0]]
|
||||
|
||||
# Guardar las configuraciones
|
||||
self.parentApp.configurations[self.component_name] = {
|
||||
"ogliveUrl": self.download_url + selected_oglive,
|
||||
"ip": self.fields["ip"]["widget"].value,
|
||||
"port": self.fields["port"]["widget"].value,
|
||||
"ogcoreUrl": self.fields["ogcoreUrl"]["widget"].value,
|
||||
"sambaUser": self.fields["sambaUser"]["widget"].value,
|
||||
"sambaUserPass": self.fields["sambaUserPass"]["widget"].value,
|
||||
}
|
||||
|
||||
# Continuar con el siguiente formulario
|
||||
self.parentApp.current_component_index += 1
|
||||
if self.parentApp.current_component_index < len(self.parentApp.selected_components):
|
||||
next_component = self.parentApp.selected_components[self.parentApp.current_component_index]
|
||||
self.parentApp.switchForm(next_component)
|
||||
else:
|
||||
self.parentApp.generate_debconf()
|
||||
self.parentApp.setNextForm(None)
|
||||
|
||||
class OgRepositoryForm(ComponentForm):
|
||||
component_name = "ogrepository"
|
||||
|
||||
def get_repo_ip(self):
|
||||
"""Obtiene la IP del servidor Repository."""
|
||||
if self.parentApp.installation_type == "mononodo":
|
||||
return self.parentApp.server_ip
|
||||
elif self.parentApp.installation_type == "multinodo":
|
||||
return self.parentApp.repo_ip
|
||||
|
||||
def get_core_ip(self):
|
||||
"""Obtiene la IP del servidor Core."""
|
||||
if self.parentApp.installation_type == "mononodo":
|
||||
return self.parentApp.server_ip
|
||||
elif self.parentApp.installation_type == "multinodo":
|
||||
return self.parentApp.core_ip
|
||||
|
||||
def configure_fields(self):
|
||||
# Campo para la IP del Repositorio
|
||||
self.add(npyscreen.FixedText, value="IP del Repositorio:", editable=False, rely=2, relx=2, color="SAFE" , highlighted=True)
|
||||
self.fields["ogrepoIp"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.Textfield,
|
||||
value=self.get_repo_ip(),
|
||||
rely=3, # Línea siguiente
|
||||
relx=18,
|
||||
highlighted=True
|
||||
)
|
||||
}
|
||||
|
||||
# Campo para la IP de OgCore
|
||||
self.add(npyscreen.FixedText, value="IP de OgCore:", editable=False, rely=5, relx=2, color="SAFE" , highlighted=True)
|
||||
self.fields["ogcoreIp"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.Textfield,
|
||||
value=self.get_core_ip(),
|
||||
rely=6, # Línea siguiente
|
||||
relx=18,
|
||||
)
|
||||
}
|
||||
|
||||
# Campo para el Usuario Samba
|
||||
self.add(npyscreen.FixedText, value="Usuario Samba:", editable=False, rely=8, relx=2, color="SAFE" , highlighted=True)
|
||||
self.fields["sambaUser"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.Textfield,
|
||||
value="opengnsys",
|
||||
rely=9, # Línea siguiente
|
||||
relx=18 ,
|
||||
)
|
||||
}
|
||||
|
||||
# Campo para la Contraseña Samba
|
||||
self.fields["sambaUserPass"] = {
|
||||
"widget": self.add(
|
||||
npyscreen.TitlePassword,
|
||||
name="Contraseña Samba:",
|
||||
value="og",
|
||||
rely=11 # Mantener el uso de TitlePassword
|
||||
)
|
||||
}
|
||||
|
||||
class InstallationProgressForm(npyscreen.Form):
|
||||
"""Formulario para mostrar el progreso de instalación y el log en tiempo real."""
|
||||
def create(self):
|
||||
# Ajustar alturas para evitar problemas de espacio
|
||||
self.progress_box = self.add(
|
||||
npyscreen.BoxTitle,
|
||||
name="Progreso de instalación",
|
||||
max_height=6,
|
||||
rely=1,
|
||||
relx=2,
|
||||
scroll_exit=True
|
||||
)
|
||||
self.log_box = self.add(
|
||||
npyscreen.BoxTitle,
|
||||
name="Log de instalación",
|
||||
max_height=8,
|
||||
rely=8,
|
||||
relx=2,
|
||||
scroll_exit=True
|
||||
)
|
||||
|
||||
def update_progress(self, message, current=0, total=0):
|
||||
"""Actualiza el progreso de instalación en la parte superior."""
|
||||
if total > 0:
|
||||
# Crear una barra de progreso personalizada
|
||||
progress_percentage = int((current / total) * 100)
|
||||
bar_length = 30 # Longitud de la barra
|
||||
filled_length = int(bar_length * current // total)
|
||||
bar = f"[{'=' * filled_length}{' ' * (bar_length - filled_length)}] {progress_percentage}%"
|
||||
self.progress_box.values.append(bar)
|
||||
self.progress_box.values.append(message)
|
||||
self.progress_box.display()
|
||||
|
||||
def update_log(self, new_line):
|
||||
"""Actualiza el log en tiempo real en la parte inferior."""
|
||||
max_lines = 10 # Número máximo de líneas a mostrar
|
||||
self.log_box.values.append(new_line.strip()) # Agregar la nueva línea
|
||||
self.log_box.values = self.log_box.values[-max_lines:] # Mantener solo las últimas `max_lines` líneas
|
||||
self.log_box.display()
|
||||
|
||||
def install_components_with_ui(form, components, selected_tag):
|
||||
"""Instala los componentes seleccionados mostrando el progreso y el log en tiempo real."""
|
||||
log_file_path = os.path.join(LOGS_DIR, "installation.log")
|
||||
installed_packages = []
|
||||
failed_packages = []
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
with open(log_file_path, "w", buffering=1) as log_file:
|
||||
total_packages = len(components)
|
||||
def tail_log():
|
||||
try:
|
||||
with open(log_file_path, "r") as log_reader:
|
||||
log_reader.seek(0, os.SEEK_END)
|
||||
while True:
|
||||
line = log_reader.readline()
|
||||
if line:
|
||||
form.update_log(line)
|
||||
except Exception as e:
|
||||
# Mostrar error en log si ocurre
|
||||
try:
|
||||
form.update_log(f"[ERROR] {e}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
log_thread = threading.Thread(target=tail_log, daemon=True)
|
||||
log_thread.start()
|
||||
|
||||
for index, package in enumerate(components, start=1):
|
||||
form.update_progress(f"Instalando paquete {index}/{total_packages}: {package}", current=index, total=total_packages)
|
||||
install_command = f"DEBIAN_FRONTEND=noninteractive apt-get install -y {package}"
|
||||
process = subprocess.Popen(
|
||||
install_command, shell=True, text=True, stdout=log_file, stderr=log_file, bufsize=1
|
||||
)
|
||||
process.wait()
|
||||
log_file.flush()
|
||||
if process.returncode != 0:
|
||||
error_message = f"Error al instalar el paquete {package}. Consulta el archivo de registro: {log_file_path}"
|
||||
form.update_progress(error_message)
|
||||
failed_packages.append(package)
|
||||
else:
|
||||
form.update_progress(f"Paquete {package} instalado correctamente.")
|
||||
installed_packages.append(package)
|
||||
if package == "ogboot":
|
||||
form.update_progress("Instalando paquete adicional: ogclient")
|
||||
install_command = "DEBIAN_FRONTEND=noninteractive apt-get install -y ogclient"
|
||||
process = subprocess.Popen(
|
||||
install_command, shell=True, text=True, stdout=log_file, stderr=log_file, bufsize=1
|
||||
)
|
||||
process.wait()
|
||||
log_file.flush()
|
||||
if process.returncode != 0:
|
||||
error_message = f"Error al instalar el paquete ogclient. Consulta el archivo de registro: {log_file_path}"
|
||||
form.update_progress(error_message)
|
||||
failed_packages.append("ogclient")
|
||||
else:
|
||||
form.update_progress("Paquete ogclient instalado correctamente.")
|
||||
installed_packages.append("ogclient")
|
||||
except Exception as e:
|
||||
try:
|
||||
form.update_progress(f"Error durante la instalación: {e}")
|
||||
except Exception:
|
||||
print(f"[ERROR] {e}")
|
||||
failed_packages.append("Error general durante la instalación")
|
||||
|
||||
end_time = time.time()
|
||||
duration = end_time - start_time
|
||||
summary = "\n--- Resumen de la instalación ---\n"
|
||||
summary += f"Tiempo total de instalación: {duration:.2f} segundos\n"
|
||||
summary += f"Paquetes instalados correctamente: {len(installed_packages)}\n"
|
||||
for pkg in installed_packages:
|
||||
summary += f" - {pkg}\n"
|
||||
if failed_packages:
|
||||
summary += f"\nPaquetes que fallaron: {len(failed_packages)}\n"
|
||||
for pkg in failed_packages:
|
||||
summary += f" - {pkg}\n"
|
||||
else:
|
||||
summary += "\nTodos los paquetes se instalaron correctamente.\n"
|
||||
summary += f"\nConsulta el archivo de registro para más detalles: {log_file_path}"
|
||||
|
||||
# Mostrar el resumen y salir del formulario
|
||||
try:
|
||||
npyscreen.notify_confirm(summary, title="Resumen de la instalación", wide=True)
|
||||
except Exception as e:
|
||||
print(summary)
|
||||
print(f"[ERROR] {e}")
|
||||
|
||||
# Forzar la salida de la aplicación después del resumen
|
||||
import sys
|
||||
sys.exit(0)
|
||||
|
||||
class MyApp(npyscreen.NPSAppManaged):
|
||||
def onStart(self):
|
||||
# Inicializar variables globales
|
||||
self.installation_type = None # Tipo de instalación seleccionado (mononodo o multinodo)
|
||||
self.server_ip = None # IP del servidor para mononodo
|
||||
self.repo_ip = None # IP del servidor Repository
|
||||
self.dhcp_ip = None # IP del servidor DHCP
|
||||
self.core_ip = None # IP del servidor Core
|
||||
self.boot_ip = None # IP del servidor Bootdpkg
|
||||
self.selected_components = [] # Componentes seleccionados
|
||||
self.selected_tag = None # Versión seleccionada
|
||||
self.configurations = {} # Configuraciones de los componentes
|
||||
|
||||
# Registrar los formularios
|
||||
self.addForm("INSTALLATION_TYPE", InstallationTypeForm)
|
||||
self.addForm("MONONODO_CONFIG", MononodoConfigForm)
|
||||
self.addForm("MULTINODO_CONFIG", MultinodoConfigForm)
|
||||
self.addForm("MAIN", ComponentSelectionForm)
|
||||
self.addForm("ogcore", OgCoreForm)
|
||||
self.addForm("oggui", OgGuiForm)
|
||||
self.addForm("ogdhcp", OgDhcpForm)
|
||||
self.addForm("ogboot", OgBootForm)
|
||||
self.addForm("ogrepository", OgRepositoryForm)
|
||||
self.addForm("INSTALLATION_PROGRESS", InstallationProgressForm)
|
||||
|
||||
# Configurar el formulario inicial
|
||||
self.setNextForm("INSTALLATION_TYPE")
|
||||
|
||||
def generate_debconf(self):
|
||||
# Comprobar si la clave pública ya existe
|
||||
logging.debug("Entrando en generate_debconf")
|
||||
key_path = "/etc/apt/trusted.gpg.d/opengnsys.gpg"
|
||||
if os.path.exists(key_path):
|
||||
# Silenciar este mensaje
|
||||
pass
|
||||
else:
|
||||
# Añadir la clave pública
|
||||
try:
|
||||
logging.debug("Añadiendo la clave pública")
|
||||
subprocess.run(
|
||||
'curl -k -L https://ognproject.evlt.uma.es/debian-opengnsys/public.key | gpg --dearmour -o /etc/apt/trusted.gpg.d/opengnsys.gpg',
|
||||
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
logging.error("Error al añadir la clave pública")
|
||||
|
||||
return
|
||||
|
||||
# Añadir el repositorio
|
||||
try:
|
||||
logging.debug("Añadiendo el repositorio")
|
||||
selected_tag = self.selected_tag # Obtener el tag seleccionado
|
||||
# Determinar el valor de repo_line según el argumento recibido
|
||||
if len(sys.argv) > 1 and sys.argv[1].lower() == "devel":
|
||||
repo_line = f'deb http://ognproject.evlt.uma.es/debian-opengnsys/opengnsys-devel/{selected_tag} noble main'
|
||||
elif len(sys.argv) > 1 and sys.argv[1].lower() == "nightly":
|
||||
repo_line = f'deb http://ognproject.evlt.uma.es/debian-opengnsys/nightly/main noble main'
|
||||
else:
|
||||
repo_line = f'deb http://ognproject.evlt.uma.es/debian-opengnsys/opengnsys/{selected_tag} noble main'
|
||||
with open('/etc/apt/sources.list.d/opengnsys.list', 'w') as repo_file:
|
||||
repo_file.write(repo_line + '\n')
|
||||
except Exception:
|
||||
logging.error("Error al añadir el repositorio")
|
||||
print("Error al añadir el repositorio")
|
||||
return
|
||||
|
||||
# Crear el archivo de versión instalada
|
||||
try:
|
||||
logging.debug("Creando el archivo de versión instalada")
|
||||
os.makedirs("/opt/opengnsys", exist_ok=True)
|
||||
with open("/opt/opengnsys/release", "w") as release_file:
|
||||
release_file.write(f"Versión instalada: {selected_tag}\n")
|
||||
except Exception as e:
|
||||
print(f"Error al crear el archivo de versión: {e}")
|
||||
|
||||
# Actualizar los repositorios
|
||||
try:
|
||||
logging.debug("Actualizando los repositorios")
|
||||
subprocess.run('apt-get update', shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
|
||||
except subprocess.CalledProcessError:
|
||||
# Silenciar errores
|
||||
return
|
||||
|
||||
# Generar configuraciones para debconf
|
||||
output_file = os.path.join(CONFIGS_DIR, "configurations.txt")
|
||||
try:
|
||||
with open(output_file, "w") as f:
|
||||
f.write("\n--- Configuraciones para debconf-set-selections ---\n")
|
||||
for component, config in self.configurations.items():
|
||||
for key, value in config.items():
|
||||
field_type = "password" if "Pass" in key else "string"
|
||||
line = f'echo "{component} opengnsys/{component}_{key} {field_type} {value}" | debconf-set-selections\n'
|
||||
f.write(line)
|
||||
|
||||
# Ejecutar la línea directamente y redirigir la salida a /dev/null
|
||||
subprocess.run(
|
||||
line, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||
)
|
||||
logging.debug(f"Configuraciones guardadas en: {output_file}")
|
||||
except Exception:
|
||||
# Silenciar errores
|
||||
logging.error(f"Error al guardar configuraciones en {output_file}")
|
||||
print(f"Error al guardar configuraciones en {output_file}")
|
||||
|
||||
# Silenciar el mensaje de configuraciones guardadas
|
||||
# print(f"\nConfiguraciones guardadas en: {output_file}")
|
||||
|
||||
# Llamar al formulario de progreso
|
||||
form = self.getForm("INSTALLATION_PROGRESS")
|
||||
self.switchForm("INSTALLATION_PROGRESS")
|
||||
install_components_with_ui(form, self.selected_components, self.selected_tag)
|
||||
|
||||
def check_terminal_size(min_width=80, min_height=24):
|
||||
"""Verifica si el tamaño del terminal es suficiente."""
|
||||
terminal_size = shutil.get_terminal_size()
|
||||
if terminal_size.columns < min_width or terminal_size.lines < min_height:
|
||||
print(f"[ERROR] El tamaño del terminal es demasiado pequeño. Se requiere al menos {min_width}x{min_height}.")
|
||||
exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
# Verificar el tamaño del terminal antes de iniciar la aplicación
|
||||
check_terminal_size()
|
||||
|
||||
logging.debug("Ejecutando la aplicación principal")
|
||||
MyApp().run()
|
||||
except RuntimeError as e:
|
||||
print(f"[ERROR] {e}")
|
||||
logging.error(f"[ERROR] {e}")
|
||||
exit(1) # Salir con un código de error
|
||||
except KeyboardInterrupt:
|
||||
logging.warning("El programa fue interrumpido por el usuario (Ctrl-C)")
|
||||
except Exception as e:
|
||||
logging.error(f"[ERROR] Ocurrió un error inesperado: {e}")
|
||||
print(f"[ERROR] {e}")
|
||||
finally:
|
||||
# Asegurarse de que todos los mensajes de log se escriban en el archivo
|
||||
logging.debug("Finalizando el programa y cerrando el log")
|
||||
logging.shutdown()
|
||||
# Restaurar el terminal al estado normal
|
||||
npyscreen.wrapper_basic(lambda stdscr: None)
|
|
@ -0,0 +1,220 @@
|
|||
import curses
|
||||
import json
|
||||
import os
|
||||
from git import Repo
|
||||
|
||||
CONFIGS_DIR = "/tmp/oginstall"
|
||||
os.makedirs(CONFIGS_DIR, exist_ok=True)
|
||||
|
||||
REPO_URL = "https://ognproject.evlt.uma.es/gitea/opengnsys/ogcore.git"
|
||||
|
||||
def get_git_tags():
|
||||
try:
|
||||
repo_path = os.path.join(CONFIGS_DIR, "opengnsys_repo")
|
||||
if not os.path.exists(repo_path):
|
||||
print("Clonando el repositorio...")
|
||||
Repo.clone_from(REPO_URL, repo_path)
|
||||
else:
|
||||
print("Usando repositorio existente en", repo_path)
|
||||
|
||||
repo = Repo(repo_path)
|
||||
tags = [tag.name for tag in repo.tags]
|
||||
if tags:
|
||||
print("Tags encontrados:", tags)
|
||||
else:
|
||||
print("No se encontraron tags con el patrón especificado.")
|
||||
|
||||
return tags
|
||||
except Exception as e:
|
||||
print("Error al obtener los tags:", str(e))
|
||||
return []
|
||||
|
||||
def get_password(stdscr, y, x, prompt, default=""):
|
||||
stdscr.addstr(y, x, prompt, curses.color_pair(1))
|
||||
password = ""
|
||||
masked_password = ""
|
||||
|
||||
stdscr.move(y, x + len(prompt)) # Coloca el cursor después del prompt
|
||||
|
||||
while True:
|
||||
key = stdscr.getch()
|
||||
if key in (curses.KEY_BACKSPACE, 127): # Maneja el retroceso
|
||||
if len(password) > 0:
|
||||
password = password[:-1]
|
||||
masked_password = "*" * len(password)
|
||||
stdscr.move(y, x + len(prompt)) # Mueve el cursor después del prompt
|
||||
stdscr.addstr(y, x + len(prompt), " " * (len(masked_password) + 1)) # Borra la línea
|
||||
stdscr.addstr(y, x + len(prompt), masked_password) # Vuelve a mostrar los asteriscos actualizados
|
||||
elif key == ord("\n"): # Confirmar con Enter
|
||||
if not password and default: # Si el usuario no ingresó nada, usa el valor predeterminado
|
||||
password = default
|
||||
break
|
||||
elif 32 <= key <= 126: # Rango de caracteres imprimibles
|
||||
password += chr(key)
|
||||
masked_password = "*" * len(password)
|
||||
stdscr.addstr(y, x + len(prompt), masked_password) # Muestra asteriscos
|
||||
|
||||
return password
|
||||
|
||||
def get_input(stdscr, y, x, prompt, default=""):
|
||||
max_y, max_x = stdscr.getmaxyx()
|
||||
if x + len(prompt) >= max_x:
|
||||
raise ValueError("El prompt es demasiado largo para caber en la pantalla.")
|
||||
|
||||
stdscr.addstr(y, x, prompt, curses.color_pair(1))
|
||||
input_text = ""
|
||||
prompt_end_x = x + len(prompt) # Calcula la posición final del prompt
|
||||
stdscr.move(y, prompt_end_x) # Coloca el cursor después del prompt
|
||||
|
||||
while True:
|
||||
key = stdscr.getch()
|
||||
if key in (curses.KEY_BACKSPACE, 127): # Maneja el retroceso
|
||||
if len(input_text) > 0:
|
||||
input_text = input_text[:-1]
|
||||
stdscr.move(y, prompt_end_x) # Mueve el cursor después del prompt
|
||||
stdscr.clrtoeol() # Limpia la línea desde la posición actual hacia el final
|
||||
stdscr.addstr(y, prompt_end_x, input_text) # Vuelve a mostrar el texto actualizado
|
||||
stdscr.move(y, prompt_end_x + len(input_text))
|
||||
elif key == ord("\n"): # Confirmar con Enter
|
||||
if not input_text and default: # Usa el valor predeterminado si está vacío
|
||||
input_text = default
|
||||
break
|
||||
elif 32 <= key <= 126: # Rango de caracteres imprimibles
|
||||
if prompt_end_x + len(input_text) < max_x - 1:
|
||||
input_text += chr(key)
|
||||
stdscr.addstr(y, prompt_end_x, input_text) # Muestra el texto actualizado
|
||||
stdscr.move(y, prompt_end_x + len(input_text)) # Mueve el cursor al final del texto
|
||||
|
||||
return input_text
|
||||
|
||||
def main(stdscr):
|
||||
# Inicializar colores
|
||||
curses.start_color()
|
||||
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
|
||||
stdscr.bkgd(' ', curses.color_pair(1))
|
||||
|
||||
curses.curs_set(0)
|
||||
stdscr.clear()
|
||||
|
||||
# Paso 1: Seleccionar componentes
|
||||
components = ["ogCore", "ogGui", "ogDhcp", "ogBoot", "ogRepository"]
|
||||
selected_components = []
|
||||
current_index = 0
|
||||
|
||||
# Mostrar instrucciones y opciones de componentes
|
||||
stdscr.addstr(1, 2, "Selecciona los componentes (usa Flechas para navegar, Espacio para seleccionar, Enter para continuar):", curses.color_pair(1) | curses.A_BOLD)
|
||||
|
||||
while True:
|
||||
for idx, comp in enumerate(components):
|
||||
if comp in selected_components:
|
||||
stdscr.addstr(idx + 3, 4, f"[X] {comp}", curses.color_pair(1))
|
||||
else:
|
||||
stdscr.addstr(idx + 3, 4, f"[ ] {comp}", curses.color_pair(1))
|
||||
stdscr.addstr(current_index + 3, 4, f"> {components[current_index]}", curses.color_pair(1))
|
||||
|
||||
key = stdscr.getch()
|
||||
|
||||
if key == curses.KEY_UP and current_index > 0:
|
||||
current_index -= 1
|
||||
elif key == curses.KEY_DOWN and current_index < len(components) - 1:
|
||||
current_index += 1
|
||||
elif key == ord(" "):
|
||||
component = components[current_index]
|
||||
if component in selected_components:
|
||||
selected_components.remove(component)
|
||||
else:
|
||||
selected_components.append(component)
|
||||
elif key == ord("\n"):
|
||||
break
|
||||
|
||||
stdscr.refresh()
|
||||
|
||||
# Menu de selección de releases
|
||||
tags = get_git_tags()
|
||||
tag_index = 0
|
||||
|
||||
stdscr.clear()
|
||||
|
||||
while True:
|
||||
for idx, tag in enumerate(tags):
|
||||
if idx == tag_index:
|
||||
stdscr.addstr(idx + 3, 4, f"> {tag}", curses.color_pair(1))
|
||||
else:
|
||||
stdscr.addstr(idx + 3, 4, f" {tag}", curses.color_pair(1))
|
||||
|
||||
key = stdscr.getch()
|
||||
|
||||
if key == curses.KEY_UP and tag_index > 0:
|
||||
tag_index -= 1
|
||||
elif key == curses.KEY_DOWN and tag_index < len(tags) - 1:
|
||||
tag_index += 1
|
||||
elif key == ord("\n"):
|
||||
break
|
||||
|
||||
stdscr.refresh()
|
||||
|
||||
# Configuración específica de cada componente seleccionado
|
||||
curses.echo()
|
||||
for component in selected_components:
|
||||
stdscr.clear()
|
||||
stdscr.addstr(1, 2, f"Configuración para {component}:", curses.color_pair(1) | curses.A_BOLD)
|
||||
curses.curs_set(1)
|
||||
|
||||
config_data = {}
|
||||
if component == "ogCore":
|
||||
user = get_input(stdscr, 3, 0, "Usuario administrador (ogadmin): ", "ogadmin")
|
||||
password = get_password(stdscr, 4, 0, "Contraseña (por defecto '12345678'): ", "12345678")
|
||||
config_data = {"username": user, "password": password, "container_version": tags[tag_index]}
|
||||
|
||||
elif component == "ogGui":
|
||||
ogcore_ip = get_input(stdscr, 3, 0, "URL Api OgCore (https://127.0.0.1:8443): " , "https://127.0.0.1:8443")
|
||||
config_data = {"ogcore_ip": ogcore_ip, "container_version": tags[tag_index]}
|
||||
|
||||
elif component == "ogDhcp":
|
||||
ogbootIP = get_input(stdscr, 3, 0, "IP servidor de Boot (127.0.0.1): ", "127.0.0.1")
|
||||
ogdhcpIP = get_input(stdscr, 4, 0, "IP servidor de DHCP (127.0.0.1): ", "127.0.0.1")
|
||||
ogdhcpDir = get_input(stdscr, 5, 0, "Directorio de ogdhcp (/opt/opengnsys/ogdhcp): ", "/opt/opengnsys/ogdhcp")
|
||||
interfaces = get_input(stdscr, 6, 0, "Interfaces Boot (eth0,eth1): ", "eth0,eth1")
|
||||
json_array_interfaces = interfaces.split(",")
|
||||
config_data = {"ogbootIP": ogbootIP, "ogDhcpIP": ogdhcpIP , "ogDhcp_Dir" : ogdhcpDir , "interfaces": json_array_interfaces, "release": tags[tag_index]}
|
||||
|
||||
elif component == "ogBoot":
|
||||
ogcore_ip = get_input(stdscr, 3, 0, "ogCore Ip Server: ", "")
|
||||
ogboot_server_ip = get_input(stdscr, 4, 0, "ogBoot Server IP: ", "")
|
||||
ogcore_dir = get_input(stdscr, 5, 0, "ogCore Dir (/opt/opengnsys/ogboot): ", "/opt/opengnsys/ogboot")
|
||||
ogLive_default = get_input(stdscr, 6, 0, "ogLive por defecto (ogLive-noble-6.8.0-31-generic-amd64-r20241128.62778c9_20241129): ", "ogLive-noble-6.8.0-31-generic-amd64-r20241128.62778c9_20241129")
|
||||
ogboot_samba_user = get_input(stdscr, 7, 0, "ogBoot Samba User (opengnsys): ", "opengnsys")
|
||||
ogboot_samba_pass = get_password(stdscr, 8, 0, "ogBoot Samba Pass (og): ", "og")
|
||||
config_data = {
|
||||
"ogCore_ServerIP": ogcore_ip,
|
||||
"ogBoot_ServerIP": ogboot_server_ip,
|
||||
"ogBoot_Dir": ogcore_dir,
|
||||
"ogLive_Default": "https://ognproject.evlt.uma.es/oglive/" + ogLive_default + ".iso",
|
||||
"ogBootSambaUser": ogboot_samba_user,
|
||||
"ogBootSambaPass": ogboot_samba_pass,
|
||||
"release": tags[tag_index]
|
||||
}
|
||||
|
||||
elif component == "ogRepository":
|
||||
ogrepository_ip = get_input(stdscr, 3, 0, "ogRepository IP Server (127.0.0.1): ", "")
|
||||
ogrepository_samba_user = get_input(stdscr, 4, 0, "ogRepository Sambauser (opengnsys): ", "opengnsys")
|
||||
ogrepository_samba_pass = get_password(stdscr, 5, 0, "ogRepository Sambapass (og): ", "og")
|
||||
config_data = {
|
||||
"ogrepository_ip": ogrepository_ip,
|
||||
"ogrepository_samba_user": ogrepository_samba_user,
|
||||
"ogrepository_samba_pass": ogrepository_samba_pass,
|
||||
"release": tags[tag_index]
|
||||
}
|
||||
|
||||
# Guardar en archivo JSON
|
||||
config_file = os.path.join(CONFIGS_DIR, f"config_{component}.json")
|
||||
with open(config_file, "w") as f:
|
||||
json.dump(config_data, f)
|
||||
stdscr.clear()
|
||||
stdscr.addstr(2, 2, f"Configuración de {component} guardada en {config_file}", curses.color_pair(1))
|
||||
stdscr.refresh()
|
||||
stdscr.getch()
|
||||
|
||||
curses.noecho() # Desactivar el eco después de la entrada
|
||||
|
||||
curses.wrapper(main)
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Setup installer environment
|
||||
|
||||
|
||||
BRANCH=${BRANCH:-main}
|
||||
GIT_SSL_NO_VERIFY=1
|
||||
GIT_REPO="https://ognproject.evlt.uma.es/gitea/api/v1/repos/opengnsys/oginstaller/archive/$BRANCH.zip"
|
||||
export GIT_SSL_NO_VERIFY
|
||||
|
||||
|
||||
|
||||
install_packages() {
|
||||
apt-get update
|
||||
apt-get install -y curl jq unzip python3 python3-git
|
||||
}
|
||||
|
||||
download_installer() {
|
||||
|
||||
rm -f /tmp/oginstaller.zip
|
||||
rm -rf /tmp/oginstaller-$BRANCH
|
||||
rm -rf /tmp/oginstaller
|
||||
|
||||
curl -q -k $GIT_REPO -H 'accept: application/json' -o /tmp/oginstaller.zip
|
||||
unzip /tmp/oginstaller.zip -d /tmp
|
||||
mv /tmp/oginstaller /tmp/oginstaller-$BRANCH
|
||||
}
|
||||
|
||||
extract_installer() {
|
||||
rm -rf /tmp/oginstall
|
||||
mkdir -p /tmp/oginstall
|
||||
cp -r /tmp/oginstaller-$BRANCH/python-installer/* /tmp/oginstall/
|
||||
cp -r /tmp/oginstaller-$BRANCH/component-installer/* /tmp/oginstall/
|
||||
chmod 755 /tmp/oginstall/*.sh
|
||||
chmod 755 /tmp/oginstall/*.py
|
||||
}
|
||||
|
||||
create_questions() {
|
||||
echo "Creating questions..."
|
||||
python3 /tmp/oginstall/oginstaller.py
|
||||
}
|
||||
|
||||
launch_component_installer() {
|
||||
echo "Launching component installer..."
|
||||
/tmp/oginstall/component-installer.sh
|
||||
}
|
||||
|
||||
|
||||
install_packages
|
||||
download_installer
|
||||
extract_installer
|
||||
create_questions
|
||||
launch_component_installer
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import requests
|
||||
import sys
|
||||
import npyscreen
|
||||
import re
|
||||
import os
|
||||
|
||||
# Configuración general
|
||||
REPO_BASE_URL = "http://ognproject.evlt.uma.es/debian-opengnsys/opengnsys-devel"
|
||||
RELEASES_URL = "https://ognproject.evlt.uma.es/debian-opengnsys/versions-dev.json"
|
||||
APT_LIST_PATH = "/etc/apt/sources.list.d/opengnsys.list"
|
||||
PACKAGES = ["ogrepository", "ogcore", "oggui", "ogclient", "ogboot", "ogdhcp"]
|
||||
RELEASE_FILE = "/opt/opengnsys/release"
|
||||
|
||||
# === Sección npyscreen ===
|
||||
|
||||
class ServerURLForm(npyscreen.Form):
|
||||
def create(self):
|
||||
self.server_url = self.add(npyscreen.TitleText, name="Servidor de validación (URL completa):", value="http://localhost:5000/validar")
|
||||
|
||||
def afterEditing(self):
|
||||
self.parentApp.server_url = self.server_url.value
|
||||
self.parentApp.setNextForm("RELEASE")
|
||||
|
||||
class ReleaseSelectorForm(npyscreen.ActionForm):
|
||||
def create(self):
|
||||
self.releases = self.parentApp.releases
|
||||
self.listbox = self.add(npyscreen.TitleSelectOne,
|
||||
name="Releases disponibles",
|
||||
values=self.releases,
|
||||
scroll_exit=True,
|
||||
max_height=len(self.releases)+4)
|
||||
|
||||
def on_ok(self):
|
||||
selected_index = self.listbox.value[0] if self.listbox.value else None
|
||||
if selected_index is None:
|
||||
npyscreen.notify_confirm("Debes seleccionar una release antes de continuar.", title="Error")
|
||||
else:
|
||||
self.parentApp.selected = self.releases[selected_index]
|
||||
self.parentApp.setNextForm(None)
|
||||
|
||||
def on_cancel(self):
|
||||
npyscreen.notify_confirm("Operación cancelada. Saliendo del formulario.", title="Cancelado")
|
||||
self.parentApp.setNextForm(None)
|
||||
|
||||
class ReleaseSelectorApp(npyscreen.NPSAppManaged):
|
||||
def __init__(self, releases):
|
||||
self.releases = releases
|
||||
self.selected = None
|
||||
self.server_url = None
|
||||
super().__init__()
|
||||
|
||||
def onStart(self):
|
||||
self.addForm("MAIN", ServerURLForm, name="Configuración inicial")
|
||||
self.addForm("RELEASE", ReleaseSelectorForm, name="Selecciona una release", releases=self.releases)
|
||||
|
||||
def choose_release_and_server(releases):
|
||||
app = ReleaseSelectorApp(releases)
|
||||
app.run()
|
||||
return app.selected, app.server_url
|
||||
|
||||
# === Funciones principales ===
|
||||
|
||||
def backup_file(filepath):
|
||||
"""Crea una copia de seguridad del archivo especificado."""
|
||||
backup_path = f"{filepath}.bak"
|
||||
if os.path.exists(filepath):
|
||||
try:
|
||||
os.replace(filepath, backup_path)
|
||||
print(f"[INFO] Copia de seguridad creada: {backup_path}")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] No se pudo crear la copia de seguridad de {filepath}: {e}")
|
||||
return backup_path
|
||||
|
||||
def restore_file(backup_path, original_path):
|
||||
"""Restaura el archivo desde su copia de seguridad."""
|
||||
if os.path.exists(backup_path):
|
||||
try:
|
||||
os.replace(backup_path, original_path)
|
||||
print(f"[INFO] Archivo restaurado: {original_path}")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] No se pudo restaurar el archivo {original_path}: {e}")
|
||||
|
||||
def get_installed_packages():
|
||||
installed = []
|
||||
for pkg in PACKAGES:
|
||||
try:
|
||||
subprocess.run(
|
||||
["dpkg-query", "-W", pkg],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.DEVNULL,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
installed.append(pkg)
|
||||
except subprocess.CalledProcessError:
|
||||
continue
|
||||
return installed
|
||||
|
||||
def get_installed_release():
|
||||
try:
|
||||
with open(RELEASE_FILE, "r") as release_file:
|
||||
line = release_file.readline().strip()
|
||||
match = re.search(r".*:\s*(.+)", line)
|
||||
if match:
|
||||
return match.group(1).strip()
|
||||
except FileNotFoundError:
|
||||
print("El archivo de release no existe.")
|
||||
except Exception as e:
|
||||
print(f"Error al leer el archivo de release: {e}")
|
||||
return None
|
||||
|
||||
def fetch_available_releases():
|
||||
try:
|
||||
response = requests.get(RELEASES_URL)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
return data.get("versions", [])
|
||||
except requests.RequestException as e:
|
||||
print(f"[ERROR] No se pudo obtener la lista de releases: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
def update_repo_file(selected_release):
|
||||
backup_path = backup_file(APT_LIST_PATH)
|
||||
line = f"deb {REPO_BASE_URL}/{selected_release} noble main\n"
|
||||
print(f"[INFO] Escribiendo nueva línea en {APT_LIST_PATH}:\n{line.strip()}")
|
||||
try:
|
||||
with open(APT_LIST_PATH, "w") as f:
|
||||
f.write(line)
|
||||
except PermissionError:
|
||||
print("[ERROR] No tienes permisos para escribir en el archivo del repositorio. Ejecuta el script como root.")
|
||||
restore_file(backup_path, APT_LIST_PATH)
|
||||
sys.exit(1)
|
||||
|
||||
# Ejecutar apt update para actualizar la información del repositorio
|
||||
try:
|
||||
print("[INFO] Actualizando la información del repositorio con 'apt update'...")
|
||||
subprocess.run(["sudo", "apt", "update"], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"[ERROR] Error al ejecutar 'apt update': {e}")
|
||||
restore_file(backup_path, APT_LIST_PATH)
|
||||
sys.exit(1)
|
||||
|
||||
def check_compatibility(server_url, installed_release, selected_release):
|
||||
payload = {
|
||||
"installed_release": installed_release,
|
||||
"target_release": selected_release
|
||||
}
|
||||
try:
|
||||
response = requests.post(server_url, json=payload, timeout=5)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
return result.get("compatible", False), result.get("message", "")
|
||||
except requests.RequestException as e:
|
||||
print(f"[ERROR] No se pudo contactar con el servidor de validación: {e}")
|
||||
return False, str(e)
|
||||
|
||||
def summarize_updates(installed_packages, selected_release):
|
||||
"""Genera un resumen de los paquetes que se van a actualizar y los que no."""
|
||||
to_update = []
|
||||
up_to_date = []
|
||||
|
||||
for pkg in installed_packages:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["apt-cache", "policy", pkg],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.DEVNULL,
|
||||
text=True,
|
||||
check=True,
|
||||
env={"LANG": "C"} # Forzar el idioma a inglés
|
||||
)
|
||||
installed = None
|
||||
candidate = None
|
||||
for line in result.stdout.splitlines():
|
||||
if "Installed:" in line: # Siempre estará en inglés
|
||||
installed = line.split(":", 1)[1].strip()
|
||||
elif "Candidate:" in line: # Siempre estará en inglés
|
||||
candidate = line.split(":", 1)[1].strip()
|
||||
|
||||
if not installed or candidate == "(none)":
|
||||
to_update.append(f"{pkg} (no instalado o sin versión candidata)")
|
||||
elif installed != candidate:
|
||||
to_update.append(f"{pkg} ({installed} → {candidate})")
|
||||
else:
|
||||
up_to_date.append(f"{pkg} ({installed})")
|
||||
|
||||
except subprocess.CalledProcessError:
|
||||
to_update.append(f"{pkg} (error obteniendo versión)")
|
||||
|
||||
summary = "\n--- Resumen de actualización ---\n"
|
||||
summary += f"Release objetivo: {selected_release}\n\n"
|
||||
summary += "Paquetes que se actualizarán:\n"
|
||||
summary += "\n".join(f" - {line}" for line in to_update) if to_update else " - Ninguno\n"
|
||||
summary += "\nPaquetes que ya están actualizados:\n"
|
||||
summary += "\n".join(f" - {line}" for line in up_to_date) if up_to_date else " - Ninguno\n"
|
||||
summary += "\n--------------------------------"
|
||||
|
||||
# Mostrar el resumen en una ventana emergente
|
||||
npyscreen.notify_confirm(summary, title="Resumen de actualización", wide=True)
|
||||
|
||||
if not to_update:
|
||||
npyscreen.notify_confirm("[INFO] Todos los paquetes están actualizados. No es necesario continuar.", title="Información")
|
||||
sys.exit(0)
|
||||
|
||||
if not npyscreen.notify_yes_no("¿Deseas continuar con la actualización?", title="Confirmación"):
|
||||
npyscreen.notify_confirm("[INFO] Actualización cancelada por el usuario.", title="Cancelado")
|
||||
restore_file(f"{APT_LIST_PATH}.bak", APT_LIST_PATH)
|
||||
restore_file(f"{RELEASE_FILE}.bak", RELEASE_FILE)
|
||||
sys.exit(0)
|
||||
|
||||
return [line.split()[0] for line in to_update]
|
||||
|
||||
def show_final_versions(packages):
|
||||
print("\n✅ Resumen final de versiones instaladas:")
|
||||
for pkg in packages:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["dpkg-query", "-W", "-f=${Version}", pkg],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.DEVNULL,
|
||||
text=True
|
||||
)
|
||||
version = result.stdout.strip()
|
||||
print(f" - {pkg}: {version}")
|
||||
except subprocess.CalledProcessError:
|
||||
print(f" - {pkg}: no instalado")
|
||||
|
||||
def update_and_install(packages_to_update, selected_release):
|
||||
backup_release = backup_file(RELEASE_FILE)
|
||||
try:
|
||||
subprocess.run(["sudo", "apt", "update"], check=True)
|
||||
subprocess.run(["sudo", "apt", "install", "-y"] + packages_to_update, check=True)
|
||||
print("[INFO] Paquetes actualizados correctamente.")
|
||||
|
||||
# Actualizar el archivo de release con la versión seleccionada
|
||||
try:
|
||||
os.makedirs(os.path.dirname(RELEASE_FILE), exist_ok=True)
|
||||
with open(RELEASE_FILE, "w") as release_file:
|
||||
release_file.write(f"Versión instalada: {selected_release}\n")
|
||||
print(f"[INFO] Archivo de release actualizado: {selected_release}")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] No se pudo actualizar el archivo de release: {e}")
|
||||
|
||||
show_final_versions(packages_to_update)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"[ERROR] Error al instalar paquetes: {e}")
|
||||
restore_file(backup_release, RELEASE_FILE)
|
||||
sys.exit(1)
|
||||
|
||||
# === Entrada principal ===
|
||||
|
||||
def main():
|
||||
print("[INFO] Iniciando actualización de paquetes de OpenGnSys...")
|
||||
installed_release = get_installed_release()
|
||||
if installed_release:
|
||||
print(f"[INFO] Versión instalada: {installed_release}")
|
||||
else:
|
||||
print("[WARN] No se encontró la versión instalada.")
|
||||
sys.exit(1)
|
||||
|
||||
installed = get_installed_packages()
|
||||
if not installed:
|
||||
print("[ERROR] No se detectaron paquetes OpenGnSys instalados.")
|
||||
sys.exit(1)
|
||||
|
||||
releases = fetch_available_releases()
|
||||
selected, server_url = choose_release_and_server(releases)
|
||||
|
||||
if not selected or not server_url:
|
||||
print("[WARN] No se seleccionó release o URL del servidor. Restaurando archivos.")
|
||||
restore_file(f"{APT_LIST_PATH}.bak", APT_LIST_PATH)
|
||||
restore_file(f"{RELEASE_FILE}.bak", RELEASE_FILE)
|
||||
sys.exit(0)
|
||||
|
||||
print(f"[INFO] Validando compatibilidad con {server_url}...")
|
||||
compatible, message = check_compatibility(server_url, installed_release, selected)
|
||||
|
||||
if not compatible:
|
||||
print(f"[ERROR] El servidor indica que la actualización no es compatible: {message}")
|
||||
restore_file(f"{APT_LIST_PATH}.bak", APT_LIST_PATH)
|
||||
restore_file(f"{RELEASE_FILE}.bak", RELEASE_FILE)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f"[INFO] Compatibilidad validada: {message}")
|
||||
|
||||
try:
|
||||
update_repo_file(selected)
|
||||
to_update = summarize_updates(installed, selected)
|
||||
update_and_install(to_update, selected)
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Error durante la actualización: {e}")
|
||||
restore_file(f"{APT_LIST_PATH}.bak", APT_LIST_PATH)
|
||||
restore_file(f"{RELEASE_FILE}.bak", RELEASE_FILE)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except SystemExit as e:
|
||||
# Manejar la excepción SystemExit para evitar interrupciones
|
||||
if e.code != 0:
|
||||
print(f"[INFO] El script terminó con código de salida: {e.code}")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Ocurrió un error inesperado: {e}")
|
||||
finally:
|
||||
# Restaurar el terminal al estado normal
|
||||
npyscreen.wrapper_basic(lambda stdscr: None)
|
|
@ -0,0 +1,75 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Setup installer environment
|
||||
|
||||
|
||||
BRANCH=${BRANCH:-main}
|
||||
GIT_SSL_NO_VERIFY=1
|
||||
GIT_REPO="https://ognproject.evlt.uma.es/gitea/api/v1/repos/opengnsys/oginstaller/archive/$BRANCH.zip"
|
||||
export GIT_SSL_NO_VERIFY
|
||||
DEVEL=$1
|
||||
|
||||
if [ "$DEVEL" == "devel" ]; then
|
||||
INSTALL_DEVEL=1
|
||||
elif [ "$DEVEL" == "nightly" ]; then
|
||||
INSTALL_NIGHTLY=1
|
||||
else
|
||||
INSTALL_STABLE=1
|
||||
fi
|
||||
|
||||
install_packages() {
|
||||
apt-get update
|
||||
apt-get install -y curl jq unzip python3 python3-git
|
||||
}
|
||||
|
||||
|
||||
create_python_venv() {
|
||||
apt-get install -y python3-venv
|
||||
python3 -m venv /tmp/oginstall/venv
|
||||
source /tmp/oginstall/venv/bin/activate
|
||||
pip install -r /tmp/oginstall/requirements.txt
|
||||
}
|
||||
|
||||
download_installer() {
|
||||
|
||||
rm -f /tmp/oginstaller.zip
|
||||
rm -rf /tmp/oginstaller-$BRANCH
|
||||
rm -rf /tmp/oginstaller
|
||||
|
||||
curl -q -k $GIT_REPO -H 'accept: application/json' -o /tmp/oginstaller.zip
|
||||
unzip /tmp/oginstaller.zip -d /tmp
|
||||
mv /tmp/oginstaller /tmp/oginstaller-$BRANCH
|
||||
}
|
||||
|
||||
extract_installer() {
|
||||
rm -rf /tmp/oginstall
|
||||
mkdir -p /tmp/oginstall
|
||||
cp -r /tmp/oginstaller-$BRANCH/non_graf_installer/python-installer/* /tmp/oginstall/
|
||||
chmod 755 /tmp/oginstall/*.py
|
||||
}
|
||||
|
||||
create_questions() {
|
||||
echo "Creating questions..."
|
||||
if [ $INSTALL_DEVEL ] ; then
|
||||
python3 /tmp/oginstall/oginstaller-v3.py devel
|
||||
elif [ $INSTALL_NIGHTLY ] ; then
|
||||
python3 /tmp/oginstall/oginstaller-v3.py nightly
|
||||
else
|
||||
python3 /tmp/oginstall/oginstaller-v3.py
|
||||
fi
|
||||
deactivate
|
||||
}
|
||||
|
||||
clean_tmp() {
|
||||
rm -rf /tmp/oginstall
|
||||
rm -rf /tmp/oginstaller-$BRANCH
|
||||
rm -f /tmp/oginstaller.zip
|
||||
}
|
||||
|
||||
|
||||
install_packages
|
||||
download_installer
|
||||
extract_installer
|
||||
create_python_venv
|
||||
create_questions
|
||||
clean_tmp
|
|
@ -0,0 +1,70 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Setup installer environment
|
||||
|
||||
|
||||
BRANCH=${BRANCH:-main}
|
||||
GIT_SSL_NO_VERIFY=1
|
||||
GIT_REPO="https://ognproject.evlt.uma.es/gitea/api/v1/repos/opengnsys/oginstaller/archive/$BRANCH.zip"
|
||||
export GIT_SSL_NO_VERIFY
|
||||
|
||||
|
||||
|
||||
install_packages() {
|
||||
apt-get update
|
||||
apt-get install -y curl jq unzip python3 python3-git
|
||||
}
|
||||
|
||||
|
||||
create_python_venv() {
|
||||
apt-get install -y python3-venv
|
||||
python3 -m venv /tmp/oginstall/venv
|
||||
source /tmp/oginstall/venv/bin/activate
|
||||
pip install -r /tmp/oginstall/requirements.txt
|
||||
}
|
||||
|
||||
download_installer() {
|
||||
|
||||
rm -f /tmp/oginstaller.zip
|
||||
rm -rf /tmp/oginstaller-$BRANCH
|
||||
rm -rf /tmp/oginstaller
|
||||
|
||||
curl -q -k $GIT_REPO -H 'accept: application/json' -o /tmp/oginstaller.zip
|
||||
unzip /tmp/oginstaller.zip -d /tmp
|
||||
mv /tmp/oginstaller /tmp/oginstaller-$BRANCH
|
||||
}
|
||||
|
||||
extract_installer() {
|
||||
rm -rf /tmp/oginstall
|
||||
mkdir -p /tmp/oginstall
|
||||
cp -r /tmp/oginstaller-$BRANCH/non_graf_installer/python-installer/* /tmp/oginstall/
|
||||
cp -r /tmp/oginstaller-$BRANCH/non_graf_installer/component-installer/* /tmp/oginstall/
|
||||
chmod 755 /tmp/oginstall/*.sh
|
||||
chmod 755 /tmp/oginstall/*.py
|
||||
}
|
||||
|
||||
create_questions() {
|
||||
echo "Creating questions..."
|
||||
python3 /tmp/oginstall/oginstaller-v2.py
|
||||
deactivate
|
||||
}
|
||||
|
||||
launch_component_installer() {
|
||||
echo "Launching component installer..."
|
||||
/tmp/oginstall/component-installer.sh
|
||||
}
|
||||
|
||||
clean_tmp() {
|
||||
rm -rf /tmp/oginstall
|
||||
rm -rf /tmp/oginstaller-$BRANCH
|
||||
rm -f /tmp/oginstaller.zip
|
||||
}
|
||||
|
||||
|
||||
install_packages
|
||||
download_installer
|
||||
extract_installer
|
||||
create_python_venv
|
||||
create_questions
|
||||
launch_component_installer
|
||||
clean_tmp
|
|
@ -0,0 +1,4 @@
|
|||
GitPython
|
||||
npyscreen
|
||||
requests
|
||||
bs4
|
|
@ -0,0 +1,214 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Detect installed components.
|
||||
|
||||
|
||||
INSTALLER_BRANCH=${INSTALLER_BRANCH:-main}
|
||||
OPENGNSYS_BASE=/opt/opengnsys
|
||||
OPENGNSYS_COMPONENTS=(ogCore ogGui ogDhcp ogBoot ogRepository)
|
||||
GIT_SSL_NO_VERIFY=1
|
||||
INSTALLED_COMPONENTS=()
|
||||
|
||||
GIT_REPO="https://ognproject.evlt.uma.es/gitea/api/v1/repos/opengnsys/oginstaller/archive/$INSTALLER_BRANCH.zip"
|
||||
|
||||
export GIT_SSL_NO_VERIFY
|
||||
INSTALLED_COMPONENTS=()
|
||||
|
||||
check_os(){
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
# Just support Ubuntu 24.04 for now
|
||||
if [ $ID == "ubuntu" ] && [ $VERSION_ID == "24.04" ]; then
|
||||
echo "OS supported."
|
||||
else
|
||||
echo "OS not supported."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "OS not supported."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
detect_installed_components() {
|
||||
local OGNODE=0
|
||||
for component in "${OPENGNSYS_COMPONENTS[@]}"; do
|
||||
if [ -f "${OPENGNSYS_BASE}/${component}/installer/config.json" ]; then
|
||||
echo "Component $component is installed."
|
||||
INSTALLED_COMPONENTS+=($component)
|
||||
OGNODE=1
|
||||
else
|
||||
echo "Component $component is not installed."
|
||||
fi
|
||||
done
|
||||
if [ $OGNODE -eq 0 ]; then
|
||||
echo "No OpenGnsys components installed."
|
||||
else
|
||||
echo "Installed components:" "${INSTALLED_COMPONENTS[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Assume taht all components are at the same release version, get the first installed compoenent and return its version
|
||||
get_og_installed_version() {
|
||||
local component=$1
|
||||
local version=$(jq -r '.release' ${OPENGNSYS_BASE}/${component}/installer/config.json)
|
||||
echo $version
|
||||
}
|
||||
|
||||
start_stop_component() {
|
||||
local component=$1
|
||||
local action=$2
|
||||
case $component in
|
||||
ogCore)
|
||||
handle_ogcore $action
|
||||
;;
|
||||
ogGui)
|
||||
handle_oggui $action
|
||||
;;
|
||||
ogDhcp)
|
||||
handle_ogdhcp $action
|
||||
;;
|
||||
ogBoot)
|
||||
handle_ogboot $action
|
||||
;;
|
||||
ogRepository)
|
||||
handle_ogrepository $action
|
||||
;;
|
||||
*)
|
||||
echo "Component $component not found."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
stop_installed_services() {
|
||||
echo "Stopping services..."
|
||||
for component in "${INSTALLED_COMPONENTS[@]}"; do
|
||||
echo "Stopping component $component..."
|
||||
start_stop_component $component stop
|
||||
done
|
||||
}
|
||||
|
||||
start_installed_services() {
|
||||
echo "Starting services..."
|
||||
for component in "${INSTALLED_COMPONENTS[@]}"; do
|
||||
echo "Starting component $component..."
|
||||
start_stop_component $component start
|
||||
done
|
||||
}
|
||||
|
||||
handle_ogboot() {
|
||||
case $1 in
|
||||
stop)
|
||||
echo "Stopping ogBoot..."
|
||||
systemctl stop nginx
|
||||
systemctl stop tftpd-hpa
|
||||
systemctl stop smbd
|
||||
systemctl stop nmbd
|
||||
;;
|
||||
start)
|
||||
echo "Starting ogBoot..."
|
||||
systemctl start nginx
|
||||
systemctl start tftpd-hpa
|
||||
systemctl start smbd
|
||||
systemctl start nmbd
|
||||
;;
|
||||
*)
|
||||
echo "Invalid action."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
handle_ogdhcp() {
|
||||
case $1 in
|
||||
stop)
|
||||
echo "Stopping ogDhcp..."
|
||||
systemctl stop kea-dhcp4-server
|
||||
systemctl stop kea-ctrl-agent
|
||||
;;
|
||||
start)
|
||||
echo "Starting ogDhcp..."
|
||||
systemctl start kea-dhcp4-server
|
||||
systemctl start kea-ctrl-agent
|
||||
;;
|
||||
*)
|
||||
echo "Invalid action."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
handle_ogrepository() {
|
||||
case $1 in
|
||||
stop)
|
||||
echo "Stopping ogRepository..."
|
||||
systemctl stop smbd
|
||||
systemctl stop nmbd
|
||||
systemctl stop ogrepo-api
|
||||
;;
|
||||
start)
|
||||
echo "Starting ogRepository..."
|
||||
systemctl start smbd
|
||||
systemctl start nmbd
|
||||
systemctl start ogrepo-api
|
||||
;;
|
||||
*)
|
||||
echo "Invalid action."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
handle_ogcore() {
|
||||
case $1 in
|
||||
stop)
|
||||
echo "Stopping ogCore..."
|
||||
systemctl stop ogcore
|
||||
;;
|
||||
start)
|
||||
echo "Starting ogCore..."
|
||||
systemctl start ogcore
|
||||
;;
|
||||
*)
|
||||
echo "Invalid action."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
handle_oggui() {
|
||||
case $1 in
|
||||
stop)
|
||||
echo "Stopping ogGui..."
|
||||
systemctl stop oggui-app
|
||||
;;
|
||||
start)
|
||||
echo "Starting ogGui..."
|
||||
systemctl start oggui-app
|
||||
;;
|
||||
*)
|
||||
echo "Invalid action."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
update_installed_components() {
|
||||
local version=$1
|
||||
echo "Updating components to version $version..."
|
||||
for component in "${INSTALLED_COMPONENTS[@]}"; do
|
||||
echo "Updating component $component..."
|
||||
update_component $component $version
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
#### Main
|
||||
|
||||
check_os
|
||||
detect_installed_components
|
||||
installed_version=$(get_og_installed_version "${INSTALLED_COMPONENTS[0]}")
|
||||
select_version_to_update
|
||||
stop_installed_services
|
||||
update_installed_components $installed_version
|
||||
start_installed_services
|
||||
|
||||
|