diff --git a/ansible/ovf.yml b/ansible/ovf.yml new file mode 100644 index 0000000..766adc7 --- /dev/null +++ b/ansible/ovf.yml @@ -0,0 +1,9 @@ +--- +- name: MSVSphere OVF image + hosts: default + become: true + + roles: + - upgrade_and_reboot + - role: vmware_guest + - cleanup_vm_ovf diff --git a/ansible/roles/cleanup_vm_ovf/README.md b/ansible/roles/cleanup_vm_ovf/README.md new file mode 100644 index 0000000..10efcb4 --- /dev/null +++ b/ansible/roles/cleanup_vm_ovf/README.md @@ -0,0 +1,3 @@ +# cleanup_vm_ovf + +An Ansible role that cleans-up and deprovisions a virtual machine. diff --git a/ansible/roles/cleanup_vm_ovf/meta/main.yml b/ansible/roles/cleanup_vm_ovf/meta/main.yml new file mode 100644 index 0000000..84d6d46 --- /dev/null +++ b/ansible/roles/cleanup_vm_ovf/meta/main.yml @@ -0,0 +1,14 @@ +galaxy_info: + role_name: cleanup_vm_ovf + author: Eugene Zamriy + description: Cleans-up and deprovisions a VM + license: MIT + min_ansible_version: '2.5' + platforms: + - name: EL + versions: + - '8' + - '9' + galaxy_tags: [] + +dependencies: [] diff --git a/ansible/roles/cleanup_vm_ovf/tasks/main.yml b/ansible/roles/cleanup_vm_ovf/tasks/main.yml new file mode 100644 index 0000000..3252e53 --- /dev/null +++ b/ansible/roles/cleanup_vm_ovf/tasks/main.yml @@ -0,0 +1,151 @@ +--- +- name: Remove old kernels + ansible.builtin.shell: dnf remove -y $(dnf repoquery --installonly --latest-limit=-1 -q) + +- name: Delete DNF cache + ansible.builtin.command: dnf clean all + +- name: Find DNF history files + ansible.builtin.find: + paths: /var/lib/dnf + patterns: "history*" + register: dnf_history + +- name: Reset DNF history + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ dnf_history.files }}" + +- name: Find temporary files + ansible.builtin.find: + file_type: any + paths: + - /tmp + - /var/tmp + patterns: '*' + register: tmp_files + +- name: Remove temporary files + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ tmp_files.files }}" + +- name: Find SSH host keys + ansible.builtin.find: + paths: /etc/ssh + patterns: '*host*key*' + register: host_keys + +- name: Remove SSH host keys + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ host_keys.files }}" + +- name: Remove kickstart files + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /root/anaconda-ks.cfg + - /root/original-ks.cfg + +- name: Truncate files + ansible.builtin.command: "truncate -s 0 {{ item }}" + loop: + - /etc/machine-id + - /etc/resolv.conf + - /var/log/audit/audit.log + - /var/log/wtmp + - /var/log/lastlog + - /var/log/btmp + - /var/log/cron + - /var/log/maillog + - /var/log/messages + - /var/log/secure + - /var/log/spooler + +- name: Remove log folders. + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /var/log/anaconda + - /var/log/qemu-ga + - /var/log/tuned + - /var/lib/cloud + - /etc/hostname + - /etc/machine-info + - /var/lib/systemd/credential.secret + +- name: Find log files. + ansible.builtin.find: + paths: + - /var/log + - /var/log/sssd + patterns: '*log,*.old,*.log.gz,*.[0-9],*.gz,*-????????' + register: log_files + +- name: Remove log files + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ log_files.files }}" + +- name: Remove random-seed + ansible.builtin.file: + path: /var/lib/systemd/random-seed + state: absent + +- name: Disable root SSH login via password + ansible.builtin.file: + path: /etc/ssh/sshd_config.d/01-permitrootlogin.conf + state: absent + when: ansible_facts['distribution_major_version'] == '9' + +- name: Fill free space with zeroes + ansible.builtin.shell: dd if=/dev/zero of=/zeroed_file bs=1M oflag=direct || rm -f /zeroed_file + +- name: Detect swap partition + ansible.builtin.command: grep -oP '^/dev/[\w-]+' /proc/swaps + register: swaps + ignore_errors: true + +- name: Wipe out swap data + block: + - name: Get swap partition UUID + ansible.builtin.command: "blkid {{ swaps.stdout }} -s UUID -o value" + register: swap_blkid + + - name: Unmount swap partition + ansible.builtin.command: "swapoff {{ swaps.stdout }}" + + - name: Fill swap partition with zeroes + ansible.builtin.shell: "dd if=/dev/zero of={{ swaps.stdout }} bs=1M oflag=direct || /bin/true" + + - name: Format swap partition + ansible.builtin.command: "mkswap -U {{ swap_blkid.stdout }} -f {{ swaps.stdout }}" + + - name: Mount swap partition + ansible.builtin.command: "swapon {{ swaps.stdout }}" + when: swaps.rc == 0 + +- name: Sync disc + ansible.builtin.command: sync + +- name: Clear shell history + ansible.builtin.shell: history -c + +- name: Check if WALinuxAgent is installed + ansible.builtin.stat: + path: /usr/sbin/waagent + register: cleanup_vm_waagent + +- name: Deprovision WALinuxAgent + ansible.builtin.command: waagent -deprovision+user -force + when: cleanup_vm_waagent.stat.exists + +- name: Remove root password + ansible.builtin.shell: "echo root: | chpasswd" diff --git a/cloud-images-build.sh b/cloud-images-build.sh new file mode 100755 index 0000000..1bdb8ec --- /dev/null +++ b/cloud-images-build.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +PACKER_LOG_FILE='./packer.log' +SOURCES="$(for FILE in *.pkr.hcl; do cat $FILE | awk 'BEGIN{f=0} {if($1 ~ /build/ && $2 ~ /{/){f=1};if(f == 1){if($1 ~ /sources/ && $2 ~ /=/ && $3 ~ /\[/){f=2;next}};if(f == 2){if($1 ~ /\]/) {f=0}};if(f == 2){print $0}}' | sed -E 's/"//g;s/,//g;s/sources\.//g'; done)" + +SOURCE='' +NO_PAKER_INIT='' +NO_PKG_INSTALL='' + +# ------------------------------------------------------------------------------ +Usage() +{ + cat < [option] + source name: +EOF + for SRC in $SOURCES; do + echo -e "\t$SRC" + done +cat </dev/null; then + install edk2-ovmf libvirt libvirt-daemon-kvm + fi +fi + +[ -z "$NO_PAKER_INIT" ] && packer init -upgrade . || exit 1 + +PACKER_LOG=1 packer build -only=$SOURCE . 2>$PACKER_LOG_FILE || exit 1 + +if [ "$SOURCE" = "vmware-iso.msvsphere-9-ovf-x86_64" ]; then + OUT_DIR='./vmware-iso.msvsphere-9-ovf-x86_64_ovf' + rm -rf $OUT_DIR + mkdir $OUT_DIR + + /usr/lib/vmware-ovftool/ovftool --machineOutput --X:logFile=./ovftool.log --X:logLevel=verbose --exportFlags=extraconfig --allowExtraConfig --X:vCloudEnableGuestCustomization ./output-msvsphere-9-ovf-x86_64/msvsphere-9.4.vmx $OUT_DIR || exit 1 +fi diff --git a/http/msvsphere-9-gencloud.x86_64.ks b/http/msvsphere-9-gencloud.x86_64.ks index e1c3b36..5828b8e 100644 --- a/http/msvsphere-9-gencloud.x86_64.ks +++ b/http/msvsphere-9-gencloud.x86_64.ks @@ -1,8 +1,8 @@ # MSVSphere 9 Generic Cloud image kickstart file -url --url https://repo1.msvsphere-os.ru/msvsphere/9.3/BaseOS/x86_64/kickstart/ -repo --name=BaseOS --baseurl=https://repo1.msvsphere-os.ru/msvsphere/9.3/BaseOS/x86_64/os/ -repo --name=AppStream --baseurl=https://repo1.msvsphere-os.ru/msvsphere/9.3/AppStream/x86_64/os/ +url --url https://repo1.msvsphere-os.ru/msvsphere/9/BaseOS/x86_64/kickstart/ +repo --name=BaseOS --baseurl=https://repo1.msvsphere-os.ru/msvsphere/9/BaseOS/x86_64/os/ +repo --name=AppStream --baseurl=https://repo1.msvsphere-os.ru/msvsphere/9/AppStream/x86_64/os/ text skipx diff --git a/http/msvsphere-9-ovf.x86_64.ks b/http/msvsphere-9-ovf.x86_64.ks new file mode 100644 index 0000000..f24ce44 --- /dev/null +++ b/http/msvsphere-9-ovf.x86_64.ks @@ -0,0 +1,65 @@ +# MSVSphere 9 OVF kickstart file + +# TODO: change url to the kickstart one when we have it +url --url https://rsync.inferitos.ru/msvsphere/9/BaseOS/x86_64/kickstart/ +repo --name=BaseOS --baseurl=https://rsync.inferitos.ru/msvsphere/9/BaseOS/x86_64/kickstart/ +repo --name=AppStream --baseurl=https://rsync.inferitos.ru/msvsphere/9/AppStream/x86_64/kickstart/ + +text +skipx +eula --agreed +firstboot --disabled + +lang C.UTF-8 +keyboard us +timezone UTC --utc + +network --bootproto=dhcp +firewall --enabled --service=ssh +services --disabled="kdump" --enabled="chronyd,rsyslog,sshd" +selinux --enforcing + +bootloader --location=mbr +zerombr +clearpart --all --initlabel +autopart --type=plain --nohome --noboot --noswap + +rootpw --plaintext msvsphere + +reboot --eject + + +%packages --inst-langs=en +@core +bzip2 +dracut-config-generic +tar +usermode +-biosdevname +-dnf-plugin-spacewalk +-dracut-config-rescue +-iprutils +-iwl*-firmware +-langpacks-* +-mdadm +-open-vm-tools +-plymouth +-rhn* +sphere-release-identity-server +sphere-release-server +sphere-release +perl +%end + +# disable kdump service +%addon com_redhat_kdump --disable +%end + +%post + +yum clean all + +# permit root login via SSH with password authentication +echo "PermitRootLogin yes" > /etc/ssh/sshd_config.d/01-permitrootlogin.conf + +%end diff --git a/http/msvsphere-9-vagrant.x86_64.ks b/http/msvsphere-9-vagrant.x86_64.ks index f86b641..5b3e6b2 100644 --- a/http/msvsphere-9-vagrant.x86_64.ks +++ b/http/msvsphere-9-vagrant.x86_64.ks @@ -1,8 +1,8 @@ # MSVSphere 9 Vagrant boxes kickstart file -url --url https://repo1.msvsphere-os.ru/msvsphere/9.3/BaseOS/x86_64/kickstart/ -repo --name=BaseOS --baseurl=https://repo1.msvsphere-os.ru/msvsphere/9.3/BaseOS/x86_64/os/ -repo --name=AppStream --baseurl=https://repo1.msvsphere-os.ru/msvsphere/9.3/AppStream/x86_64/os/ +url --url https://repo1.msvsphere-os.ru/msvsphere/9/BaseOS/x86_64/kickstart/ +repo --name=BaseOS --baseurl=https://repo1.msvsphere-os.ru/msvsphere/9/BaseOS/x86_64/os/ +repo --name=AppStream --baseurl=https://repo1.msvsphere-os.ru/msvsphere/9/AppStream/x86_64/os/ text skipx diff --git a/msvsphere-9-ovf.pkr.hcl b/msvsphere-9-ovf.pkr.hcl new file mode 100644 index 0000000..201ab09 --- /dev/null +++ b/msvsphere-9-ovf.pkr.hcl @@ -0,0 +1,52 @@ +/** + * Packer template for building MSVSphere 9 ovf image. + */ + +source "vmware-iso" "msvsphere-9-ovf-x86_64" { + vm_name = "msvsphere-9.4" + iso_url = var.iso_url_9_x86_64 + iso_checksum = var.iso_checksum_9_x86_64 + boot_command = var.ovf_boot_cmd_9_x86_64_bios + boot_wait = var.boot_wait + cpus = var.cpus + memory = var.memory + disk_size = var.vmware_disk_size + headless = var.headless + http_directory = var.http_directory + guest_os_type = "centos-64" + shutdown_command = var.root_shutdown_command + ssh_username = var.ovf_ssh_username + ssh_password = var.ovf_ssh_password + ssh_timeout = var.ssh_timeout + version = 15 + vmx_data = { + "cpuid.coresPerSocket" : "1" + } + vmx_data_post = { + "memsize" : var.post_memory + "numvcpus" : var.post_cpus + } + vmx_remove_ethernet_interfaces = true +} + + +build { + sources = [ + "sources.vmware-iso.msvsphere-9-ovf-x86_64" + ] + provisioner "ansible" { + playbook_file = "ansible/ovf.yml" + galaxy_file = "ansible/requirements.yml" + roles_path = "ansible/roles" + collections_path = "ansible/collections" + ansible_env_vars = [ + "ANSIBLE_PIPELINING=True", + "ANSIBLE_REMOTE_TEMP=/tmp", + "ANSIBLE_SSH_ARGS='-o ControlMaster=no -o ControlPersist=180s -o ServerAliveInterval=120s -o TCPKeepAlive=yes -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa'" + ] + extra_arguments = [ + "--extra-vars", + "packer_provider=${source.type}" + ] + } +} diff --git a/packer-environment.pkr.hcl b/packer-environment.pkr.hcl index c9abcbd..9d306bb 100644 --- a/packer-environment.pkr.hcl +++ b/packer-environment.pkr.hcl @@ -1,5 +1,5 @@ packer { - required_version = "= 1.10.0" + required_version = "= 1.11.2" required_plugins { ansible = { diff --git a/variables.pkr.hcl b/variables.pkr.hcl index 3b0c98d..dd68530 100644 --- a/variables.pkr.hcl +++ b/variables.pkr.hcl @@ -233,3 +233,35 @@ variable "gencloud_ssh_password" { type = string default = "msvsphere" } + +/** + * OVF-specific settings. + */ + +variable "vmware_disk_size" { + description = "The VM disk size in megabytes" + type = number + default = 20000 +} + +variable "ovf_boot_cmd_9_x86_64_bios" { + description = "The boot command for x86_64 VMs in a BIOS mode" + type = list(string) + default = [ + " inst.text inst.gpt ", + "inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/msvsphere-9-ovf.x86_64.ks", + "" + ] +} + +variable "ovf_ssh_username" { + description = "A login to use for SSH authentication" + type = string + default = "root" +} + +variable "ovf_ssh_password" { + description = "A password to use for SSH authentication" + type = string + default = "msvsphere" +}