This commit is contained in:
emdee 2024-01-01 01:04:40 +00:00
parent eaf6ffdbef
commit c417a6f3f9
29 changed files with 16625 additions and 608 deletions

View File

@ -28,14 +28,6 @@ VERBOSE=2
all: install lint build check run test
# groddy but works for me
install::
# ( /usr/local/src ; ansible-galaxy collection install \
# file:///usr/local/src/community.general )
[ -e $(ANSIBLE_PLUGINS)/connection/libvirt_qemu.py ] \
|| ln -s ${PWD}/lib/plugins/libvirt_qemu.py \
$(ANSIBLE_PLUGINS)/connection/q || true
lint::
@sudo xmllint -noout roles/ansible-gentoo_install/templates/etc/libvirt/qemu/gentoo.xml
@yamllint -c .yamllint.yml -f standard *.yml roles/*/*s/*yml 2>&1| \
@ -44,18 +36,29 @@ lint::
grep -B 2 error | tee .yamllint.err || true
grep Error .yamllint.out || true
build:: build_base
# groddy but works for me
install:: lint
# ( /usr/local/src ; ansible-galaxy collection install \
# file:///usr/local/src/community.general )
[ -e $(ANSIBLE_PLUGINS)/connection/libvirt_qemu.py ] \
|| ln -s ${PWD}/lib/plugins/libvirt_qemu.py \
$(ANSIBLE_PLUGINS)/connection/q || true
@[ -f ${BOX_NBD_BASE_QCOW} ] || { \
echo ERROR: not created BOX_NBD_DEV="${BOX_NBD_DEV}" - use ; \
echo qemu-img create -f qcow2 "${BOX_NBD_BASE_QCOW}" 20G ; \
exit 2 ; }
@( ps ax | grep -v grep | \
grep "qemu-nbd.*/dev/nbd.*${BOX_NBD_BASE_QCOW}" ) || { \
echo ERROR: not mounted BOX_NBD_DEV="${BOX_NBD_DEV}" - use ; \
echo qemu-nbd -n -f qcow2 -c /dev/nbd1 ${BOX_NBD_BASE_QCOW} ;\
exit 1 ; }
build::
sudo $(MAKE) -$(MAKEFLAGS) build_base
sudo $(MAKE) -$(MAKEFLAGS) build_overlay
build_base:: lint
build_base:: install
echo INFO: $@ "${BOX_NBD_BASE_QCOW}"
@[ ! -f ${BOX_NBD_BASE_QCOW} ] || { \
echo WARN looks like theres already a build of \
"${BOX_NBD_BASE_QCOW}" ; exit 2 ; }
@( ! ps ax | grep -v grep | \
grep "qemu-nbd.*/dev/nbd.*${BOX_NBD_BASE_QCOW}" ) ||{ \
echo WARN looks like theres an active nbd mount of \
"${BOX_NBD_BASE_QCOW}" && exit 1 ; }
echo INFO running the toxcore role will build ${BOX_NBD_BASE_QCOW}
sudo sh ansible_local.bash --diff -i ${PWD}/hosts.yml \
-l ${LOCALHOST} -c local --verbose ${VERBOSE} \

View File

@ -77,26 +77,36 @@ the hosts.yml file from the host called gentoo1 in the linux_libvirt_group.
There are 3 ansible roles:
1. base : The base role sets up the basics and is required to be run.
It sets up the essential parameters to run roles on the host or client.
Check the settings in roles/base/defaults/main.yml before running the role.
2. proxy : The proxy role sets up the networking with proxies,
and is required to be run, even if you don't use a proxy.
It sets proxying and installs basic packages on the host or client.
Check the settings in roles/proxy/defaults/main.yml before running the role.
3. toxcore :
This role sets up the software to run libvirt on the host.
Check the settings in roles/toxcore/defaults/main.yml before running the role.
In addition, toxcore calls an included role ansible-gentoo_install.
This is an updated version of the abandonned
https://github.com/agaffney/ansible-gentoo_install/ This role,
when run on the host, builds the Gentoo base qcow image. As a safety
feature, you must create the qcow2 image and activate it with:
The host creates the base qcow2 image and then creates the overlay
image. When both are created, it install Tox software on the host and
client.
In addition, toxcore calls an included role ansible-gentoo_install.
This is an updated version of the abandonned
https://github.com/agaffney/ansible-gentoo_install/ This role,
when run on the host, builds the Gentoo base qcow image. As a safety
feature, you must create the qcow2 image and activate it with:
modprobe nbd
qemu-img $BOX_NBD_BASE_QCOW 20G
qemu-nbd -c $BOX_NBD_DEV $BOX_NBD_BASE_QCOW
modprobe nbd
qemu-img $BOX_NBD_BASE_QCOW 20G
qemu-nbd -c $BOX_NBD_DEV $BOX_NBD_BASE_QCOW
and put these values into the hosts.yml file in the pentoo or devuan
target, depending on your host operating system. The filesytem that
holds base qcow2 $BOX_NBD_BASE_QCOW must have at least 12G available,
and may grow to almost 20G.
and put these values into the hosts.yml file in the pentoo or devuan
target, depending on your host operating system. The filesytem that
holds base qcow2 $BOX_NBD_BASE_QCOW must have at least 12G available,
and may grow to almost 20G.
After you have finished building the base qcow2 image, you will want
to dismount it with qemu-nbd -d $BOX_NBD_DEV. Be careful and look

View File

@ -259,7 +259,7 @@ all:
# for a non-root login: ansible_ssh_extra_args: "--userspec=foo:adm"
vars: # linux_unix_group
# toxcore
BOX_NBD_DEV: nbd3
BOX_NBD_DEV: nbd1
BOX_NBD_MP: /mnt/gentoo
BOX_NBD_OVERLAY_NAME: "gentoo1"
BOX_NBD_FILES: "/i/data/Agile/tmp/Topics/GentooImgr"

View File

@ -163,7 +163,8 @@ def run_module():
action=dict(type='str', required=True),
loglevel=dict(type='int', required=False, default=logging.INFO),
threads=dict(type='int', required=False, default=1),
config=dict(type='path', default=def_config, required=True),
# Module error: required and default are mutually exclusive for config
config=dict(type='path', default=def_config),
profile=dict(type='str', required=False),
kernel_dir=dict(type='path', required=False),
portage=dict(type='path', required=False),

View File

@ -1,183 +0,0 @@
# -*- mode: yaml; indent-tabs-mode: nil; tab-width: 2; coding: utf-8-unix -*-
---
- name: "DEBUG: ansible-gentoo_install local"
debug:
verbosity: 0
msg: "DEBUG: ansible-gentoo_install local BOX_NBD_DEV={{BOX_NBD_DEV}}"
check_mode: no
- assert:
that:
- "'{{BOX_NBD_DEV}}' != ''"
when: ansible_connection in ['local', 'chroot']
- set_fact:
AGI_use_local_kernel: true
when:
- ansible_distribution == 'Gentoo' or BOX_GENTOO_FROM_MP not in ['/', '']
- set_fact:
AGI_PROXY_MODE: "{{PROXY_MODE|default('')}}"
when:
- PROXY_MODE|default('') != ''
check_mode: no
- set_fact:
AGI_PROXY_MODE: "{{BOX_PROXY_MODE|default('')}}"
when:
- AGI_PROXY_MODE == ''
check_mode: no
- block:
- name: check for mounted disk
shell: |
grep '/dev/{{AGI_NBD_DEV}}' /proc/mounts && exit 0
ps ax | grep -v grep | \
grep "qemu-nbd.*/dev/nbd.*{{BOX_NBD_BASE_QCOW}}" && \
echo WARN looks like theres an active nbd mount of \
"${BOX_NBD_BASE_QCOW}" && exit 1
exit 2
failed_when: false
changed_when: false
register: check_mounted_disk
check_mode: no
- name: partition if disk not mounted
fail:
msg: "looks like theres an active nbd mount of {{BOX_NBD_BASE_QCOW}}"
when:
- check_mounted_disk.rc == 1
check_mode: no
- name: partition if disk not mounted or active
include: disk.yml
when:
- check_mounted_disk.rc > 1
check_mode: no
- name: mount root partition
mount:
name: "{{AGI_NBD_MP}}"
src: "{{ AGI_install_disk }}p3"
fstype: ext4
state: mounted
check_mode: false
- name: create /boot mountpoint
file:
path: "{{AGI_NBD_MP}}/boot"
state: directory
check_mode: false
- name: mount boot partition
mount:
name: "{{AGI_NBD_MP}}/boot"
src: "{{ AGI_install_disk }}p1"
fstype: ext2
state: mounted
check_mode: false
- include: tarball.yml
- include: copy.yml
when: AGI_use_local_kernel
- name: mount distfiles
delegate_to: localhost
shell: |
[ -d "{{MOUNT_GENTOO_DISTFILES_ARCHIVES}}" ] || exit 1
grep {{MOUNT_GENTOO_DISTFILES_ARCHIVES}} /proc/mounts && exit 0
[ -d {{AGI_NBD_MP}}/usr/portage/ ] || exit 0
[ -d {{AGI_NBD_MP}}/usr/portage/distfiles ] || mkdir {{AGI_NBD_MP}}/usr/portage/distfiles
mount --bind {{MOUNT_GENTOO_DISTFILES_ARCHIVES}} {{AGI_NBD_MP}}/usr/portage/distfiles
when:
- "MOUNT_GENTOO_DISTFILES_ARCHIVES != ''"
- "AGI_NBD_MP != ''"
- include: chroot.yml
delegate_to: localhost
when: ansible_connection in ['chroot', 'local'] # libvirt?
- block:
- name: check chroot wrapper installed
shell: |
[ -x /var/tmp/chroot_wrapper.sh ] || exit 1
df /mnt/gentoo || exit 2
/var/tmp/chroot_wrapper.sh /bin/df | grep /mnt/gentoo && exit 4
exit 0
register: chroot_out
check_mode: false
- name: enable chroot wrapper
set_fact:
ansible_shell_executable: /var/tmp/chroot_wrapper.sh
old_ansible_python_interpreter: "{{ansible_python_interpreter}}"
ansible_python_interpreter: "/usr/bin/python3"
check_mode: false
when: ansible_connection in ['local']
- block:
- include: portage.yml
- include: misc.yml
- include: network.yml
- include: kernel.yml
when: not AGI_use_local_kernel
- include: bootloader.yml
- include: daemons.yml
# - include: finish.yml
check_mode: false
when:
- "ansible_connection in ['chroot'] or (ansible_connection in ['local'] or and chroot_out.rc|default(1) == 0)"
rescue:
- debug:
msg: "ERROR: error during chroot execution"
- name: disable chroot wrapper
set_fact:
ansible_shell_executable: /bin/sh
ansible_python_interpreter: "{{old_ansible_python_interpreter}}"
when:
- "ansible_connection in ['local'] and chroot_out.rc|default(1) == 0"
check_mode: false
- name: unmount filesystems
mount:
name: "{{AGI_NBD_MP}}/{{ item }}"
state: unmounted
with_items:
- proc
- sys
- dev/pts
- dev/shm
- dev
- boot
- ''
loop_control:
label: "{{AGI_NBD_MP}}/{{ item }}"
when:
- "ansible_connection in ['local'] and chroot_out.rc|default(1) == 0"
- false # leave it mounted for testing
- name: dismount any other mounts
shell: |
if [ -z "{{MOUNT_GENTOO_DISTFILES_ARCHIVES}}" ] && \
[ -d "{{MOUNT_GENTOO_DISTFILES_ARCHIVES}}" ] && \
grep {{MOUNT_GENTOO_DISTFILES_ARCHIVES}} /proc/mounts ; then
umount {{MOUNT_GENTOO_DISTFILES_ARCHIVES}}
fi
df -a | grep "{{AGI_NBD_MP}}" | sed -e 's/.* //' | tac | while read elt;do
umount $elt
done
base_chroot_unbind.bash "{{AGI_NBD_MP}}"
when:
- "ansible_connection in ['chroot'] or chroot_out.rc|default(1) == 0"
- false # leave it mounted for testing

View File

@ -39,12 +39,12 @@
- name: copy kernel sources
copy:
src: "{{AGI_GENTOO_FROM_MP}}/usr/src/{{kernel_out.stdout}}"
dest: "{{AGI_NBD_MP}}/usr/src"
dest: "{{AGI_NBD_MP}}/usr/src/{{kernel_out.stdout}}"
remote_src: no
creates: "{{AGI_NBD_MP}}/usr/src/{{kernel_out.stdout}}"
when:
- kernel_out.rc|default(1) == 0
- AGI_use_local_kernel
- false
- name: resolve kver
shell: |
@ -99,6 +99,8 @@
when:
- AGI_use_local_kernel
- ramfs_out.rc|default(1) == 0
- false
ignore_errors: true
- name: make directories
shell: |
@ -111,6 +113,6 @@
[ -f "{{AGI_NBD_MP}}/$file" ] && continue
cp -np "$file" "{{AGI_NBD_MP}}/$file"
done
ignore_errors: false
ignore_errors: true
# dracut

View File

@ -32,12 +32,19 @@
- name: check for mounted disk
shell: |
grep '/dev/{{AGI_NBD_DEV}}' /proc/mounts && exit 0
ps ax | grep -v grep | \
grep "qemu-nbd.*/dev/nbd.*{{BOX_NBD_BASE_QCOW}}" && \
echo WARN looks like theres an active nbd mount of \
"${BOX_NBD_BASE_QCOW}" && exit 1
exit 2
grep '/dev/{{AGI_NBD_DEV}}p3' /proc/mounts && exit 0
if [ ! -f "{{BOX_NBD_BASE_QCOW}}" ] ; then
echo ERROR: not created BOX_NBD_DEV="{{BOX_NBD_DEV}}" - use
echo qemu-img create -f qcow2 "{{BOX_NBD_BASE_QCOW}}" 20G
exit 1
fi
if ! ps ax | grep -v grep | \
grep "qemu-nbd.*{{AGI_NBD_DEV}}.*{{BOX_NBD_BASE_QCOW}}" ; then
echo ERROR: not mounted BOX_NBD_DEV="{{BOX_NBD_DEV}}" - use
echo qemu-nbd -n -f qcow2 -c /dev/nbd1 {{BOX_NBD_BASE_QCOW}}
exit 2
fi
exit 3
failed_when: false
changed_when: false
register: check_mounted_disk
@ -45,15 +52,15 @@
- name: partition if disk not mounted
fail:
msg: "looks like theres an active nbd mount of {{BOX_NBD_BASE_QCOW}}"
msg: "make and nbd mount {{BOX_NBD_BASE_QCOW}}"
when:
- check_mounted_disk.rc == 1
- check_mounted_disk.rc in [2, 1]
check_mode: no
- name: partition if disk not mounted or active
include: disk.yml
when:
- check_mounted_disk.rc > 1
- check_mounted_disk.rc > 2
check_mode: no
- name: mount root partition

