From c75cc2f669c5bf92b28d91a9a179d8821dbe101c Mon Sep 17 00:00:00 2001 From: Eugene Zamriy Date: Sun, 9 Jul 2023 15:47:19 +0300 Subject: [PATCH] Adds Generic Cloud image build configuration --- README.md | 25 +++- ansible/gencloud.yml | 8 ++ ansible/requirements.yml | 3 +- ansible/roles/disable_firstboot/README.md | 3 + .../roles/disable_firstboot/tasks/main.yml | 10 ++ ansible/roles/gencloud_guest/README.md | 3 + ansible/roles/gencloud_guest/files/ifcfg-eth0 | 9 ++ ansible/roles/gencloud_guest/meta/main.yml | 7 + ansible/roles/gencloud_guest/tasks/main.yml | 124 ++++++++++++++++++ ansible/roles/pvgrub_config/README.md | 3 + ansible/roles/pvgrub_config/tasks/main.yml | 52 ++++++++ .../pvgrub_config/templates/grub.conf.j2 | 7 + ansible/roles/qemu_guest_agent/README.md | 3 + ansible/roles/qemu_guest_agent/meta/main.yml | 16 +++ ansible/roles/qemu_guest_agent/tasks/main.yml | 5 + ansible/roles/setup_cloud_init/README.md | 10 ++ .../roles/setup_cloud_init/defaults/main.yml | 2 + ansible/roles/setup_cloud_init/tasks/main.yml | 24 ++++ http/msvsphere-9-gencloud.x86_64.ks | 73 +++++++++++ msvsphere-9-gencloud.pkr.hcl | 56 ++++++++ variables.pkr.hcl | 68 ++++++++++ 21 files changed, 509 insertions(+), 2 deletions(-) create mode 100644 ansible/gencloud.yml create mode 100644 ansible/roles/disable_firstboot/README.md create mode 100644 ansible/roles/disable_firstboot/tasks/main.yml create mode 100644 ansible/roles/gencloud_guest/README.md create mode 100644 ansible/roles/gencloud_guest/files/ifcfg-eth0 create mode 100644 ansible/roles/gencloud_guest/meta/main.yml create mode 100644 ansible/roles/gencloud_guest/tasks/main.yml create mode 100644 ansible/roles/pvgrub_config/README.md create mode 100644 ansible/roles/pvgrub_config/tasks/main.yml create mode 100644 ansible/roles/pvgrub_config/templates/grub.conf.j2 create mode 100644 ansible/roles/qemu_guest_agent/README.md create mode 100644 ansible/roles/qemu_guest_agent/meta/main.yml create mode 100644 ansible/roles/qemu_guest_agent/tasks/main.yml create mode 100644 ansible/roles/setup_cloud_init/README.md create mode 100644 ansible/roles/setup_cloud_init/defaults/main.yml create mode 100644 ansible/roles/setup_cloud_init/tasks/main.yml create mode 100644 http/msvsphere-9-gencloud.x86_64.ks create mode 100644 msvsphere-9-gencloud.pkr.hcl diff --git a/README.md b/README.md index 0ddc286..e49ce84 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,8 @@ $ packer version Packer v1.9.1 ``` -Install required Packer plugins: +In order to install required Packer plugins run the following command in the +project root: ```shell $ packer init -upgrade . @@ -38,6 +39,19 @@ $ firewall-cmd --zone=public --add-port=8000-9000/tcp --permanent $ firewall-cmd --reload ``` +You will also need to install either QEMU/KVM or VirtualBox or +VMWare Workstation, depending on what types of images you are going to build. + +For VirtualBox and VMWare Workstation just follow the official site +instructions. + +The QEMU/KVM installation instructions are provided below: + +```shell +$ dnf install @virtualization +$ dnf install edk2-ovmf +``` + ## Building images @@ -63,6 +77,15 @@ See the [variables.pkr.hcl](variables.pkr.hcl) file for other supported variables. +### Building Generic Cloud images + +Generic Cloud image build command: + +```shell +$ packer build -only=qemu.msvsphere-9-gencloud-x86_64 . +``` + + ### Building Vagrant boxes VirtualBox Vagrant box build command: diff --git a/ansible/gencloud.yml b/ansible/gencloud.yml new file mode 100644 index 0000000..5043682 --- /dev/null +++ b/ansible/gencloud.yml @@ -0,0 +1,8 @@ +--- +- name: MSVSphere Generic Cloud image + hosts: default + become: true + + roles: + - gencloud_guest + - cleanup_vm diff --git a/ansible/requirements.yml b/ansible/requirements.yml index 6585815..266b833 100644 --- a/ansible/requirements.yml +++ b/ansible/requirements.yml @@ -1,4 +1,5 @@ --- -collections: [] +collections: + - community.general roles: - name: ezamriy.vbox_guest diff --git a/ansible/roles/disable_firstboot/README.md b/ansible/roles/disable_firstboot/README.md new file mode 100644 index 0000000..797a085 --- /dev/null +++ b/ansible/roles/disable_firstboot/README.md @@ -0,0 +1,3 @@ +# disable_firstboot + +An Ansible role that disables the `firstboot` program run. diff --git a/ansible/roles/disable_firstboot/tasks/main.yml b/ansible/roles/disable_firstboot/tasks/main.yml new file mode 100644 index 0000000..76b11fa --- /dev/null +++ b/ansible/roles/disable_firstboot/tasks/main.yml @@ -0,0 +1,10 @@ +--- +- name: Disable firstboot + ansible.builtin.lineinfile: + path: /etc/sysconfig/firstboot + regexp: '^RUN_FIRSTBOOT=' + line: 'RUN_FIRSTBOOT=NO' + create: true + owner: root + group: root + mode: 0o644 diff --git a/ansible/roles/gencloud_guest/README.md b/ansible/roles/gencloud_guest/README.md new file mode 100644 index 0000000..cfcdf48 --- /dev/null +++ b/ansible/roles/gencloud_guest/README.md @@ -0,0 +1,3 @@ +# gencoud_guest + +An Ansible role that configures an MSVSphere Generic Cloud image system. diff --git a/ansible/roles/gencloud_guest/files/ifcfg-eth0 b/ansible/roles/gencloud_guest/files/ifcfg-eth0 new file mode 100644 index 0000000..bd1e650 --- /dev/null +++ b/ansible/roles/gencloud_guest/files/ifcfg-eth0 @@ -0,0 +1,9 @@ +TYPE=Ethernet +DEVICE=eth0 +BOOTPROTO=dhcp +PERSISTENT_DHCLIENT=1 +IPV6INIT=yes +IPV6_FAILURE_FATAL=no +PEERDNS=yes +USERCTL=yes +ONBOOT=yes diff --git a/ansible/roles/gencloud_guest/meta/main.yml b/ansible/roles/gencloud_guest/meta/main.yml new file mode 100644 index 0000000..6e4c5a7 --- /dev/null +++ b/ansible/roles/gencloud_guest/meta/main.yml @@ -0,0 +1,7 @@ +--- +dependencies: + - role: disable_firstboot + - role: setup_cloud_init + cloud_init_user: 'msvsphere' + - role: pvgrub_config + - role: qemu_guest_agent diff --git a/ansible/roles/gencloud_guest/tasks/main.yml b/ansible/roles/gencloud_guest/tasks/main.yml new file mode 100644 index 0000000..682a166 --- /dev/null +++ b/ansible/roles/gencloud_guest/tasks/main.yml @@ -0,0 +1,124 @@ +--- +- name: Remove firewalld and linux-firmware + ansible.builtin.dnf: + name: + - firewalld + - firewalld-filesystem + - ipset + - ipset-libs + - iptables + - python3-firewall + - python3-slip + - libnftnl + - libnfnetlink + - linux-firmware + state: absent + +- name: Install additional software + ansible.builtin.dnf: + name: + - cockpit-system + - cockpit-ws + - dnf-utils + - gdisk + - nfs-utils + - rsync + - tar + - tuned + - tcpdump + state: present + +- name: Find persistent-net.rules + ansible.builtin.find: + paths: /etc/udev/rules.d + patterns: 70* + register: net_rules + +- name: Delete persistent-net.rules + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + with_items: "{{ net_rules.files }}" + +- name: Configure /etc/sysconfig/network + ansible.builtin.lineinfile: + path: /etc/sysconfig/network + line: "{{ item }}" + with_items: + - NETWORKING=yes + - NOZEROCONF=yes + +- name: Configure /etc/sysconfig/network-scripts/ifcfg-eth0 + ansible.builtin.copy: + src: ifcfg-eth0 + dest: /etc/sysconfig/network-scripts/ifcfg-eth0 + owner: root + group: root + mode: 0644 + +- name: Disable consistent network device naming + ansible.builtin.file: + src: /dev/null + dest: /etc/udev/rules.d/80-net-name-slot.rules + owner: root + group: root + state: link + +- name: Disable virtual terminals allocation by logind + ansible.builtin.replace: + path: '/etc/systemd/logind.conf' + regexp: '^#?NAutoVTs=\d+' + replace: 'NAutoVTs=0' + +- name: Configure NetworkManager default DHCP timeout + community.general.ini_file: + path: /etc/NetworkManager/conf.d/dhcp.conf + section: connection + option: ipv4.dhcp-timeout + value: 300 + owner: root + group: root + mode: 0644 + seuser: system_u + +- name: Set default kernel package type to kernel + ansible.builtin.replace: + path: /etc/sysconfig/kernel + regexp: '^(DEFAULTKERNEL=).*$' + replace: '\1kernel' + +# https://bugzilla.redhat.com/show_bug.cgi?id=1849082#c7 +- name: Enable Xen support + block: + - name: Enable xen drivers in dracut + ansible.builtin.lineinfile: + path: /etc/dracut.conf.d/xen.conf + line: 'add_drivers+=" xen-netfront xen-blkfront "' + create: true + owner: root + group: root + mode: 0644 + + - name: Upgrade initramfs + ansible.builtin.command: dracut -f --regenerate-all + when: ansible_facts['architecture'] == 'x86_64' + +- name: Add msvsphere user to /etc/sudoers + ansible.builtin.lineinfile: + path: /etc/sudoers + line: "msvsphere\tALL=(ALL)\tNOPASSWD: ALL" + state: present + +- name: Set virtual-guest as default profile for tuned + ansible.builtin.lineinfile: + path: /etc/tuned/active_profile + line: virtual-guest + create: yes + +- name: Regenerate the initramfs + ansible.builtin.command: dracut -f --regenerate-all + +- name: Disable root login + ansible.builtin.user: + name: root + password: '!!' diff --git a/ansible/roles/pvgrub_config/README.md b/ansible/roles/pvgrub_config/README.md new file mode 100644 index 0000000..4819a57 --- /dev/null +++ b/ansible/roles/pvgrub_config/README.md @@ -0,0 +1,3 @@ +# pvgrub_config + +An Ansible role that generates pvgrub bootloader configuration. diff --git a/ansible/roles/pvgrub_config/tasks/main.yml b/ansible/roles/pvgrub_config/tasks/main.yml new file mode 100644 index 0000000..ef2e6e8 --- /dev/null +++ b/ansible/roles/pvgrub_config/tasks/main.yml @@ -0,0 +1,52 @@ +--- +- name: Get root partition UUID + ansible.builtin.command: findmnt / -o UUID -n + changed_when: false + register: root_uuid + +- name: Get default boot record title + ansible.builtin.command: grubby --default-title + changed_when: false + register: grub_rec_title + +- name: Get default kernel path + ansible.builtin.command: grubby --default-kernel + changed_when: false + register: grub_kernel_path + +- name: Get default initrd path + ansible.builtin.shell: grubby --info=DEFAULT | grep initrd | grep -oP 'initrd="\K\S+?.img' + changed_when: false + register: grub_initrd_path + +- name: Create /boot/grub directory + ansible.builtin.file: + path: /boot/grub + state: directory + owner: root + group: root + mode: 0755 + +- name: Render /boot/grub/grub.conf + ansible.builtin.template: + src: grub.conf.j2 + dest: /boot/grub/grub.conf + owner: root + group: root + mode: 0644 + +- name: Create /boot/grub/menu.lst symlink + ansible.builtin.file: + src: grub.conf + dest: /boot/grub/menu.lst + owner: root + group: root + state: link + +- name: Create /etc/grub.conf symlink + ansible.builtin.file: + src: /boot/grub/grub.conf + dest: /etc/grub.conf + owner: root + group: root + state: link diff --git a/ansible/roles/pvgrub_config/templates/grub.conf.j2 b/ansible/roles/pvgrub_config/templates/grub.conf.j2 new file mode 100644 index 0000000..a548697 --- /dev/null +++ b/ansible/roles/pvgrub_config/templates/grub.conf.j2 @@ -0,0 +1,7 @@ +default=0 +timeout=0 + +title {{ grub_rec_title.stdout }} + root (hd0) + kernel {{ grub_kernel_path.stdout }} ro root=UUID={{ root_uuid.stdout }} console=hvc0 LANG=en_US.UTF-8 + initrd {{ grub_initrd_path.stdout }} diff --git a/ansible/roles/qemu_guest_agent/README.md b/ansible/roles/qemu_guest_agent/README.md new file mode 100644 index 0000000..0bc9e78 --- /dev/null +++ b/ansible/roles/qemu_guest_agent/README.md @@ -0,0 +1,3 @@ +# qemu_guest_agent + +An Ansible role that installs `qemu-guest-agent` on a virtual machine. diff --git a/ansible/roles/qemu_guest_agent/meta/main.yml b/ansible/roles/qemu_guest_agent/meta/main.yml new file mode 100644 index 0000000..16f0c49 --- /dev/null +++ b/ansible/roles/qemu_guest_agent/meta/main.yml @@ -0,0 +1,16 @@ +galaxy_info: + role_name: qemu_guest_agent + author: Eugene Zamriy + description: Installs qemu-guest-agent + license: MIT + min_ansible_version: '2.5' + platforms: + - name: EL + versions: + - '8' + - '9' + galaxy_tags: + - qemu + - qemu-guest-agent + +dependencies: [] diff --git a/ansible/roles/qemu_guest_agent/tasks/main.yml b/ansible/roles/qemu_guest_agent/tasks/main.yml new file mode 100644 index 0000000..66eecbd --- /dev/null +++ b/ansible/roles/qemu_guest_agent/tasks/main.yml @@ -0,0 +1,5 @@ +--- +- name: Install qemu-guest-agent + ansible.builtin.dnf: + name: qemu-guest-agent + state: present diff --git a/ansible/roles/setup_cloud_init/README.md b/ansible/roles/setup_cloud_init/README.md new file mode 100644 index 0000000..8e473e0 --- /dev/null +++ b/ansible/roles/setup_cloud_init/README.md @@ -0,0 +1,10 @@ +# setup_cloud_init + +An Ansible role that installs and configures [cloud-init](https://cloud-init.io/). + + +## Role Variables + +The role variables and their default values are listed below: + +* `cloud_init_user: ''` - a name of a user managed by cloud-init. diff --git a/ansible/roles/setup_cloud_init/defaults/main.yml b/ansible/roles/setup_cloud_init/defaults/main.yml new file mode 100644 index 0000000..8f7aee6 --- /dev/null +++ b/ansible/roles/setup_cloud_init/defaults/main.yml @@ -0,0 +1,2 @@ +--- +cloud_init_user: '' diff --git a/ansible/roles/setup_cloud_init/tasks/main.yml b/ansible/roles/setup_cloud_init/tasks/main.yml new file mode 100644 index 0000000..2e6a4c5 --- /dev/null +++ b/ansible/roles/setup_cloud_init/tasks/main.yml @@ -0,0 +1,24 @@ +- name: Install cloud-init + ansible.builtin.dnf: + name: + - cloud-init + - cloud-utils-growpart + - dracut-config-generic + state: present + +- name: Enable cloud-init services + ansible.builtin.service: + name: "{{ item }}" + enabled: true + with_items: + - cloud-config + - cloud-init + - cloud-init-local + - cloud-final + +- name: Configure cloud-init user name + ansible.builtin.replace: + dest: /etc/cloud/cloud.cfg + regexp: '^(\s+name:).*$' + replace: "\\1 {{ cloud_init_user }}" + when: cloud_init_user | length > 0 diff --git a/http/msvsphere-9-gencloud.x86_64.ks b/http/msvsphere-9-gencloud.x86_64.ks new file mode 100644 index 0000000..76efcf3 --- /dev/null +++ b/http/msvsphere-9-gencloud.x86_64.ks @@ -0,0 +1,73 @@ +# MSVSphere 9 Generic Cloud image kickstart file + +# TODO: change url to the kickstart one when we have it +url --url https://rsync.inferitos.ru/msvsphere/9.2/BaseOS/x86_64/os/ +repo --name=BaseOS --baseurl=https://rsync.inferitos.ru/msvsphere/9.2/BaseOS/x86_64/os/ +repo --name=AppStream --baseurl=https://rsync.inferitos.ru/msvsphere/9.2/AppStream/x86_64/os/ + +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 --timeout=1 --location=mbr --append="console=tty0 console=ttyS0,115200n8 no_timer_check crashkernel=auto net.ifnames=0" + +%pre --erroronfail + +parted -s -a optimal /dev/sda -- mklabel gpt +parted -s -a optimal /dev/sda -- mkpart biosboot 1MiB 2MiB set 1 bios_grub on +parted -s -a optimal /dev/sda -- mkpart '"EFI System Partition"' fat32 2MiB 202MiB set 2 esp on +parted -s -a optimal /dev/sda -- mkpart boot xfs 202MiB 714MiB +parted -s -a optimal /dev/sda -- mkpart root xfs 714MiB 100% + +%end + +part biosboot --fstype=biosboot --onpart=sda1 +part /boot/efi --fstype=efi --onpart=sda2 +part /boot --fstype=xfs --onpart=sda3 +part / --fstype=xfs --onpart=sda4 + +rootpw --plaintext msvsphere + +reboot --eject + + +%packages --inst-langs=en +@core +dracut-config-generic +grub2-pc +usermode +-biosdevname +-dnf-plugin-spacewalk +-dracut-config-rescue +-iprutils +-iwl*-firmware +-langpacks-* +-mdadm +-open-vm-tools +-plymouth +-rhn* +%end + + +# disable kdump service +%addon com_redhat_kdump --disable +%end + +%post --erroronfail + +grub2-install --target=i386-pc /dev/sda + +# permit root login via SSH with password authetication +echo "PermitRootLogin yes" > /etc/ssh/sshd_config.d/01-permitrootlogin.conf + +%end diff --git a/msvsphere-9-gencloud.pkr.hcl b/msvsphere-9-gencloud.pkr.hcl new file mode 100644 index 0000000..e997d71 --- /dev/null +++ b/msvsphere-9-gencloud.pkr.hcl @@ -0,0 +1,56 @@ +/** + * Packer template for building MSVSphere 9 Generic Cloud images. + */ + +source "qemu" "msvsphere-9-gencloud-x86_64" { + iso_url = var.iso_url_9_x86_64 + iso_checksum = var.iso_checksum_9_x86_64 + boot_command = var.gencloud_boot_cmd_9_x86_64_uefi + boot_wait = var.boot_wait + cpus = var.cpus + memory = var.memory + disk_size = var.gencloud_disk_size + headless = var.headless + http_directory = var.http_directory + shutdown_command = var.root_shutdown_command + ssh_username = var.gencloud_ssh_username + ssh_password = var.gencloud_ssh_password + ssh_timeout = var.ssh_timeout + vnc_bind_address = var.vnc_bind_address + accelerator = "kvm" + efi_firmware_code = var.uefi_ovmf_code + efi_firmware_vars = var.uefi_ovmf_vars + disk_interface = "virtio-scsi" + disk_cache = "unsafe" + disk_discard = "unmap" + disk_detect_zeroes = "unmap" + disk_compression = true + format = "qcow2" + machine_type = "q35" + net_device = "virtio-net" + qemu_binary = var.qemu_binary + vm_name = "MSVSphere-${var.os_version_9}-${formatdate("YYYYMMDD", timestamp())}.gencloud.x86_64.qcow2" + qemuargs = [ + ["-cpu", "host"] + ] +} + + +build { + sources = [ + "qemu.msvsphere-9-gencloud-x86_64" + ] + + provisioner "ansible" { + playbook_file = "ansible/gencloud.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 = ["--scp-extra-args", "'-O'"] + } +} diff --git a/variables.pkr.hcl b/variables.pkr.hcl index 103b55c..f171332 100644 --- a/variables.pkr.hcl +++ b/variables.pkr.hcl @@ -45,6 +45,12 @@ variable "boot_wait" { default = "10s" } +variable "root_shutdown_command" { + description = "The VM shutdown command" + type = string + default = "/sbin/shutdown -hP now" +} + variable "ssh_timeout" { description = "The SSH connection timeout" type = string @@ -69,6 +75,24 @@ variable "http_directory" { default = "http" } +variable "uefi_ovmf_code" { + description = "QEMU/KVM UEFI firmware path" + type = string + default = "/usr/share/OVMF/OVMF_CODE.fd" +} + +variable "uefi_ovmf_vars" { + description = "QEMU/KVM UEFI firmware variables path" + type = string + default = "/usr/share/OVMF/OVMF_VARS.fd" +} + +variable "qemu_binary" { + description = "QEMU binary path" + type = string + default = null +} + /** * Vagrant-specific settings. */ @@ -105,3 +129,47 @@ variable "vagrant_shutdown_command" { type = string default = "echo vagrant | sudo -S /sbin/shutdown -hP now" } + +variable "vnc_bind_address" { + description = "The Packer VNC server bind address" + type = string + default = "127.0.0.1" +} + +/** + * Generic Cloud-specific settings. + */ +variable "gencloud_disk_size" { + description = "The VM disk size" + type = string + default = "10G" +} + +variable "gencloud_boot_cmd_9_x86_64_uefi" { + description = "The boot command for x86_64 VMs in UEFI mode" + type = list(string) + default = [ + "c", + "linuxefi", + " /images/pxeboot/vmlinuz", + " inst.stage2=hd:LABEL=MSVSphere-9-2-Minimal-x86_64 ro", + " inst.text biosdevname=0 net.ifnames=0", + " inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/msvsphere-9-gencloud.x86_64.ks", + "", + "initrdefi /images/pxeboot/initrd.img", + "", + "boot" + ] +} + +variable "gencloud_ssh_username" { + description = "A login to use for SSH authentication" + type = string + default = "root" +} + +variable "gencloud_ssh_password" { + description = "A password to use for SSH authentication" + type = string + default = "msvsphere" +}