View File

@ -52,6 +52,8 @@
fi
if ! grep -q "{{BOX_NBD_DEV}}" /proc/partitions ; then
echo ERROR: not mounted BOX_NBD_DEV="{{BOX_NBD_DEV}}" - use qemu-nbd
echo qemu-img create -f qcow2 /a/tmp/GentooImgr/gentoo.qcow2 20G
echo qemu-nbd -n -f qcow2 -c /dev/nbd1 /a/tmp/GentooImgr/gentoo.qcow
exit 2
fi
exit 0
@ -71,19 +73,27 @@
fi
fi
# should operate on json with jq
# "imgsize": "20G",
# "memory": 4096,
# "mountpoint": "/mnt/gentoo",
# "imagename": null,
# "initsys": "openrc",
# "iso": null,
# "portage": null,
# "stage3": null,
# "partition": 1
if [ -f "$tofile" ] ; then
# "imgsize": "20G",
sed -i -e 's@"imgsize": ".*"@"imgsize": "{{BOX_NBD_OVERLAY_GB}}"@' $tofile
# "memory": 4096,
sed -i -e 's@"memory": ".*"@"imgsize": "{{BOX_NBD_OVERLAY_RAM}}"@' $tofile
# "mountpoint": "/mnt/gentoo",
sed -i -e 's@"mountpoint": ".*"@"mountpoint": "{{BOX_NBD_MP}}"@' $tofile
# "imagename": null,
sed -i -e 's@"imagename": ".*"@"imagename": "{{BOX_NBD_OVERLAY_NAME}}"@' $tofile
# "initsys": "openrc",
sed -i -e 's@"initsys": ".*"@"initsys: "{{BOX_NBD_BASE_PROFILE}}"@' $tofile
# "iso": null,
# "portage": null,
sed -i -e 's@""portage": ".*"@""portage: "{{BOX_NBD_PORTAGE_FILE}}"@' $tofile
# "stage3": null,
sed -i -e 's@""stage3": ".*"@""stage3: "{{BOX_NBD_STAGE3_FILE}}"@' $tofile
# "partition": 1
sed -i -e 's@"partition": ".*"@"partition": 3@' $tofile
# but this is crucial
# "disk": "/dev/sda",
sed -i -e 's@"disk": ".*"@"disk": "'{{BOX_NBD_DEV}}'"@' $tofile
sed -i -e 's@"disk": ".*"@"disk": "{{BOX_NBD_DEV}}"@' $tofile
grep {{BOX_NBD_DEV}} $tofile || exit 4
fi

View File

@ -0,0 +1,193 @@
<!--
WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
OVERWRITTEN AND LOST. Changes to this xml configuration should be made using:
virsh edit gentoo
or other application using the libvirt API.
-->
<domain type='kvm'>
<name>gentoo</name>
<uuid>1362f7fd-ea55-4f2f-91c5-3b61ec26c1a0</uuid>
<metadata>
<libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
<libosinfo:os id="http://gentoo.org/gentoo/rolling"/>
</libosinfo:libosinfo>
</metadata>
<memory unit='KiB'>3121152</memory>
<currentMemory unit='KiB'>2097152</currentMemory>
<memoryBacking>
<source type='memfd'/>
<access mode='shared'/>
</memoryBacking>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='x86_64' machine='pc-q35-8.1'>hvm</type>
<boot dev='hd'/>
</os>
<features>
<acpi/>
<apic/>
<vmport state='off'/>
</features>
<cpu mode='host-passthrough' check='none' migratable='on'/>
<clock offset='utc'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
<timer name='hpet' present='no'/>
</clock>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<pm>
<suspend-to-mem enabled='no'/>
<suspend-to-disk enabled='no'/>
</pm>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/var/lib/libvirt/images/gentoo1.qcow2'/>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
</disk>
<disk type='file' device='cdrom'>
<driver name='qemu' type='raw'/>
<target dev='sda' bus='sata'/>
<readonly/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>
<controller type='usb' index='0' model='qemu-xhci' ports='15'>
<address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
</controller>
<controller type='pci' index='0' model='pcie-root'/>
<controller type='pci' index='1' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='1' port='0x10'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
</controller>
<controller type='pci' index='2' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='2' port='0x11'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
</controller>
<controller type='pci' index='3' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='3' port='0x12'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
</controller>
<controller type='pci' index='4' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='4' port='0x13'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
</controller>
<controller type='pci' index='5' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='5' port='0x14'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
</controller>
<controller type='pci' index='6' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='6' port='0x15'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
</controller>
<controller type='pci' index='7' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='7' port='0x16'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x6'/>
</controller>
<controller type='pci' index='8' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='8' port='0x17'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x7'/>
</controller>
<controller type='pci' index='9' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='9' port='0x18'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0' multifunction='on'/>
</controller>
<controller type='pci' index='10' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='10' port='0x19'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x1'/>
</controller>
<controller type='pci' index='11' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='11' port='0x1a'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x2'/>
</controller>
<controller type='pci' index='12' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='12' port='0x1b'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x3'/>
</controller>
<controller type='pci' index='13' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='13' port='0x1c'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x4'/>
</controller>
<controller type='pci' index='14' model='pcie-root-port'>
<model name='pcie-root-port'/>
<target chassis='14' port='0x1d'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x5'/>
</controller>
<controller type='sata' index='0'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
</controller>
<controller type='virtio-serial' index='0'>
<address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
</controller>
<interface type='network'>
<mac address='52:54:00:be:61:e1'/>
<source network='Whonix-External'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</interface>
<serial type='pty'>
<target type='isa-serial' port='0'>
<model name='isa-serial'/>
</target>
</serial>
<console type='pty'>
<target type='serial' port='0'/>
</console>
<channel type='unix'>
<target type='virtio' name='org.qemu.guest_agent.0'/>
<address type='virtio-serial' controller='0' bus='0' port='1'/>
</channel>
<channel type='spicevmc'>
<target type='virtio' name='com.redhat.spice.0'/>
<address type='virtio-serial' controller='0' bus='0' port='2'/>
</channel>
<input type='tablet' bus='usb'>
<address type='usb' bus='0' port='1'/>
</input>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<graphics type='spice' autoport='yes'>
<listen type='address'/>
<image compression='off'/>
</graphics>
<sound model='ich9'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
</sound>
<audio id='1' type='spice'/>
<video>
<model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
</video>
<redirdev bus='usb' type='spicevmc'>
<address type='usb' bus='0' port='2'/>
</redirdev>
<redirdev bus='usb' type='spicevmc'>
<address type='usb' bus='0' port='3'/>
</redirdev>
<watchdog model='itco' action='reset'/>
<memballoon model='virtio'>
<address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
</memballoon>
<rng model='virtio'>
<backend model='random'>/dev/urandom</backend>
<address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
</rng>
</devices>
</domain>

View File

@ -0,0 +1,92 @@
#!/bin/bash
# -*- mode: sh; fill-column: 75; tab-width: 8; coding: utf-8-unix -*-
# from https://github.com/earlruby/create-vm/
[ -f /usr/local/bin/usr_local_tput.bash ] && \
. /usr/local/bin/usr_local_tput.bash || {
DBUG() { echo DEBUG $* ; }
INFO() { echo INFO $* ; }
WARN() { echo WARN $* ; }
ERROR() { echo ERROR $* ; }
}
prog=`basename $0 .bash`
PREFIX=/usr/local
ROLE=toxcore
BOX=gentoo
export BASE_SRC_ANSIBLE=/o/var/local/src/play_tox
yamllint -c $BASE_SRC_ANSIBLE/.yamllint.rc $BASE_SRC_ANSIBLE/hosts.yml|| {
ERROR
exit 1
}
# put these values in $BASE_SRC_ANSIBLE/hosts.yml
[ -n "$BOX_NBD_OVERLAY_NAME" ] || \
BOX_NBD_OVERLAY_NAME=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_OVERLAY_NAME $BOX)
[ -n "$BOX_NBD_BASE_PUBKEY" ] || \
BOX_NBD_BASE_PUBKEY=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_BASE_PUBKEY $BOX)
[ -n "$BOX_NBD_BASE_QCOW" ] || \
BOX_NBD_BASE_QCOW=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_BASE_QCOW $BOX)
[ -n "$BOX_NBD_OVERLAY_GB" ] || \
BOX_NBD_OVERLAY_GB=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_OVERLAY_GB $BOX)
[ -n "$BOX_NBD_OVERLAY_CPUS" ] || \
BOX_NBD_OVERLAY_CPUS=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_OVERLAY_CPUS $BOX)
[ -n "$BOX_NBD_OVERLAY_RAM" ] || \
BOX_NBD_OVERLAY_RAM=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_OVERLAY_RAM $BOX)
[ -n "$BOX_NBD_OVERLAY_DIR" ] || \
BOX_NBD_OVERLAY_DIR=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_OVERLAY_DIR $BOX)
[ -n "$BOX_NBD_OVERLAY_BR" ] || \
BOX_NBD_OVERLAY_BR=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_OVERLAY_BR $BOX)
#[ -n "$BOX_NBD_OVERLAY_NETWORK" ] || \
# BOX_NBD_OVERLAY_NETWORK=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_OVERLAY_NETWORK $BOX)
[ -n "$BOX_NBD_OVERLAY_PASS" ] || \
BOX_NBD_OVERLAY_PASS=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_OVERLAY_PASS $BOX)
[ ! -f "$BOX_NBD_BASE_QCOW" ] && \
ERROR BOX_NBD_BASE_QCOW=$BOX_NBD_BASE_QCOW must exist && exit 3
[ ! -d "$BOX_NBD_OVERLAY_DIR" ] && \
ERROR BOX_NBD_OVERLAY_DIR=$BOX_NBD_OVERLAY_DIR must exist && exit 5
[ -z "$BOX_NBD_OVERLAY_BR" ] && \
ERROR BOX_NBD_OVERLAY_BR=$BOX_NBD_OVERLAY_BR must not be null && exit 5
[ -n "$BOX_NBD_DEV" ] && grep "$BOX_NBD_DEV" /proc/mounts && WARN "$BOX_NBD_DEV" is mounted
export BOX_NBD_OVERLAY_DIR
# libvirt.libvirtError: Network not found: no network with matching name 'default'a
DBUG bash toxcore_create-vm.bash \
-n $BOX_NBD_OVERLAY_NAME \
-k $BOX_NBD_BASE_PUBKEY \
-i $BOX_NBD_BASE_QCOW \
-s $BOX_NBD_OVERLAY_GB \
-b $BOX_NBD_OVERLAY_BR \
-c $BOX_NBD_OVERLAY_CPUS \
-r $BOX_NBD_OVERLAY_RAM \
-d $BOX_NBD_OVERLAY_DIR \
-p $BOX_NBD_OVERLAY_PASS \
-o gentoo
bash toxcore_create-vm.bash \
-n $BOX_NBD_OVERLAY_NAME \
-k $BOX_NBD_BASE_PUBKEY \
-i $BOX_NBD_BASE_QCOW \
-s $BOX_NBD_OVERLAY_GB \
-b $BOX_NBD_OVERLAY_BR \
-c $BOX_NBD_OVERLAY_CPUS \
-r $BOX_NBD_OVERLAY_RAM \
-d $BOX_NBD_OVERLAY_DIR \
-p $BOX_NBD_OVERLAY_PASS \
-o gentoo < /dev/null
retval=$?
[ $retval -gt 0 ] && exit 1$retval
[ -f $BOX_NBD_OVERLAY_DIR/images/$BOX_NBD_OVERLAY_NAME.img ] && \
INFO $BOX_NBD_OVERLAY_DIR/images/$BOX_NBD_OVERLAY_NAME.img || {
ERROR NO $BOX_NBD_OVERLAY_DIR/images/$BOX_NBD_OVERLAY_NAME.img ; exit 2$retval ; }
INFO virsh define $BOX_NBD_OVERLAY_NAME
virsh define $BOX_NBD_OVERLAY_DIR/$BOX_NBD_OVERLAY_NAME.img
sleep 5
INFO virsh net-dhcp-leases default
sudo virsh net-dhcp-leases default
sudo find /var/lib/libvirt/qemu/channel/target/*${BOX_NBD_OVERLAY_NAME}* \
-name org.qemu.guest_agent.0

View File

@ -0,0 +1,125 @@
#!/bin/bash
# -*- mode: sh; fill-column: 75; tab-width: 8; coding: utf-8-unix -*-
prog=`basename $0 .bash`
PREFIX=/usr/local
ROLE=toxcore
. /usr/local/bin/usr_local_tput.bash || exit 2
# FixMe - systemd
MYID=`id -u`
# or use sudo? or just diagnostics? $prog should be run as root $MYID
[ $MYID -eq 0 ] && sudo= || sudo="WARN as root - sudo"
grep -q iommu=pt /proc/cmdline || WARN 'iommu=pt not on command line'
grep -q intel_iommu=on /proc/cmdline || WARN 'intel_iommu=on not on command line'
# https://wiki.archlinux.org/index.php/PCI_passthrough_via_OVMF#Setting_up_IOMMU
shopt -s nullglob
if [ $MYID -eq 0 ] ; then
[ -s "$TOXCORE_LOG_DIR"/iommu_groups.log ] || \
for g in /sys/kernel/iommu_groups/*; do
echo "IOMMU Group ${g##*/}:"
for d in $g/devices/*; do
echo -e "\t$(lspci -nns ${d##*/})"
done
done 2>&1|tee "$TOXCORE_LOG_DIR"/iommu_groups.log
b=`wc -l "$TOXCORE_LOG_DIR"/iommu_groups.log|sed -e 's/ .*//'`
[ $? -eq 0 -a -n "$b" -a "$b" -gt 0 ] || dmesg | grep -q 'DMAR: IOMMU enabled' || WARN 'IOMMU not active'
fi
if ifconfig -a | grep ^eth ; then
INFO eth present
else
WARN eth NOT present
lspci -v | grep '0[1-9]:00.0 Ethernet controller' || {
WARN Ethernet NOT present
rmmod r8169
modprobe r8169 debug=6
}
true || [ -f /var/lib/libvirt/net.xml ] || cat > /var/lib/libvirt/net.xml << EOF
<network>
<name>network</name>
<uuid>3f1b2eb3-98da-4e6d-8ad8-30e0e05a21d4</uuid>
<forward mode="nat"/>
<bridge name="virbr0" stp="on" delay="0"/>
<mac address="52:54:00:14:17:cf"/>
<domain name="network"/>
<ip address="192.168.100.1" netmask="255.255.255.0">
<dhcp>
<range start="192.168.100.128" end="192.168.100.254"/>
</dhcp>
</ip>
</network>
EOF
ifconfig -a |grep ^eth && {
ifconfig -a |grep ^virbr || {
$sudo virsh net-create /var/lib/libvirt/net.xml
}
}
fi
#4?
MODS4=(
nf_conntrack_ipv4
nf_nat_ipv4
)
MODS=(
ip_tables
iptable_filter
iptable_nat
iptable_mangle
ipt_REJECT
nf_conntrack
nf_defrag_ipv4
#5? nf_log_common
#5? nf_log_syslog
nf_log_ipv4
nf_nat
nf_reject_ipv4
nft_masq
xt_MASQUERADE
x_tables
bridge
br_netfilter
)
# bridge: filtering via arp/ip/ip6tables is no longer available by default. Update your scripts to load br_netfilter if you need this.
lsmod | sort > /tmp/$$.lsmod
for mod in "${MODS[@]}" ; do
grep -q ^$mod /tmp/$$.lsmod || $sudo modprobe $mod
done
rm -f /tmp/$$.lsmod
# selectively activate runtime features
[ "$#" -eq 0 ] && exit 0
if [ "$1" = "libvirt" ] ; then
/etc/init.d/libvirtd status || $sudo /etc/init.d/libvirtd start
ifconfig -a | grep virbr0 || {
WARN virbr0 not present - modprobe "${MODS[@]}" ; }
fi
if [ "$1" = "qemu" ] ; then
lsmod | grep -q kvm || $sudo modprobe kvm
exit 0
fi
if [ "$1" = "docker" ] ; then
#? modules for docker?
/etc/init.d/docker status || $sudo /etc/init.d/docker start
exit 0
fi
exit 0

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,420 @@
#!/bin/bash
# -*- mode: sh; fill-column: 75; tab-width: 8; coding: utf-8-unix -*-
# from https://github.com/earlruby/create-vm/
[ -f /usr/local/bin/usr_local_tput.bash ] && \
. /usr/local/bin/usr_local_tput.bash || {
DBUG() { echo DEBUG $* ; }
INFO() { echo INFO $* ; }
WARN() { echo WARN $* ; }
ERROR() { echo ERROR $* ; }
}
prog=`basename $0 .bash`
PREFIX=/usr/local
ROLE=toxcore
# create-vm - Quickly create guest VMs using cloud image files and cloud-init.
# Copyright 2018-2023 Earl C. Ruby III
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# Set BOX_NBD_OVERLAY_DIR environment variable to override default storage location for VMs
HOSTNAME=
IMG_FQN=
AUTH_KEYS_FQN=
RAM=2048
VCPUS=1
STORAGE=20
BRIDGE=virbr1
MAC='52:54:00:1d:9c:6f'
VERBOSE=
PASS=
OSINFO=gentoo
password=ansible
OUTDIR=${BOX_NBD_OVERLAY_DIRs:-"${HOME}/vms/virsh"}
usage()
{
cat << EOF
usage: $0 options
Quickly create guest VMs using cloud image files and cloud-init.
OPTIONS:
-h Show this message
-n Host name (required)
-i Full path and name of the base .img file to use (required)
-k Full path and name of the ansible user's public key file (required)
-d Output directory for the overlay qcow2 and related files
-r RAM in MB (defaults to ${RAM})
-c Number of VCPUs (defaults to ${VCPUS})
-s Amount of storage to allocate in GB (defaults to ${STORAGE})
-b Bridge interface to use (defaults to ${BRIDGE})
-m MAC address to use (default is to use a randomly-generated MAC)
-o OSINFO name like win11, win10, fedora32, gentoo, ububtu20
-p ansible users plaintext password
-v Verbose
EOF
}
while getopts "h:n:i:k:r:c:s:b:m:o:p:d:v" option; do
case "${option}"
in
h)
usage
exit 0
;;
n) HOSTNAME=${OPTARG};;
i) IMG_FQN=${OPTARG};;
k) AUTH_KEYS_FQN=${OPTARG};;
r) RAM=${OPTARG};;
c) VCPUS=${OPTARG};;
s) STORAGE=${OPTARG};;
b) BRIDGE=${OPTARG};;
m) MAC=${OPTARG};;
p) PASS=${OPTARG};;
o) password=${OPTARG};;
d) OUTDIR=${OPTARG};
BOX_NBD_OVERLAY_DIR=${OUTDIR};;
v) VERBOSE=1;;
*)
ERROR unhandled option "${option}" ${OPTARG}
usage
exit 1
;;
esac
done
if [[ -z $HOSTNAME ]]; then
ERROR "Host name is required"
usage
exit 1
fi
if [[ -z $IMG_FQN ]]; then
ERROR "Base cloud image file name is required"
usage
exit 1
fi
if [[ -z $BOX_NBD_OVERLAY_DIR ]]; then
ERROR "Output image directory is required BOX_NBD_OVERLAY_DIR"
usage
exit 1
fi
if [[ -z $AUTH_KEYS_FQN ]]; then
ERROR "ansible public key file $AUTH_KEYS_FQN not found"
usage
exit 1
fi
if ! [[ -f $IMG_FQN ]]; then
ERROR "$IMG_FQN file not found"
usage
exit 1
fi
if [[ -n $VERBOSE ]]; then
INFO "Building ${HOSTNAME} in $BOX_NBD_OVERLAY_DIR"
set -xv
fi
mkdir -p "$BOX_NBD_OVERLAY_DIR"/{images,xml,init,base} || exit 2
echo "Creating a qcow2 image file ${BOX_NBD_OVERLAY_DIR}/images/${HOSTNAME}.img that uses the cloud image file ${IMG_FQN} as its base"
INFO qemu-img create -b "${IMG_FQN}" -f qcow2 -F qcow2 "${BOX_NBD_OVERLAY_DIR}/images/${HOSTNAME}.img" "${STORAGE}G"
qemu-img create -b "${IMG_FQN}" -f qcow2 -F qcow2 "${BOX_NBD_OVERLAY_DIR}/images/${HOSTNAME}.img" "${STORAGE}G" || exit 3
echo "Creating meta-data file $BOX_NBD_OVERLAY_DIR/init/meta-data"
cat > "$BOX_NBD_OVERLAY_DIR/init/meta-data" << EOF
instance-id: ${HOSTNAME}
local-hostname: ${HOSTNAME}
EOF
# echo "Creating meta-data file $BOX_NBD_OVERLAY_DIR/init/meta-data.json"
# cat > "$BOX_NBD_OVERLAY_DIR/init/meta-data.json" << EOF
cat > /dev/null << EOF
{
"admin_pass": "root",
"availability_zone": "nova",
"hostname": "test.novalocal",
"launch_index": 0,
"name": "gentoo6",
"meta": {
"role": "webservers",
"essential": "false"
},
"public_keys": {
"mykey": " ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDRCJCQ1UD9QslWDSw5Pwsvba0Wsf1pO4how5BtNaZn0xLZpTq2nqFEJshUkd/zCWF7DWyhmNphQ8c+U+wcmdNVcg2pI1kPxq0VZzBfZ7cDwhjgeLsIvTXvU+HVRtsXh4c5FlUXpRjf/x+a3vqFRvNsRd1DE+5ZqQHbOVbnsStk3PZppaByMg+AZZMx56OUk2pZCgvpCwj6LIixqwuxNKPxmJf45RyOsPUXwCwkq9UD4me5jksTPPkt3oeUWw1ZSSF8F/141moWsGxSnd5NxCbPUWGoRfYcHc865E70nN4WrZkM7RFI/s5mvQtuj8dRL67JUEwvdvEDO0EBz21FV/iOracXd2omlTUSK+wYrWGtiwQwEgr4r5bimxDKy9L8UlaJZ+ONhLTP8ecTHYkaU1C75sLX9ZYd5YtqjiNGsNF+wdW6WrXrQiWeyrGK7ZwbA7lagSxIa7yeqnKDjdkcJvQXCYGLM9AMBKWeJaOpwqZ+dOunMDLd5VZrDCU2lpCSJ1M="
},
"uuid": "83679162-1378-4288-a2d4-70e13ec132aa"
}
EOF
# password=`openssl passwd -1 -stdin <<< $password`
echo "Creating user-data file $BOX_NBD_OVERLAY_DIR/init/user-data"
# https://techglimpse.com/nova-boot-instance-with-password/
cat > "$BOX_NBD_OVERLAY_DIR/init/user-data" << EOF
#cloud-config
# password: ansible
# chpasswd: { expire: False }
ssh_pwauth: true
runcmd:
- "rc-update add qemu-guest-agent"
- "chmod 755 /etc/init.d/qemu-guest-agent"
- "/etc/init.d/qemu-guest-agent start"
- "echo /etc/init.d/qemu-guest-agent start >> /etc/rc.local"
users:
- default
- name: ansible
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
groups:
- wheel
- adm
shell: /bin/bash
plain_text_password: "$password"
chpasswd: { expire: False }
homedir: /home/ansible
ssh_pwauth: true
ssh_authorized_keys:
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDRCJCQ1UD9QslWDSw5Pwsvba0Wsf1pO4how5BtNaZn0xLZpTq2nqFEJshUkd/zCWF7DWyhmNphQ8c+U+wcmdNVcg2pI1kPxq0VZzBfZ7cDwhjgeLsIvTXvU+HVRtsXh4c5FlUXpRjf/x+a3vqFRvNsRd1DE+5ZqQHbOVbnsStk3PZppaByMg+AZZMx56OUk2pZCgvpCwj6LIixqwuxNKPxmJf45RyOsPUXwCwkq9UD4me5jksTPPkt3oeUWw1ZSSF8F/141moWsGxSnd5NxCbPUWGoRfYcHc865E70nN4WrZkM7RFI/s5mvQtuj8dRL67JUEwvdvEDO0EBz21FV/iOracXd2omlTUSK+wYrWGtiwQwEgr4r5bimxDKy9L8UlaJZ+ONhLTP8ecTHYkaU1C75sLX9ZYd5YtqjiNGsNF+wdW6WrXrQiWeyrGK7ZwbA7lagSxIa7yeqnKDjdkcJvQXCYGLM9AMBKWeJaOpwqZ+dOunMDLd5VZrDCU2lpCSJ1M="
EOF
echo "Adding keys from the public key file $AUTH_KEYS_FQN to the user-data file"
while IFS= read -r key; do
echo " - $key" >> "$BOX_NBD_OVERLAY_DIR/init/user-data"
done < <(grep -v '^ *#' < "$AUTH_KEYS_FQN")
VM_IMAGE_DIR="$BOX_NBD_OVERLAY_DIR"
#old . /usr/local/bin/toxcore_create-ga.sh || exit 4
cat > "$BOX_NBD_OVERLAY_DIR/init/user-data" << \EOF
#!/bin/bash
# typically only executes on first boot
echo "############# user_data executing ##############"
#grep gentoo /etc/shadow
sed -e 's/#-:ALL:ALL/+:gentoo:ALL/' -i /etc/security/access.conf
PW=`echo $PASS | openssl passwd -1 --stdin `
grep -q ^gentoo /etc/passwd || \
useradd --gid 4 --uid 1000 --home-dir /home/gentoo \
--comment Gentoo --password "$PW" \
-G adm,wheel --shell /bin/bash gentoo
usermod --password "$PW" -G adm,wheel gentoo
# root
usermod --password '$1$1Ho4y/W8$5VymfKWWAhLxwkkPZiWTZ1' root
# unlock account
passwd -u gentoo
passwd -u root
sed -e 's/# %wheel /%wheel /' -i /etc/sudoers
sed -e 's/PasswordAuthentication no/PasswordAuthentication yes/' -i /etc//ssh/sshd_config
sed -e 's/PermitRootLogin.*/PermitRootLogin yes/' -i /etc//ssh/sshd_config
grep net.ipv4.ip_forward=1 /etc/sysctl.conf || \
echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf
cd /etc/init.d
[ -e net.eth0 ] || ln -s net.lo net.eth0
for elt in i o linuxPen19 ; do
grep -q $elt /etc/fstab && continue
echo "$elt /mnt/$elt virtiofs defaults 0 0" >> /etc/fstab
done
#grep gentoo /etc/shadow
EOF
echo "Generating the cidata ISO file $BOX_NBD_OVERLAY_DIR/images/${HOSTNAME}-cidata.iso"
(
cd "$BOX_NBD_OVERLAY_DIR/init/"
genisoimage \
-output "$BOX_NBD_OVERLAY_DIR/images/${HOSTNAME}-cidata.img" \
-volid cidata \
-rational-rock \
-joliet \
-input-charset utf-8 \
user-data meta-data
) || exit 5
MACCMD=
if [[ -n $MAC ]]; then
MACCMD="--mac=${MAC}"
fi
[ -f ${BOX_NBD_OVERLAY_DIR}/images/${HOSTNAME}.img ] || exit 5
[ -f $BOX_NBD_OVERLAY_DIR/images/${HOSTNAME}-cidata.img ] || exit 6
# libvirt.libvirtError: /usr/lib/qemu/qemu-bridge-helper --use-vnet --br=-c --fd=31: failed to communicate with bridge helper: stderr=failed to parse default acl file `/etc/qemu/bridge.conf'
if [ ! -f "/etc/qemu/bridge.conf" ] ; then
echo allow $BRIDGE >> "/etc/qemu/bridge.conf"
elif ! grep $BRIDGE "/etc/qemu/bridge.conf" ; then
echo allow $BRIDGE >> "/etc/qemu/bridge.conf"
fi
if [ $BRIDGE = virbr0 ] ; then
network=default
# 192.168.122.248/24
elif [ $BRIDGE = virbr1 ] ; then
network=Whonix-External
else
WARN unrecognized $BRIDGE
fi
if [ "$network" != '' ] ; then
virsh net-list | grep -q $network || \
virsh net-start $network
fi
sudo ifconfig -a | grep $BRIDGE && \
NETWORK="--network bridge=${BRIDGE},model=virtio" || \
WARN bridge $BRIDGE not running. not adding a network
NETWORK="--network network=default,model=virtio"
INFO virt-install \
--name="${HOSTNAME}" \
--osinfo "$OSINFO" \
--import \
--name="${HOSTNAME}" \
--disk "path=${BOX_NBD_OVERLAY_DIR}/images/${HOSTNAME}.img,format=qcow2" \
--disk "path=$BOX_NBD_OVERLAY_DIR/images/${HOSTNAME}-cidata.img,device=cdrom" \
--ram="${RAM}" \
--vcpus="${VCPUS}" \
--autostart \
--hvm \
--arch x86_64 \
--accelerate \
--check-cpu \
--force \
--watchdog=default \
--graphics spice,listen=socket \
--channel spicevmc,target.type=virtio,target.name=com.redhat.spice.0 \
--channel type=unix,target.type=virtio,target.name=org.qemu.guest_agent.0 \
--network "bridge=${BRIDGE},model=virtio" \
--rng /dev/urandom \
--os-variant detect=on,name=$OSINFO \
--noautoconsole
# squelch warnings
python3.sh `which virt-install` \
--name="${HOSTNAME}" \
--osinfo "$OSINFO" \
--import \
--disk "path=${BOX_NBD_OVERLAY_DIR}/images/${HOSTNAME}.img,format=qcow2" \
--disk "path=$BOX_NBD_OVERLAY_DIR/images/${HOSTNAME}-cidata.img,device=cdrom" \
--ram="${RAM}" \
--vcpus="${VCPUS}" \
--autostart \
--hvm \
--arch x86_64 \
--accelerate \
--check-cpu \
--force \
--watchdog=default \
--graphics spice,listen=socket \
--filesystem /,/mnt/linuxPen19 \
--channel spicevmc,target.type=virtio,target.name=com.redhat.spice.0 \
--channel unix,target.type=virtio,target.name=org.qemu.guest_agent.0 \
$NETWORK \
--rng /dev/urandom \
--os-variant detect=on,name=$OSINFO \
--noautoconsole \
|| exit 7
# --debug
#? --shmem name=shmem_server,type="memfd",mode="shared"
# --shmem name=shmem0 ivshmem device is not supported with this QEMU binary
# was --graphics vnc,listen=0.0.0.0
# --osinfo "$OSINFO" \
# Make a backup of the VM's XML definition file
virsh dumpxml "${HOSTNAME}" > "${BOX_NBD_OVERLAY_DIR}/xml/${HOSTNAME}.xml" || exit 8
INFO wrote xml `ls -l ${BOX_NBD_OVERLAY_DIR}/xml/${HOSTNAME}.xml`
if [ -n "$VERBOSE" ]; then
set +xv
fi
# problems: type=qemu-vdagent unix unix=on
# problems: type="spicevmc
# ERROR Unknown --channel options: ['unix']
cp "${BOX_NBD_OVERLAY_DIR}/xml/${HOSTNAME}.xml" \
"${BOX_NBD_OVERLAY_DIR}/xml/${HOSTNAME}.xml".new
cat > /tmp/ga.works <<EOF
<channel type="unix">
<source mode="bind" path="/var/lib/libvirt/qemu/channel/target/domain-25-gentoo1/org.qemu.guest_agent.0"/>
<target type="virtio" name="org.qemu.guest_agent.0" state="connected"/>
<address type="virtio-serial" controller="0" bus="0" port="2"/>
</channel>
EOF
cat > /tmp/sp.works <<EOF
<channel type="spicevmc">
<target type="virtio" name="com.redhat.spice.0" state="disconnected"/>
<address type="virtio-serial" controller="0" bus="0" port="1"/>
</channel>
EOF
# Show running VMs
virsh list | grep "${HOSTNAME}" && INFO "${HOSTNAME}" || { ERROR "${HOSTNAME}" ; exit 9$? ; }
# use the following passwordless demonstration key for testing or
# replace with your own key pair
#
# -----BEGIN OPENSSH PRIVATE KEY-----
# b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
# NhAAAAAwEAAQAAAYEA0QiQkNVA/ULJVg0sOT8LL22tFrH9aTuIaMOQbTWmZ9MS2aU6tp6h
# RCbIVJHf8wlhew1soZjaYUPHPlPsHJnTVXINqSNZD8atFWcwX2e3A8IY4Hi7CL0171Ph1U
# bbF4eHORZVF6UY3/8fmt76hUbzbEXdQxPuWakB2zlW57ErZNz2aaWgcjIPgGWTMeejlJNq
# WQoL6QsI+iyIsasLsTSj8ZiX+OUcjrD1F8AsJKvVA+JnuY5LEzz5Ld6HlFsNWUkhfBf9eN
# ZqFrBsUp3eTcQmz1FhqEX2HB3POuRO9JzeFq2ZDO0RSP7OZr0Lbo/HUS+uyVBML3bxAztB
# Ac9tRVf4jq2nF3dqJpU1EivsGK1hrYsEMBIK+K+W4psQysvS/FJWiWfjjYS0z/HnEx2JGl
# NQu+bC1/WWHeWLao4jRrDRfsHVulq160Ilnsqxiu2cGwO5WoEsSGu8nqpyg43ZHCb0FwmB
# izPQDASlniWjqcKmfnTrpzAy3eVWawwlNpaQkidTAAAFgGKSj8diko/HAAAAB3NzaC1yc2
# EAAAGBANEIkJDVQP1CyVYNLDk/Cy9trRax/Wk7iGjDkG01pmfTEtmlOraeoUQmyFSR3/MJ
# YXsNbKGY2mFDxz5T7ByZ01VyDakjWQ/GrRVnMF9ntwPCGOB4uwi9Ne9T4dVG2xeHhzkWVR
# elGN//H5re+oVG82xF3UMT7lmpAds5VuexK2Tc9mmloHIyD4BlkzHno5STalkKC+kLCPos
# iLGrC7E0o/GYl/jlHI6w9RfALCSr1QPiZ7mOSxM8+S3eh5RbDVlJIXwX/XjWahawbFKd3k
# 3EJs9RYahF9hwdzzrkTvSc3hatmQztEUj+zma9C26Px1EvrslQTC928QM7QQHPbUVX+I6t
# pxd3aiaVNRIr7BitYa2LBDASCvivluKbEMrL0vxSVoln442EtM/x5xMdiRpTULvmwtf1lh
# 3li2qOI0aw0X7B1bpatetCJZ7KsYrtnBsDuVqBLEhrvJ6qcoON2Rwm9BcJgYsz0AwEpZ4l
# o6nCpn5066cwMt3lVmsMJTaWkJInUwAAAAMBAAEAAAGAEuz77Hu9EEZyujLOdTnAW9afRv
# XDOZA6pS7yWEufjw5CSlMLwisR83yww09t1QWyvhRqEyYmvOBecsXgaSUtnYfftWz44apy
# /gQYvMVELGKaJAC/q7vjMpGyrxUPkyLMhckALU2KYgV+/rj/j6pBMeVlchmk3pikYrffUX
# JDY990WVO194Dm0buLRzJvfMKYF2BcfF4TvarjOXWAxSuR8www050oJ8HdKahW7Cm5S0po
# FRnNXFGMnLA62vN00vJW8V7j7vui9ukBbhjRWaJuY5rdG/UYmzAe4wvdIEnpk9xIn6JGCp
# FRYTRn7lTh5+/QlQ6FXRP8Ir1vXZFnhKzl0K8Vqh2sf4M79MsIUGAqGxg9xdhjIa5dmgp8
# N18IEDoNEVKUbKuKe/Z5yf8Z9tmexfH1YttjmXMOojBvUHIjRS5hdI9NxnPGRLY2kjAzcm
# gV9Rv3vtdF/+zalk3fAVLeK8hXK+di/7XTvYpfJ2EZBWiNrTeagfNNGiYydsQy3zjZAAAA
# wBNRak7UrqnIHMZn7pkCTgceb1MfByaFtlNzd+Obah54HYIQj5WdZTBAITReMZNt9S5NAR
# M8sQB8UoZPaVSC3ppILIOfLhs6KYj6RrGdiYwyIhMPJ5kRWF8xGCLUX5CjwH2EOq7XhIWt
# MwEFtd/gF2Du7HUNFPsZGnzJ3e7pDKDnE7w2khZ8CIpTFgD769uBYGAtk45QYTDo5JroVM
# ZPDq08Gb/RhIgJLmIpMwyreVpLLLe8SwoMJJ+rihmnJZxO8gAAAMEA0lhiKezeTshht4xu
# rWc0NxxD84a29gSGfTphDPOrlKSEYbkSXhjqCsAZHd8S8kMr3iF6poOk3IWSvFJ6mbd3ie
# qdRTgXH9Thwk4KgpjUhNsQuYRHBbI59Mo+BxSI1B1qzmJSGdmCBL54wwzZmFKDQPQKPxiL
# n0Mlc7GooiDMjT1tbuW/O1EL5EqTRqwgWPTKhBA6r4PnGF150hZRIMooZkD2zX6b1sGojk
# QpvKkEykTwnKCzF5TXO8+wJ3qbcEo9AAAAwQD+Z0r68c2YMNpsmyj3ZKtZNPSvJNcLmyD/
# lWoNJq3djJN4s2JbK8l5ARUdW3xSFEDI9yx/wpfsXoaqWnygP3PoFw2CM4i0EiJiyvrLFU
# r3JLfDUFRy3EJ24RsqbigmEsgQOzTl3xfzeFPfxFoOhokSvTG88PQji1AYHz5kA7p6Zfaz
# Ok11rJYIe7+e9B0lhku0AFwGyqlWQmS/MhIpnjHIk5tP4heHGSmzKQWJDbTskNWd6aq1G7
# 6HWfDpX4HgoM8AAAALaG9sbWFuYkBhcmM=
# -----END OPENSSH PRIVATE KEY-----
#

View File

@ -0,0 +1,80 @@
#!/bin/sh
# -*- mode: sh; fill-column: 75; tab-width: 8; coding: utf-8-unix -*-
# The idea here is to run ansible_local.bash --tags daily
# and then use this to do the parsing and throwing errors based on the output.
# This way the ansible run can be free from erroring and this can be
# run repeatedly anytime outside of ansible to deal with the issues raised.
# It is also run at the end of ansible_local.bash --tags daily to raise the issues.
prog=`basename $0 .bash`
PREFIX=/usr/local
ROLE=toxcore
. /usr/locaal/etc/testforge/testforge.bash
TOXCORE_LOG_DIR=$PREFIX/var/log
[ -d $TOXCORE_LOG_DIR ] || mkdir -p $TOXCORE_LOG_DIR
MYID=`id -u`
[ $MYID -eq 0 ] || WARN $prog should be run as root $MYID
which ansifilter >/dev/null 2>&1 && ansifilter=ansifilter || ansifilter=cat
ly=daily
i=0
errs=0
warns=0
WLOG="$TOXCORE_LOG_DIR"/$ly/W$prog$$.log
ELOG="$TOXCORE_LOG_DIR"/$ly/E$prog$$.log
#?ols_make_testforge_logs $TOXCORE_LOG_DIR
find "$TOXCORE_LOG_DIR"/$ly/ -type f -name W${prog}*.log -o -name E${prog}*.log -mtime +1 -delete
if virsh list | grep -q Whonix-Gateway ; then
/usr/local/bin/toxcore_libvirt_test_ga.bash
fi
# -%d
if ls /var/log/libvirt/qemu/*.log 2>/dev/null ; then
sudo grep ^`date +%Y-%m`.*warning /var/log/libvirt/qemu/*.log | tee -a $WLOG
fi
# FixMe missing
[ -x $PREFIX/bin/toxcore_libvirt_test_xml.bash ] && \
$PREFIX/bin/toxcore_libvirt_test_xml.bash 2>&1 | grep WARN: >> $WLOG
if which virt-host-validate 2>/dev/null ; then
[ -f $TOXCORE_LOG_DIR/daily/virt-host-validate.log ] || \
sudo virt-host-validate > $TOXCORE_LOG_DIR/daily/virt-host-validate.log 2>&1
b=`grep FAIL $TOXCORE_LOG_DIR/daily/virt-host-validate.log|wc -l|sed -e 's/ .*//'`
[ $? -eq 0 -a -n "$b" -a $b -gt 0 ]
b=`grep WARN $TOXCORE_LOG_DIR/daily/virt-host-validate.log|wc -l|sed -e 's/ .*//'`
[ $? -eq 0 -a -n "$b" -a $b -gt 0 ] && \
WARN $b WARN in $TOXCORE_LOG_DIR/$ly/virt-host-validate.log $warns | tee -a $WLOG
fi
if /etc/init.d/libvirtd status ; then
virsh list | grep '^ [0-9]' | while read id elt rest ; do
[ $rest = running ] || continue
virsh dumpxml $elt | grep org.qemu.guest_agent.0.*connected || \
WARN org.qemu.guest_agent not connected for $elt |tee -a $WLOG
# <target type='virtio' name='com.redhat.spice.0' state='connected'/>
# <target type='virtio' name='org.qemu.guest_agent.0' state='connected'/>
done
fi
warns=`grep -c WARN: "$WLOG"`
[ $warns -ne 0 ] && \
WARN "$prog $ly $warns warnings in $WLOG"
errs=`grep -c ERROR: "$ELOG"`
[ $errs -ne 0 ] && \
ERROR "$prog $ly $errs errors in $ELOG" && \
exit -$errs
[ $warns -eq 0 -a $errs -eq 0 ] && \
ols_clean_testforge_logs $TOXCORE_LOG_DIR && \
INFO "No $ly errors in $TOXCORE_LOG_DIR"
exit 0

View File

@ -0,0 +1,61 @@
#!/bin/bash
# -*- mode: sh; fill-column: 75; tab-width: 8; coding: utf-8-unix -*-
# from https://github.com/earlruby/create-vm/
[ -f /usr/local/bin/usr_local_tput.bash ] && \
. /usr/local/bin/usr_local_tput.bash || {
DBUG() { echo DEBUG $* ; }
INFO() { echo INFO $* ; }
WARN() { echo WARN $* ; }
ERROR() { echo ERROR $* ; }
}
prog=`basename $0 .bash`
PREFIX=/usr/local
ROLE=toxcore
# delete-vm - Delete a virtual machine created with create-vm
# Copyright 2018-2023 Earl C. Ruby III
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
VM=$1
# Set VM_IMAGE_DIR environment variable to override default storage location for VMs
VM_IMAGE_DIR=${VM_IMAGE_DIR:-"${HOME}/vms/virsh"}
VM_IMAGE="${VM_IMAGE_DIR}/images/$VM.img"
CI_IMAGE="${VM_IMAGE_DIR}/images/$VM-cidata.img"
usage()
{
cat << EOF
usage: $0 vmname
EOF
}
if [[ -z $VM ]]; then
usage
exit 1
fi
if [[ -e $VM_IMAGE ]]; then
# VM exists
virsh destroy "$VM"
virsh undefine "$VM"
rm -fv "$VM_IMAGE" "$CI_IMAGE"
else
echo "Cannot find an VM image file named '$VM_IMAGE'. Attempting undefine..."
virsh undefine "$VM"
fi

View File

@ -0,0 +1,55 @@
#!/bin/bash
# -*- mode: sh; fill-column: 75; tab-width: 8; coding: utf-8-unix -*-
# from https://github.com/earlruby/create-vm/
[ -f /usr/local/bin/usr_local_tput.bash ] && \
. /usr/local/bin/usr_local_tput.bash || {
DBUG() { echo DEBUG $* ; }
INFO() { echo INFO $* ; }
WARN() { echo WARN $* ; }
ERROR() { echo ERROR $* ; }
}
prog=`basename $0 .bash`
PREFIX=/usr/local
ROLE=toxcore
. /usr/local/etc/testforge/testforge.bash
[ -n "$HOSTVMS_VAR_LOCAL" ] && PREFIX=$HOSTVMS_VAR_LOCAL
# get-node-ip - Get the IP address of a VM managed by virsh.
# Copyright 2018-2023 Earl C. Ruby III
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
usage()
{
cat << EOF
usage: $0 hostname
This script will take a virsh-managed VM hostname and return the IP address.
EOF
}
HOSTNAME=$1
if [[ -z $HOSTNAME ]]; then
echo "ERROR: Hostname is required"
usage
exit 1
fi
MAC=$(virsh domiflist $HOSTNAME | awk '{ print $5 }' | tail -2 | head -1)
arp -a | grep $MAC | awk '{ print $2 }' | sed 's/[()]//g'
INFO MAC=$MAC arp=$arp

View File

@ -0,0 +1,56 @@
#!/bin/sh
# -*- mode: sh; fill-column: 75; tab-width: 8; coding: utf-8-unix -*-
# The idea here is to run ansible_local.bash --tags daily
# and then use this to do the parsing and throwing errors based on the output.
# This way the ansible run can be free from erroring and this can be
# run repeatedly anytime outside of ansible to deal with the issues raised.
# It is also run at the end of ansible_local.bash --tags daily to raise the issues.
prog=`basename $0 .bash`
PREFIX=/usr/local
ROLE=toxcore
. /usr/locaal/etc/testforge/testforge.bash
TOXCORE_LOG_DIR=$PREFIX/var/log
[ -d $TOXCORE_LOG_DIR ] || mkdir -p $TOXCORE_LOG_DIR
MYID=`id -u`
[ $MYID -eq 0 ] || WARN $prog should be run as root $MYID
which ansifilter >/dev/null 2>&1 && ansifilter=ansifilter || ansifilter=cat
ly=hourly
i=0
errs=0
warns=0
WLOG="$TOXCORE_LOG_DIR"/$ly/W$prog$$.log
ELOG="$TOXCORE_LOG_DIR"/$ly/E$prog$$.log
#?ols_make_testforge_logs $TOXCORE_LOG_DIR
find "$TOXCORE_LOG_DIR"/$ly/ -type f -name W${prog}*.log -o -name E${prog}*.log -mtime +1 -delete
if virsh list | grep -q Whonix-Gateway ; then
/usr/local/bin/toxcore_libvirt_test_ga.bash
fi
# -%d
if ls /var/log/libvirt/qemu/*.log 2>/dev/null ; then
sudo grep ^`date +%Y-%m`.*warning /var/log/libvirt/qemu/*.log | tee -a $WLOG
fi
warns=`grep -c WARN: "$WLOG"`
[ $warns -ne 0 ] && \
WARN "$prog $ly $warns warnings in $WLOG"
errs=`grep -c ERROR: "$ELOG"`
[ $errs -ne 0 ] && \
ERROR "$prog $ly $errs errors in $ELOG" && \
exit -$errs
[ $warns -eq 0 -a $errs -eq 0 ] && \
ols_clean_testforge_logs $TOXCORE_LOG_DIR && \
INFO "No $ly errors in $TOXCORE_LOG_DIR"
exit 0

View File

@ -0,0 +1,372 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
OZZlbuptEES1RYiDmaXBskBpswGrGSXvXApMHUhHTKwv5buyYEJ0mxOguy6C7HJM
nHY+EbXWOWGuDuBPz1TY8Ty1JJDnHQto+OJFmw8p/VciDbPGkTtzFs/VpIRvWnda
R4iHmjb+12+Z+UFmGRZDBaiB62mAr1hCIi+Dn8pSvHaLaJuLrfwg3EN9elBQ3qUQ
8+L4vM5RhpjGmnAWhpmG9Dea+rn5jwYQTNUu+0wVfNYx0aII/wgNCSIQmBDIfAam
I3qszAXhxWeoi3v/brRYR2VudG9vIGVidWlsZCByZXBvc2l0b3J5IHNpZ25pbmcg
a2V5IChBdXRvbWF0ZWQgU2lnbmluZyBLZXkpIDxpbmZyYXN0cnVjdHVyZUBnZW50
b28ub3JnPokBXwQTAQoASRYhBBj3A9cCsblZE3MUjFXTI47AUDluBQJcofqEKxpo
dHRwczovL3d3dy5nZW50b28ub3JnL2dsZXAvZ2xlcC0wMDc5Lmh0bWwACgkQVdMj
jsBQOW4GgQf/YRJbSEWQr66zifC5Q09a9gYMoamGc5eJ7+xLIW9M3K00JLltXxr0
0eq4dtVmQPBjKe6B0rtJKT1u8YO44/prRZX8UlfhsbJ49xNhaFDGrUSQeS2Zh2d0
FhdPuLc0NmOOoUz37Lixskus7gi8xolEVcLdjJojiqO4Tae0nzZw1aixHDZtk1yb
jTXyUhpdSS8Jibmi1TJzx3IIUdwH0YTwoVvzAY6CuSTv7gbBtT8I01pCZp+b42b/
OZUMqr63iBicpRfUpGFuADfe6bXl/CbPcCEP8IutUhC6gaLKkdEEdlw8DtqwUdn0
vtLxbxD1CNH8BZo1fE3Ex9oa7z8bu4CwcYkCVAQTAQoAPgIbAQULCQgHAgYVCgkI
CwIEFgIDAQIeAQIXgBYhBNzQW3HquUGZUn9ErNtrjB+W2L9tBQJiq4l8BQkXs3k4
AAoJENtrjB+W2L9tc90P/R3do410Cz3ZZrb/g86UhT+sSavfA8mhCgKD25yMWPK/
fuMlIkIhD7YSzc+Wtu+9Au/NempXBDHsBX2Dx512aFWcSQy2EDOWnJ4tlFrJ2Xmd
0F1yZk395Ogka4cl6WbpuxN47C7YIqkOho1UUR7dRO0XgMAGS63qgp6w2ego9o4I
K27rchy5u7rTcpt0U8gYyILxAGa82f5W4j4P8JLPTvzQ0weND3hXTCwDWwTtDmzb
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
BBgRAgAmAhsMFiEE2Z6sc3moULzkfaXynmQ4yBcHIFgFAmG+0EkFCSSWMTEACgkQ
nmQ4yBcHIFg9gQCgoDtS/5VxuEk4KeO0WO+xqeic/O0An1SqPWNFuVamxsRSznXU
c5SqNJU9mQINBEqUWzgBEACXftaG+HVuSQBEqdpBIg2SOVgWW/KbCihO5wPOsdbM
93e+psmbwvw+OtNHADQvxocKMuZX8Q/j5i3nQ/ikQFW5Oj6UXvl1qyxZhR2P7GZS
NQxn0eMIzAX08o691ws2/dFGXKmNT6btYJ0FxuTtTVSK6zi68WF+ILGK/O2TZXK9
EKfZKPDHKHcGrUq4c03vcGANz/8ksJj2ZYEGxMr1h7Wfe9PVcm0gCB1MhYHNR755
M47V5Pchfyxbs6vaKz82PgrNjjjbT0PISvnKReUOdA2PFUWry6UKQkiVrLVDRkd8
fryLL8ey5JxgVoJZ4echoVWQ0JYJ5lJTWmcZyxQYSAbz2w9dLB+dPyyGpyPp1KX1
ADukbROp9S11I9+oVnyGdUBm+AUme0ecekWvt4qiCw3azghLSwEyGZc4a8nNcwSq
FmH7Rqdd1+gHc+4tu4WHmguhMviifGXKyWiRERULp0obV33JEo/c4uwyAZHBTJtK
tVaLb92zaRdh1yox2I85iumyt62lq9dfOuet4NNVDnUkqMYCQD23JB8IM+qnVaDw
J6oCSIKinY3uyoqbVE6Lkm+Hk5q5pbvg1cpEH6HWWAl20EMCzMOoMcH0tPyQLDlD
2Mml7veGkwdy3S6RkjCympbNzqWec2+hkU2c93Bgpfh7QP0GDN0qrzcNNFmrD579
5QARAQABtFNHZW50b28gTGludXggUmVsZWFzZSBFbmdpbmVlcmluZyAoQXV0b21h
dGVkIFdlZWtseSBSZWxlYXNlIEtleSkgPHJlbGVuZ0BnZW50b28ub3JnPokBXwQT
AQoASRYhBBj3A9cCsblZE3MUjFXTI47AUDluBQJcofrRKxpodHRwczovL3d3dy5n
ZW50b28ub3JnL2dsZXAvZ2xlcC0wMDc5Lmh0bWwACgkQVdMjjsBQOW6RNggAkpDb
nZu+LLDLnfQTl5UCB6wKlKpRgLkOcazFgRlY4me3Xf2gIR++tWfiXXCPhTUhVdjx
PGAGwSeBddLvf79cD/FkDWzJw1TKR9pgh5SWgGNxaWVQqK4RuGv/1UWRjGxFO3KB
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
=T4NL
-----END PGP PUBLIC KEY BLOCK-----

View File

@ -0,0 +1,127 @@
#!/bin/sh
# -*- mode: sh; tab-width: 8; encoding: utf-8-unix -*-
prog=`basename $0 .bash`
PREFIX=/usr/local
[ -f /usr/local/etc/testforge/testforge.bash ] && \
. /usr/local/etc/testforge/testforge.bash
ROLE=toxcore
DESC=""
. $PREFIX/src/usr_local_tput.bash || exit 1
PKG=toxcore
DIR=c-$PKG
GIT_HUB=github.com
GIT_USER=TokTok
GIT_DIR=$DIR
GIT_BRAN=master
VERS=2.18.0
cd $PREFIX/src || exit 2
WD=$PWD
if [ "$#" -eq 0 ] ; then
if [ -d /etc/portage ] ; then
ols_funtoo_requires app-crypt/argon2 dev-vcs/git media-gfx/qrencode
fi
WD=$PWD
if [ ! -d "$DIR" ] ; then
if [ ! -d "$PREFIX/net/Git/$GIT_HUB/$GIT_USER/$GIT_DIR" ] ; then
[ -d "$PREFIX/net/Git/$GIT_HUB/$GIT_USER" ] || \
mkdir "$PREFIX/net/Git/$GIT_HUB/$GIT_USER"
ols_are_we_connected || { DEBUG not connected ; exit 0 ; }
cd "$PREFIX/net/Git/$GIT_HUB/$GIT_USER"
git clone -b $GIT_BRAN --depth=1 https://$GIT_HUB/$GIT_USER/$GIT_DIR || exit 4
git clone --depth=1 https://$GIT_HUB/$GIT_USER/dockerfiles
cd $WD
# wget -xcP ../net/Http/ https://github.com/TokTok/c-toxcore/releases/download/v0.2.18/c-toxcore-0.2.18.tar.gz
fi
cp -rip "$PREFIX/net/Git/$GIT_HUB/$GIT_USER"/$GIT_DIR $DIR
fi
cd "$DIR" || exit 5
[ -f third_party/cmp/Makefile ] || git submodule update --init || exit 6
# ols_apply_testforge_patches
# # [ -f CMakeLists.txt.dst ] || patch -b -z.dst < toxcore.diff || exit 7
[ -f cmake.sh ] || cat > cmake.sh << \EOF
#!/bin/sh
# -*- mode: sh; fill-column: 75; tab-width: 8; coding: utf-8-unix -*-
PREFIX=$PREFIX
ROLE=toxcore
CORE=$PREFIX/src/c-toxcore
DIR=_build
LIB=$CORE/$DIR
cd $CORE | exit 3
mkdir _build
cd _build
cmake \
-DCMAKE_BUILD_TYPE="Debug" \
-DCMAKE_UNITY_BUILD=ON \
-DMIN_LOGGER_LEVEL=DEBUG \
-DMUST_BUILD_TOXAV=ON \
-DSTRICT_ABI=ON \
-DTEST_TIMEOUT_SECONDS=1200 \
-DUSE_IPV6=OFF \
-DAUTOTEST=ON \
-DNON_HERMETIC_TESTS=ON \
-DPROXY_TEST=ON \
-DBUILD_MISC_TESTS=ON \
-DBUILD_FUN_UTILS=ON \
-DBOOTSTRAP_DAEMON=ON \
-DSOCKS_PORT_TEST=9050 \
-DSOCKS_HOST_TEST=localhost \
.. > cmake.log 2>&1
#sed -e 's/-DNDEBUG/-g/' -i CMakeCache.txt
make .. > make.log 2>&1
ls $LIB/*so* || { echo ERROR $LIB ; exit 2 ; }
EOF
bash cmake.sh || {
retcval=$?
ERROR cmake $retcval
exit 3$retval
}
cd _build
make >> make.log 2>&1 || {
retcval=$?
ERROR cmake $retcval
exit 3$retval
}
cp -p other/bootstrap_daemon/tox-bootstrapd $PREFIX/bin
cp -p other/bootstrap_daemon/tox-bootstrapd.sh $PREFIX/etc/init.d/tox-bootstrapd
# ln -s $PREFIX/etc/init.d/tox-bootstrapd /etc/init.d
exit 0
elif [ $1 = 'check' ] ; then # 1*
# ols_test_bins && exit 0 || exit $?
[ ! -d $DIR/_build ] && WARN not built yet $DIR && exit 11
[ -f $DIR/_build/libtoxcore.so.${VERS} ] && WARN not compiled yet $DIR && exit 12
ldd $DIR/_build/libtoxcore.so.${VERS} | grep found && ERROR ldd fails $DIR && exit 13
exit 0
elif [ "$1" = 'test' ] ; then # 3*
cd $PREFIX/src/$DIR/_build || exit 30
ctest || exit 31
elif [ "$1" = 'refresh' ] ; then # 6*
cd $PREFIX/src/$DIR || exit 60
/usr/local/sbin/base_diff_from_dst.bash $ROLE || exit 6$?
elif [ "$1" = 'update' ] ; then # 7*
ols_are_we_connected || exit 0
cd $PREFIX/src/$DIR || exit 70
git pull || exit 7$?
fi

View File

@ -0,0 +1,86 @@
#!/bin/sh
# -*- mode: sh; tab-width: 8; encoding: utf-8-unix -*-
prog=`basename $0 .bash`
PREFIX=/usr/local
[ -f /usr/local/etc/testforge/testforge.bash ] && \
. /usr/local/etc/testforge/testforge.bash
ROLE=toxcore
DESC=""
. $PREFIX/bin/usr_local_tput.bash || exit 1
PKG=negotiator
DIR=negatiator
declare -a FILES
FILES=(
1a/dd/3fcb75aebaa0a28f4f4e4a5773833d5cc7fecd47f2b535fc7e445f289539/negotiator-common-0.12.2.tar.gz
65/e5/bddc148f12aa8e81cfb0fbe504541436d0d38c6cb1546fa4fb5fbefcb5ce/negotiator-host-0.12.2.tar.gz
fe/a1/3d77020b8c5034f7ef65434d0510d1262840550155ce5f433c8189499326/negotiator-guest-0.12.2.tar.gz
)
declare -a GUESTS
GUESTS=(
coloredlogs-15.0.1-py2.py3-none-any.whl
executor-23.2-py2.py3-none-any.whl
fasteners-0.19-py3-none-any.whl
humanfriendly-10.0-py2.py3-none-any.whl
negotiator_common-0.12.2-py3-none-any.whl
negotiator_guest-0.12.2-py3-none-any.whl
property_manager-3.0-py2.py3-none-any.whl
setuptools-69.0.2-py3-none-any.whl
six-1.16.0-py2.py3-none-any.whl
supervisor-4.2.5-py2.py3-none-any.whl
verboselogs-1.7-py2.py3-none-any.whl
)
cd $PREFIX/src || exit 2
WD=$PWD
if [ "$#" -eq 0 ] ; then
WD=$PWD
if true || [ ! -d "$DIR" ] ; then
route | grep -q ^def || { DBUG not connected ; exit 0 ; }
wget -xcP $PREFIX/net/Http/ https://pypi.org/project/negotiator-common/
for f in "${FILES[@]}" ; do
wget -xcP $PREFIX/net/Http/ https://files.pythonhosted.org/packages/$f
done
if [ ! -d "$WD/$DIR" ] ; then
[ -d $WD/$DIR ] || mkdir $WD/$DIR
pip3.sh download -d $WD/$DIR \
negotiator-guest supervisor humanfriendly
cd $WD
fi
for f in "${FILES[@]}" ; do
tar xvfkz $PREFIX/net/Http/$f 2>/dev/null
done
fi
for f in "${FILES[@]}" ; do
base=`basename $f .tar.gz`
[ -d base ] && continue
tar xvfkz $PREFIX/net/Http/$f 2>/dev/null
cd $base
pip3.sh install --prefix=/usr/local . >> install.log 2>&1 || \
WARN problems installing $base retval=$retval
cd ..
done
exit 0
elif [ "$1" = 'test' ] ; then # 3*
cd $PREFIX/src/$DIR/_build || exit 30
ctest || exit 31
elif [ "$1" = 'refresh' ] ; then # 6*
cd $PREFIX/src/$DIR || exit 60
/usr/local/sbin/base_diff_from_dst.bash $ROLE || exit 6$?
elif [ "$1" = 'update' ] ; then # 7*
ols_are_we_connected || exit 0
cd $PREFIX/src/$DIR || exit 70
git pull || exit 7$?
fi

View File

@ -0,0 +1,75 @@
#/bin/sh
# -*- mode: sh; tab-width: 8; encoding: utf-8-unix -*-
PREFIX=/usr/local
[ -f /usr/local/etc/testforge/testforge.bash ] && \
. /usr/local/etc/testforge/testforge.bash
ROLE=toxcore
PYVER=3
P="BASE_PYTHON${PYVER}_MINOR"
[ -z "$PYTHON_MINOR" ] && PYTHON_MINOR="$(eval echo \$$P)"
PYTHON_EXE_MSYS=$PREFIX/bin/python$PYVER.bash
PYTHON_EXE=$PYTHON_EXE_MSYS
DESC=""
. /var/local/src/var_local_src.bash || exit 1
SITE_PACKAGES_MSYS=$PREFIX/$LIB/python$PYTHON_MINOR/site-packages
HTTP_DIR=$PREFIX/net/Http
DIR=tox_profile
MOD=$DIR
GIT_HUB=git.plastiras.org
GIT_USER=emdee
GIT_DIR=$DIR
# tox_profile
cd $PREFIX/src || exit 2
WD=$PWD
if [ "$#" -eq 0 ] ; then
if [ ! -d "$DIR" ] ; then
if [ ! -d "$PREFIX/net/Git/$GIT_HUB/$GIT_USER/$GIT_DIR" ] ; then
ols_are_we_connected || exit 0
[ -d "$PREFIX/net/Git/$GIT_HUB/$GIT_USER" ] || \
mkdir "$PREFIX/net/Git/$GIT_HUB/$GIT_USER"
( cd "$PREFIX/net/Git/$GIT_HUB/$GIT_USER" && \
git clone "https://$GIT_HUB/$GIT_USER/$GIT_DIR" ) ||\
exit 2
( cd "$PREFIX/net/Git/$GIT_HUB/$GIT_USER" && \
git config user emdee && \
git config email emdee@ )
fi
cp -rip "$PREFIX/net/Git/$GIT_HUB/$GIT_USER/$GIT_DIR" . || exit 3
fi
python$PYVER.bash -c 'import namedlist' || \
pip$PYVER.bash install namedlist
cd $DIR || exit 4
[ -f __init__.py ] || touch __init__.py
# "$PYTHON_EXE_MSYS" -c "import $MOD" 2>/dev/null || exit 10
exit 0
elif [ $1 = 'check' ] ; then # 1*
# "$PYTHON_EXE_MSYS" -c "import $MOD" 2>/dev/null || exit 10
:
elif [ "$1" = 'lint' ] ; then # 2*
[ -n "$PYVER" ] || return 20
pylint -E --recursive y || exit 2$?
elif [ "$1" = 'test' ] ; then # 3*
cd $PREFIX/src/$DIR/$DIR || exit 32
$PYTHON_EXE_MSYS tox_savefile_test.bash \
>> $WD/$DIR/test.log 2>&1 || \
{ ERROR "$MOD code $?" ; cat $WD/$DIR/test.log ; exit 35 ; }
elif [ "$1" = 'refresh' ] ; then # 6*
cd $PREFIX/src/$DIR || exit 60
fi

View File

@ -0,0 +1,43 @@
#!/bin/bash
# -*- mode: sh; tab-width: 8; coding: utf-8-unix -*-
prog=`basename $0 .bash`
ROLE=toxcore
PREFIX=/var/local
# we install into /var/local/bin and it takes precedence
# export PATH=$PREFIX/bin:$PATH
#. /var/local/src/var_local_src.bash || exit 2
[ `id -u` -eq 0 ] && ERROR $prog should not be run as root && exit 3
if [ "$#" -eq 0 ] ; then
cd $PREFIX/src || exit 2
WD=$PWD
bash c-toxcore.bash || exit 3$?
bash tox_profile.bash || 4$?
# sh toxcore_docker.bash || exit 4$?
exit 0
elif [ "$1" = 'check' ] ; then
exit 0
ols_run_checks_pip3
ols_run_tests_exit check || exit 10$?
exit $?
elif [ "$1" = 'lint' ] ; then
exit 0
ols_run_tests_shellcheck $ROLE || exit 2$?
ols_run_tests_exit $1 || exit 21$?
ols_run_tests_pylint || exit 22$?
elif [ "$1" = 'test' ] ; then
exit 0
ols_run_tests_exit $1 || exit 30$?
ols_gentoo_test_imports || exit 32$?
#hangs /usr/bin/expect gpgkey_test_gpg.exp foobar || exit 31$?
fi

View File

@ -24,7 +24,7 @@
when:
- item != '' and item != []
- BASE_ARE_CONNECTED|default('') != ''
- "{{ ansible_virtualization_role|replace('NA', 'host') == 'host' }}"
- "ansible_virtualization_role|replace('NA', 'host') == 'host'"
- name: install toxcore packages GUEST
environment: "{{ portage_proxy_env }}"
@ -58,19 +58,20 @@
[ -f "/etc/local.d/rc.local.start" ] && exit 0
echo /etc/rc.local > /etc/local.d/rc.local.start
chmod 755 /etc/local.d/rc.local.start
if ! grep consolefont /etc/rc.local ; then
cat >> /etc/rc.local << EOF
/etc/init.d/consolefont stop; /etc/init.d/consolefont start
stty -F /dev/tty1 cols 80 rows 24
grep vda /proc/partitions && \
e2label /dev/vda3 root && \
e2label /dev/vda1 boot
sed -e 's/^#L/L/' -i /etc/fstab
EOF
fi
chmod 755 /etc/rc.local
bash /etc/rc.local
exit 0
when:
- not ansible_check_mode
- BOX_SERVICE_MGR != 'systemd' # maybe
- "ansible_virtualization_role|replace('NA', 'host') != 'host'"
# safe ones
- block:
@ -167,5 +168,6 @@
FCFLAGS="${CFLAGS}"
FFLAGS="${CFLAGS}"
when: true
when:
- "{{ ansible_virtualization_role|replace('NA', 'host') != 'host' }}"

View File

@ -0,0 +1,275 @@
# -*- mode: yaml; indent-tabs-mode: nil; tab-width: 2; coding: utf-8-unix -*-
---
- name: "proxy libvirt_whonix.yml"
debug:
verbosity: 1
msg: "proxy libvirt_whonix.yml PROXY_FEATURES={{PROXY_FEATURES}}"
- name: "hulahoop .asc"
environment: "{{ shell_proxy_env }}"
shell: |
base="hulahoop"
URL="www.whonix.org/$base"
dir="{{HARDEN_VAR_LOCAL}}/net/Http/"
[ -d $dir ] || mkdir $dir
[ -f $dir/$URL.asc ] || wget {{BASE_WGET_ARGS}} -xc -P $dir https://$URL.asc || exit 3
# FixMe:
gpg --import $dir/$URL.asc > /tmp/V$$.out 2>&1
[ $? -eq 0 ] || exit 1$?
grep 'imported: 1' /tmp/V$$.out
exit 0
args:
creates: "{{HARDEN_VAR_LOCAL}}/net/Http/www.whonix.org/hulahoop.asc"
when:
- not ansible_check_mode
- BASE_ARE_CONNECTED|default('') != ''
ignore_errors: true
- block:
- name: "/etc/libvirt/virtlogd.conf"
lineinfile:
path: /etc/libvirt/virtlogd.conf
create: yes
mode: 0755
insertafter: BOF
line: '{{item.key}}="{{item.val}}"'
regexp: "^#{{item.key}}.*"
with_items:
- key: log_filters
val: "1:logging 4:object 4:json 4:event 1:util"
- key: log_outputs
val: "3:file:/var/log/libvirt/virtlogd.log"
when:
- "'libvirt' in BOX_HOSTVMS_FEATURES or BOX_WHONIX_PROXY_HOST != ''"
- "ansible_virtualization_role|replace('NA', 'host') == 'host'"
- name: /etc/sysctl.d/70_testforge_libvirt.conf
blockinfile:
dest: /etc/sysctl.d/70_testforge_libvirt.conf
create: yes
marker: "# {mark} ANSIBLE MANAGED BLOCK ip_forward"
block: |
# enisables packet forwarding - required on the host for libvirt
net.ipv4.ip_forward = 1
# Disables IP dynaddr
#net.ipv4.ip_dynaddr = 0
# Disable ECN
#net.ipv4.tcp_ecn = 0
when:
- false # use lineinfile on the file it is already in
- "'hostvms' in ROLES|default([]) or 'toxcore' in ROLES|default([]) or 'privacy' in ROLES|default([])"
- "ansible_virtualization_role|replace('NA', 'host') == 'host'"
# FixMe: WTF?
# The destination directory (/etc/sysctl.d) is not writable by the current user.
ignore_errors: true
- block:
# https://www.whonix.org/wiki/KVM/Whonix_Signing_Key
- name: verify an image download.whonix.org/libvirt
shell: |
HTTP_DIR=/g/Privacy/net/Http/
URL=download.whonix.org/libvirt/{{PRIV_WHONIX_VERSION}}/Whonix-XFCE-{{PRIV_WHONIX_VERSION}}.libvirt.xz
[ -f $HTTP_DIR/$URL ] || \
wget -c {{ BASE_WGET_ARGS }} -P $HTTP_DIR https://$URL https://$URL.asc || exit 3
gpg --verify $HTTP_DIR/$URL.asc $HTTP_DIR/$URL >/tmp/V$$.out
[ $? -ne 0 ] && exit $?
grep 'Good signature from' /tmp/V$$.out || exit 11
grep ' key fingerprint: 04EF 2F66 6D36 C354 058B 9DD4 50C7 8B6F 9FF2 EC85' /tmp/V$$.out || exit 12
# FixMe:
ignore_errors: true
when:
- BASE_ARE_CONNECTED|default('') != ''
- name: untar
shell: |
HTTP_DIR=/g/Privacy/net/Http/
URL=download.whonix.org/libvirt/{{PRIV_WHONIX_VERSION}}/Whonix-XFCE-{{PRIV_WHONIX_VERSION}}.libvirt.xz
CDIR={{PROXY_GATEWAY_QEMU_DIR}}
[ -d $CDIR ] || mkdir -p $CDIR
cd $CDIR || exit
[ -f WHONIX_DISCLAIMER ] || tar xvfJ $HTTP_DIR/$URL
args:
creates: "{{PROXY_GATEWAY_QEMU_DIR}}/Whonix-XFCE-{{PRIV_WHONIX_VERSION}}.sha256sums.asc"
- name: sha256sum -c Whonix-XFCE-{{PRIV_WHONIX_VERSION}}.sha256sums.asc
shell: |
CDIR={{PROXY_GATEWAY_QEMU_DIR}}
[ -d $CDIR ] || exit 1
gpg --verify Whonix-XFCE-{{PRIV_WHONIX_VERSION}}.sha256sums.asc Whonix-XFCE-{{PRIV_WHONIX_VERSION}}.sha256sums >/tmp/V$$.out
[ $? -ne 0 ] && exit $?
grep 'OK' /tmp/V$$.out || exit 11
- name: sha256sum -c Whonix-XFCE-{{PRIV_WHONIX_VERSION}}.sha256sums
shell: |
CDIR={{PROXY_GATEWAY_QEMU_DIR}}
[ -d $CDIR ] || exit 1
sha256sum -c Whonix-XFCE-{{PRIV_WHONIX_VERSION}}.sha256sums
- name: touch WHONIX_BINARY_LICENSE_AGREEMENT_accepted
shell: |
CDIR={{PROXY_GATEWAY_QEMU_DIR}}
cd $CDIR || exit 1
[ -f WHONIX_BINARY_LICENSE_AGREEMENT_accepted ] || \
touch WHONIX_BINARY_LICENSE_AGREEMENT_accepted
- name: net-define $DIR/Whonix_external*.xml
shell: |
CDIR={{PROXY_GATEWAY_QEMU_DIR}}
virsh net-list --all | grep Whonix-External && exit 0
cd $CDIR || exit 1
virsh -c qemu:///system net-define $DIR/Whonix_external*.xml
- name: virsh net-info Whonix-Internal
shell: |
CDIR={{PROXY_GATEWAY_QEMU_DIR}}
virsh net-list --all | grep Whonix-Internal && exit 0
cd $CDIR || exit 1
virsh -c qemu:///system net-define $DIR/Whonix_internal*.xml
- name: virsh define Whonix-Gateway
shell: |
CDIR={{PROXY_GATEWAY_QEMU_DIR}}
virsh list --all | grep Whonix-Gateway && exit 0
cd $CDIR || exit 1
grep -q "{{PRIV_WHONIX_GATE_MEM}}" Whonix-Gateway-XFCE-{{PRIV_WHONIX_VERSION}}.xml || \
sed -e "s@<currentMemory unit='KiB'>524288</currentMemory>@<currentMemory unit='KiB'>{{PRIV_WHONIX_GATE_MEM}}</currentMemory>@" -i \
Whonix-Gateway-XFCE-{{PRIV_WHONIX_VERSION}}.xml
virsh define Whonix-Gateway-XFCE-{{PRIV_WHONIX_VERSION}}.xml --validate
args:
creates: /etc/libvirt/qemu/Whonix-Gateway.xml
- name: virsh define Whonix-Workstation
shell: |
CDIR={{PROXY_GATEWAY_QEMU_DIR}}
virsh list --all | grep Whonix-Workstation && exit 0
cd $CDIR || exit 1
virsh define Whonix-Workstation-XFCE-{{PRIV_WHONIX_VERSION}}.xml --validate
args:
creates: /etc/libvirt/qemu/Whonix-Workstation.xml
when:
- false
# FixMe should only edit if not running
- name: /etc/libvirt/qemu/*xml
blockinfile:
dest: "{{item}}"
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK proxy whonix -->"
mode: 0660
owner: "{{BOX_ROOT_USER}}"
group: "{{BOX_ROOT_GROUP}}"
create: no
block: |
<channel type='unix'>
<source mode='bind'/>
<target type='virtio' name='org.qemu.guest_agent.0'/>
<address type='virtio-serial' controller='0' bus='0' port='2'/>
</channel>
with_fileglob: /etc/libvirt/qemu/Whonix*.xml
when:
- false # use xml instead
- "ansible_virtualization_role|replace('NA', 'host') == 'host'"
# console=tty0 console=ttyS0,115200n8 spectre_v2=on spec_store_bypass_disable=on tsx=off tsx_async_abort=full,nosmt mds=full,nosmt l1tf=full,force nosmt=force kvm.nx_huge_pages=force random.trust_cpu=off intel_iommu=on efi=disable_early_pci_dma slab_nomerge slub_debug=FZP page_poison=1 mce=0 pti=on vsyscall=none extra_latent_entropy
# from Gateway
- block:
# /usr/portage/app-emulation/libvirt/files/libvirtd.init-r19 after livirt-7.2.0a
- name: /usr/local/sbin/proxy_whonix-libvirt-install.bash
shell: |
cp -p /usr/local/etc/init.d/libvirtd.openrc /etc/init.d/livirtd
chmod 755 /etc/init.d/libvirtd
args:
creates: /etc/init.d/libvirtd
- name: "/etc/libvirt/qemu.conf"
blockinfile:
dest: "/etc/libvirt/qemu.conf"
create: false
marker: "# {mark} ANSIBLE MANAGED BLOCK hostvms vms {{item.name}}"
insertafter: '^#* *{{item.name}}.*'
block: |
{{ item.name }} = {{ item.val }}
with_items:
- { name: 'migration_address', val: '"0.0.0.0"' }
- { name: 'user', val: '"root"' }
#? why qemu - serverfault sez must be root for passthrough
# root is not enough for passthorugh mounting rw
- { name: 'group', val: '"root"' }
#? why
- { name: 'dynamic_ownership', val: '1' }
#?? why
# error : virGetUserID:1041 : invalid argument: Failed to parse user 'tss'
# - { name: 'swtpm_user', val: '"tss"' }
#?? why
# error : virGetGroupID:1124 : invalid argument: Failed to parse group 'tss'
# - { name: 'swtpm_group', val: '"tss"' }
# - { name: '', val: '' }
ignore_errors: true
# required
when: not ansible_check_mode
- name: "/etc/libvirt/libvirtd.conf"
blockinfile:
dest: /etc/libvirt/libvirtd.conf
create: yes
marker: "# {mark} ANSIBLE MANAGED BLOCK hostvms vms {{item.name}}"
insertafter: '^#* *{{item.name}}.*'
block: |
{{ item.name }} = "{{ item.val }}"
with_items:
#listen_addr = "192.168.0.1"
- { name: "listen_addr", val: "127.0.0.1" }
#_sock_group = "libvirt"
- { name: "unix_sock_group", val: "libvirt" }
- { name: "unix_sock_ro_perms", val: "0750" }
- { name: "unix_sock_rw_perms", val: "0770" }
#ca_file = "/etc/pki/CA/cacert.pem"
- { name: "ca_file", val: "{{ PLAY_CA_CERT }}" }
- { name: "auth_unix_ro", val: "none" }
- { name: "auth_unix_rw", val: "none" }
- { name: "log_filters", val: "1:qemu 1:libvirt 4:object 4:json 4:event 1:util" }
- { name: "log_outputs", val: "3:file:/var/log/libvirtd.log" }
#
# - { name: "", val: "" }
ignore_errors: true
# required
when: not ansible_check_mode
notify: restart libvirtd
- name: /etc/modprobe.d/nbd.conf"
shell: |
file="/etc/modprobe.d/nbd.conf"
[ -f $file ] || echo >$file options nbd max_part=16
args:
creates: /etc/modprobe.d/nbd.conf
- name: "/etc/default/libvirt-guests"
lineinfile:
path: /etc/default/libvirt-guests
create: yes
mode: 0755
insertafter: BOF
line: '{{item.key}}="{{item.val}}"'
regexp: "^#{{item.key}}.*"
with_items:
- key: ON_BOOT
val: ignore
when:
- "ansible_distribution == 'Debian'"
- name: /usr/local/sbin/proxy_whonix-libvirt-install.bash
shell: |
/usr/local/sbin/proxy_whonix-libvirt-install.bash
args:
creates: /etc/libvirt/qemu/Whonix-Gateway.xml
when:
- ansible_virtualization_role|replace('NA', 'host') == 'host'

View File

@ -187,6 +187,7 @@
- LOOP_ITEM != '' and LOOP_ITEM != []
with_items:
- "vms"
- "{{ 'libvirt_whonix' if (BOX_WHONIX_PROXY_HOST != '' or 'libvirt' in BOX_HOSTVMS_FEATURES or 'whonix' in BOX_TOXCORE_FEATURES) else [] }}"
loop_control:
loop_var: LOOP_ITEM

View File

@ -0,0 +1,75 @@
#!/bin/bash
# -*- mode: sh; fill-column: 75; tab-width: 8; coding: utf-8-unix -*-
prog=`basename $0 .bash`
PREFIX=/usr/local
[ -f /usr/local/bin/usr_local_tput.bash ] && \
. /usr/local/bin/usr_local_tput.bash || {
DBUG() { echo DEBUG $* ; }
INFO() { echo INFO $* ; }
WARN() { echo WARN $* ; }
ERROR() { echo ERROR $* ; }
}
ROLE=toxcore
BOX=gentoo1
EXE=python3.sh
export PLAY_ANSIBLE_SRC=/o/var/local/src/play_tox
yamllint -c $PLAY_ANSIBLE_SRC/.yamllint.rc $PLAY_ANSIBLE_SRC/hosts.yml || {
ERROR
exit 1
}
[ "$#" -eq 0 ] && set -- build
# put these values in $BASE_SRC_ANSIBLE/hosts.yml
[ -n "$BOX_NBD_FILES" ] || \
BOX_NBD_FILES=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_FILES $BOX)
[ -n "$BOX_NBD_BASE_QCOW" ] || \
BOX_NBD_BASE_QCOW=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_BASE_QCOW $BOX)
[ -n "$BOX_NBD_LOGLEVEL" ] || \
BOX_NBD_LOGLEVEL=$( /usr/local/bin/ansible_get_inventory.bash BOX_NBD_LOGLEVEL $BOX)
[ -z "$BOX_NBD_LOGLEVEL" ] && BOX_NBD_LOGLEVEL=20
DBUG "BOX_NBD_LOGLEVEL=$BOX_NBD_LOGLEVEL"
declare -a RARGS
RARGS=(-j 1)
if [ -n "$BOX_NBD_FILES" ] && [ -d "$BOX_NBD_FILES" ] ; then
PORTAGE="$BOX_NBD_FILES"/portage-20231223.tar.xz
[ -f $PORTAGE ] && RARGS+=(--portage $PORTAGE)
STAGE3="$BOX_NBD_FILES"/stage3-amd64-openrc-20231217T170203Z.tar.xz
[ -f $STAGE3 ] && RARGS+=(--stage3 $STAGE3)
RARGS+=( --download-dir $BOX_NBD_FILES )
fi
[ -n "$BOX_NBD_BASE_QCOW" ] && \
BOX_NBD_BASE_DIR=$(dirname "$BOX_NBD_BASE_QCOW") && \
[ -d "$BOX_NBD_BASE_DIR" ] && \
RARGS+=( --temporary-dir "$BOX_NBD_BASE_DIR" )
export BOX_NBD_OVERLAY_DIR
LOGLEVEL=20
JSON=$PLAY_ANSIBLE_SRC/src/ansible_gentooimgr/gentooimgr/configs/base.json
if [ -n "$JSON" ] && [ -f "$JSON" ] ; then
PKGS=`yq .packages.base < $JSON`
DBUG gentooimgr "${PKGS}"
RARGS+=( --config "$JSON" )
fi
# default --kernel-dir /usr/src/linux
RARGS+=( --config-base --loglevel $LOGLEVEL --days 27 --openrc --format qcow2 )
DBUG gentooimgr "${RARGS[@]}" "$@"
PYTHONPATH=$PLAY_ANSIBLE_SRC/src/ansible_gentooimgr $EXE \
-m gentooimgr \
"${RARGS[@]}" "$@"
retval=$?
[ $retval -gt 0 ] && exit 1$retval
#[ -f $BOX_NBD_OVERLAY_DIR/images/$BOX_NBD_OVERLAY_NAME.img ] && \
# INFO $BOX_NBD_OVERLAY_DIR/images/$BOX_NBD_OVERLAY_NAME.img || {
# ERROR NO $BOX_NBD_OVERLAY_DIR/images/$BOX_NBD_OVERLAY_NAME.img ; exit 2$retval ; }
exit 0

View File

@ -1,360 +0,0 @@
"""Configure a Gentoo guest with cloud image settings
This step keeps track of how far it's gotten, so re-running this command
will continue on if an error was to occur, unless --start-over flag is given.
"""
import os
import sys
import shutil
import configparser
from subprocess import Popen, PIPE
import logging
import traceback
import gentooimgr.config
import gentooimgr.configs
import gentooimgr.common
import gentooimgr.chroot
import gentooimgr.kernel
from gentooimgr import LOG
from gentooimgr import HERE
from gentooimgr.configs import *
FILES_DIR = os.path.join(HERE, "..")
def step1_diskprep(args, cfg):
LOG.info("\t:: Step 1: Disk Partitioning")
# http://rainbow.chard.org/2013/01/30/how-to-align-partitions-for-best-performance-using-parted/
# http://honglus.blogspot.com/2013/06/script-to-automatically-partition-new.html
cmds = [
['parted', '-s', f'{cfg.get("disk")}', 'mklabel', 'msdos'],
['parted', '-s', f'{cfg.get("disk")}', 'mkpart', 'primary', '2048s', '100%'],
['partprobe'],
['mkfs.ext4', '-FF', f'{cfg.get("disk")}{cfg.get("partition", 1)}']
]
for c in cmds:
proc = Popen(c, stdout=PIPE, stderr=PIPE)
stdout, stderr = proc.communicate()
completestep(1, "diskprep")
def step2_mount(args, cfg):
LOG.info(f'\t:: Step 2: Mounting {gentooimgr.config.GENTOO_MOUNT}')
proc = Popen(["mount", f'{cfg.get("disk")}{cfg.get("partition")}', cfg.get("mountpoint")])
proc.communicate()
completestep(2, "mount")
def step3_stage3(args, cfg):
LOG.info(f'\t:: Step 3: Stage3 Tarball')
stage3 = cfg.get("stage3") or args.stage3 # FIXME: auto detect stage3 images in mountpoint and add here
if not stage3:
stage3 = gentooimgr.common.stage3_from_dir(FILES_DIR)
proc = Popen(["tar", "xpf", os.path.abspath(stage3), "--xattrs-include='*.*'", "--numeric-owner", "-C",
f'{cfg.get("mountpoint")}'])
proc.communicate()
completestep(3, "stage3")
def step4_binds(args, cfg):
LOG.info(f'\t:: Step 4: Binding Filesystems')
gentooimgr.chroot.bind(verbose=False)
completestep(4, "binds")
def step5_portage(args, cfg):
LOG.info(f'\t:: Step 5: Portage')
portage = cfg.get("portage") or args.portage
if not portage:
portage = gentooimgr.common.portage_from_dir(FILES_DIR)
proc = Popen(["tar", "xpf", portage, "-C", f"{cfg.get('mountpoint')}/usr/"])
proc.communicate()
# Edit portage
portage_env = os.path.join(cfg.get("mountpoint"), 'etc', 'portage', 'env')
os.makedirs(portage_env, exist_ok=True)
with open(os.path.join(portage_env, 'singlejob.conf'), 'w') as f:
f.write('MAKEOPTS="-j1"\n')
env_path = os.path.join(cfg.get("mountpoint"), 'etc', 'portage', 'package.env')
with open(env_path, 'w') as f:
f.write("app-portage/eix singlejob.conf\ndev-util/maturin singlejob.conf\ndev-util/cmake singlejob.conf")
completestep(5, "portage")
def step6_licenses(args, cfg):
LOG.info(f'\t:: Step 6: Licenses')
license_path = os.path.join(cfg.get("mountpoint"), 'etc', 'portage', 'package.license')
os.makedirs(license_path, exist_ok=True)
for f, licenses in cfg.get("licensefiles", {}).items():
with open(os.path.join(license_path, f), 'w') as f:
f.write('\n'.join(licenses))
completestep(6, "license")
def step7_repos(args, cfg):
LOG.info(f'\t:: Step 7: Repo Configuration')
repo_path = os.path.join(cfg.get("mountpoint"), 'etc', 'portage', 'repos.conf')
os.makedirs(repo_path, exist_ok=True)
# Copy from template
repo_file = os.path.join(repo_path, 'gentoo.conf')
shutil.copyfile(
os.path.join(cfg.get("mountpoint"), 'usr', 'share', 'portage', 'config', 'repos.conf'),
repo_file)
# Regex replace lines
cp = configparser.ConfigParser()
for repofile, data in cfg.get("repos", {}).items():
cp.read(cfg.get("mountpoint") + repofile) # repofile should be absolute path, do not use os.path.join.
for section, d in data.items():
if section in cp:
for key, val in d.items():
# Replace everything after the key with contents of value.
# Sed is simpler than using regex for this purpose.
cp.set(section, key, val)
else:
sys.stderr.write(f"\tWW No section {section} in {repofile}\n")
cp.write(open(cfg.get("mountpoint") + repofile, 'w'))
completestep(7, "repos")
def step8_resolv(args, cfg):
LOG.info(f'\t:: Step 8: Resolv')
proc = Popen(["cp", "--dereference", "/etc/resolv.conf", os.path.join(cfg.get("mountpoint"), 'etc')])
proc.communicate()
# Copy all step files and python module to new chroot
os.system(f"cp /tmp/*.step {cfg.get('mountpoint')}/tmp")
os.system(f"cp -r . {cfg.get('mountpoint')}/mnt/")
completestep(8, "resolv")
def step9_sync(args, cfg):
LOG.info(f"\t:: Step 9: sync")
LOG.info("\t\t:: Entering chroot")
os.chroot(cfg.get("mountpoint"))
os.chdir(os.sep)
os.system("source /etc/profile")
proc = Popen(["emerge", "--sync", "--quiet"])
proc.communicate()
LOG.info("\t\t:: Emerging base")
proc = Popen(["emerge", "--update", "--deep", "--newuse", "--keep-going", "@world"])
proc.communicate()
completestep(9, "sync")
def step10_emerge_pkgs(args, cfg):
LOG.info(f"\t:: Step 10: emerge pkgs")
packages = cfg.get("packages", {})
for oneshot_up in packages.get("oneshots", []):
proc = Popen(["emerge", "--oneshot", "--update", oneshot_up])
proc.communicate()
for single in packages.get("singles", []):
proc = Popen(["emerge", "-j1", single])
proc.communicate()
LOG.info(f"KERNEL PACKAGES {packages.get('kernel')}")
if packages.get("kernel", []):
cmd = ["emerge", "-j", str(args.threads)] + packages.get("kernel", [])
proc = Popen(cmd)
proc.communicate()
cmd = ["emerge", "-j", str(args.threads), "--keep-going"]
cmd += packages.get("keepgoing", [])
proc = Popen(cmd)
proc.communicate()
cmd = ["emerge", "-j", str(args.threads)]
cmd += packages.get("base", [])
cmd += packages.get("additional", [])
cmd += packages.get("bootloader", [])
LOG.info(cmd)
proc = Popen(cmd)
proc.communicate()
completestep(10, "pkgs")
def step11_kernel(args, cfg):
# at this point, genkernel will be installed
LOG.info(f"\t:: Step 11: kernel")
proc = Popen(["eselect", "kernel", "set", "1"])
proc.communicate()
if not args.kernel_dist:
os.chdir(args.kernel_dir)
threads = str(gentooimgr.config.THREADS)
gentooimgr.kernel.build_kernel(args, cfg)
completestep(11, "kernel")
def step12_grub(args, cfg):
LOG.info(f"\t:: Step 12: kernel")
proc = Popen(["grub-install", cfg.get('disk')])
proc.communicate()
code = proc.returncode
if code != 0:
sys.stderr.write(f"Failed to install grub on {cfg.get('disk')}\n")
sys.exit(code)
with open("/etc/default/grub", 'w') as f:
f.write(f"{gentooimgr.kernel.GRUB_CFG}")
proc = Popen(["grub-mkconfig", "-o", "/boot/grub/grub.cfg"])
proc.communicate()
completestep(12, "grub")
def step13_serial(args, cfg):
LOG.info(f"\t:: Step 13: Serial")
os.system("sed -i 's/^#s0:/s0:/g' /etc/inittab")
os.system("sed -i 's/^#s1:/s1:/g' /etc/inittab")
completestep(13, "serial")
def step14_services(args, cfg):
LOG.info(f"\t:: Step 14: Services")
for service in ["acpid", "syslog-ng", "cronie", "sshd", "cloud-init-local", "cloud-init", "cloud-config",
"cloud-final", "ntpd", "nfsclient"]:
if args.profile == "systemd":
proc = Popen(["systemctl", "enable", service])
else:
proc = Popen(["rc-update", "add", service, "default"])
proc.communicate()
completestep(14, "services")
def step15_ethnaming(args, cfg):
LOG.info(f"\t:: Step 15: Eth Naming")
completestep(15, "networking")
def step16_sysconfig(args, cfg):
LOG.info(f"\t:: Step 16: Sysconfig")
with open("/etc/timezone", "w") as f:
f.write("UTC")
proc = Popen(["emerge", "--config", "sys-libs/timezone-data"])
proc.communicate()
with open("/etc/locale.gen", "a") as f:
f.write("en_US.UTF-8 UTF-8\nen_US ISO-8859-1\n")
proc = Popen(["locale-gen"])
proc.communicate()
proc = Popen(["eselect", "locale", "set", "en_US.utf8"])
proc.communicate()
proc = Popen(["env-update"])
proc.communicate()
with open('/etc/sysctl.d/swappiness.conf', 'w') as f:
f.write("vm.swappiness = 0\n")
modloadpath = os.path.join(os.sep, 'etc', 'modules-load.d')
os.makedirs(modloadpath, exist_ok=True)
with open(os.path.join(modloadpath, 'cloud-modules.conf'), 'w') as f:
f.write('\n'.join(gentooimgr.config.CLOUD_MODULES))
cloudcfg = os.path.join(os.sep, 'etc', 'cloud')
if not os.path.exists(cloudcfg):
os.makedirs(cloudcfg, exist_ok=True)
os.makedirs(os.path.join(cloudcfg, 'templates'), exist_ok=True)
with open(os.path.join(cloudcfg, 'cloud.cfg'), 'w') as cfg:
cfg.write(f"{CLOUD_YAML}")
os.chmod(os.path.join(cloudcfg, "cloud.cfg"), 0o644)
with open(os.path.join(cloudcfg, "templates", "hosts.gentoo.tmpl"), 'w') as tmpl:
tmpl.write(f"{HOST_TMPL}") # FIXME:
os.chmod(os.path.join(cloudcfg, "templates", "hosts.gentoo.tmpl"), 0o644)
proc = Popen("sed -i 's/domain_name\,\ domain_search\,\ host_name/domain_search/g' /etc/dhcpcd.conf", shell=True)
proc.communicate()
hostname = os.path.join(os.sep, 'etc', 'conf.d', 'hostname')
with open(hostname, 'w') as f:
f.write(f"{HOSTNAME}\n")
os.chmod(hostname, 0o644)
proc = Popen(["eix-update"])
proc.communicate()
os.remove(os.path.join(os.sep, 'etc', 'resolv.conf'))
completestep(16, "sysconfig")
def step17_fstab(args, cfg):
LOG.info(f"\t:: Step 17: fstab")
with open(os.path.join(os.sep, 'etc', 'fstab'), 'a') as fstab:
fstab.write(f"{cfg.get('disk')}\t/\text4\tdefaults,noatime\t0 1\n")
completestep(17, "fstab")
def completestep(step, stepname, prefix='/tmp'):
with open(os.path.join(prefix, f"{step}.step"), 'w') as f:
f.write("done.") # text in this file is not currently used.
def getlaststep(prefix='/tmp'):
i = 1
found = False
while not found:
if os.path.exists(f"{i}.step"):
i += 1
else:
found = True
return i
def stepdone(step, prefix='/tmp'):
return os.path.exists(os.path.join(prefix, f"{step}.step"))
def configure(args, config: dict):
# Load configuration
if not os.path.exists(gentooimgr.config.GENTOO_MOUNT):
if not args.force:
# We aren't in a gentoo live cd are we?
sys.stderr.write("Your system doesn't look like a gentoo live cd, exiting for safety.\n"
"If you want to continue, use --force option and re-run `python -m gentooimgr install` with your configuration\n")
sys.exit(1)
else:
# Assume we are root as per live cd, otherwise user should run this as root as a secondary confirmation
os.makedirs(gentooimgr.config.GENTOO_MOUNT)
# disk prep
cfg = config
# must be root for linux-6.1.52-pentoo/certs/signing_key.pem
if not stepdone(1): step1_diskprep(args, cfg)
# mount root
if not stepdone(2): step2_mount(args, cfg)
# extract stage
if not stepdone(3): step3_stage3(args, cfg)
# mount binds
if not stepdone(4): step4_binds(args, cfg)
# extract portage
if not stepdone(5): step5_portage(args, cfg)
# Set licenses
if not stepdone(6): step6_licenses(args, cfg)
# repos.conf
if not stepdone(7): step7_repos(args, cfg)
# portage env files and resolv.conf
if not stepdone(8): step8_resolv(args, cfg)
# emerge --sync
if not stepdone(9): step9_sync(args, cfg)
# bindist
if not stepdone(10): step10_emerge_pkgs(args, cfg)
# emerge packages
# configure & emerge kernel (use cloud configuration too)
if not stepdone(11): step11_kernel(args, cfg)
# grub
if not stepdone(12): step12_grub(args, cfg)
# enable serial console
if not stepdone(13): step13_serial(args, cfg)
# services
if not stepdone(14): step14_services(args, cfg)
# eth0 naming
# timezone
if not stepdone(15): step15_ethnaming(args, cfg)
# locale
# set some sysctl things
# set some dhcp things
# hostname
if not stepdone(16): step16_sysconfig(args, cfg)
# fstab
if not stepdone(17): step17_fstab(args, cfg)
# copy cloud cfg?
gentooimgr.chroot.unbind()
# Finish install processes like emaint and eix-update and news read

View File

@ -72,7 +72,7 @@ def main(args):
gentooimgr.kernel.build_kernel(args, configjson)
return 0
if __name__ == "__main__":
def oMainParser()
"""Gentoo Cloud Image Builder Utility"""
parser = argparse.ArgumentParser(prog="gentooimgr", description="Gentoo Image Builder Utility")
parser.add_argument("-c", "--config", nargs='?', type=pathlib.Path,
@ -161,9 +161,9 @@ if __name__ == "__main__":
parser_kernel = subparsers.add_parser('kernel', help="Build the kernel based on configuration and optional --kernel-dist flag")
return parser
args = parser.parse_args()
def oMainLogger(args)
assert args.loglevel < 59
if coloredlogs is not None:
# https://pypi.org/project/coloredlogs/
@ -175,11 +175,14 @@ if __name__ == "__main__":
else:
logging.basicConfig(level=args.loglevel) # logging.INFO
logging.basicConfig(level=args.loglevel)
return LOG
if __name__ == "__main__":
parser = oMainParser()
args = parser.parse_args()
oMainLogger(args)
isos = gentooimgr.common.find_iso(args.download_dir)
if args.action == "run" and args.iso is None and len(isos) > 1:
LOG.error(f"Error: multiple iso files were found in {args.download_dir}, please specify one using `--iso [iso]`")
sys.exit(1)
main(args)

View File

@ -314,6 +314,7 @@ def configure(args, config: dict):
os.makedirs(gentooimgr.config.GENTOO_MOUNT)
# disk prep
cfg = config
# must be root for linux-6.1.52-pentoo/certs/signing_key.pem
if not stepdone(1): step1_diskprep(args, cfg)
# mount root
if not stepdone(2): step2_mount(args, cfg)