The soul-crushing boredom configuring Linux manually.
Deploy your kickstart server before your coffee gets cold.
I was once at an event and talked about automating Virtual I/O Servers with Ansible. At the end, one of the participants asked the question:
I have only one managed system with two VIOSes. Do you mean, it would be good to automate so few systems with Ansible?
My answer is YES! If you do something very-very seldom, there is a huge chance that you forget what and how you did it.
Perhaps installing a kickstart server for Linux installations is not the most common task, and you only do it once in your lifetime. However, to document the installation and remember how it was done, it is better to do it in an automated manner.
Of course, using Ansible!
Prerequisites
We need a fresh installed Linux server with two disks. One disk is used for the system, and one disk is used for our repository.
We need ISO images of the operating systems, we want to deploy using our Kickstart server, and we need the package grub2-ppc64le-modules-2.06-104.el9_6.noarch.rpm.
In my version, all these files are stored on a web server, and I download them from there. You can store the files locally or on your Ansible node. Don’t forget to change the corresponding parts of the playbook!
Let’s start with LVM!
We have a disk (let’s define it as repo_disk
) and we create a new volume group on it.
- name: Create volume group
community.general.lvg:
vg: repovg
pvs:
- "{{ repo_disk }}"
state: present
Of course, we need some logical volumes in it. I create two of them - one small for the TFTP data and one big for all other data. The task is done by community.general.lvol module. I will do it too. BUT!
BUT! Read carefully the documentation for the module:
Please note that when using `+', `-', or percentage of FREE, the module is not idempotent.
This is exactly what I did in my previous newsletter. I used 100%FREE to create the largest possible logical volume.
What if I still want to use it? Then we must create a check to see if the logical volume already exists.
But first, we create the smallest logical volume, and because we use the hard-coded size for it, the task is idempotent:
- name: Create logical volume for TFTP
community.general.lvol:
lv: tftplv
vg: repovg
size: 2G
For the second logical volume, we first check if it already exists. If it does NOT exist, we create it:
- name: Check if repolv already exists
ansible.builtin.command:
cmd: lvs repovg/repolv
register: repolv_check
failed_when: false
changed_when: false
- name: Create repolv
community.general.lvol:
lv: repolv
vg: repovg
size: 100%FREE
when: repolv_check.rc != 0
If we have logical volumes, we must have filesystems there.
- name: Create filesystems
community.general.filesystem:
dev: "/dev/repovg/{{ item }}"
fstype: xfs
state: present
loop:
- tftplv
- repolv
If you want to use the filesystems, they must be mounted. The ansible.posix.mount module write the filesystems automatically in /etc/fstab.
- name: Mount filesystems
ansible.posix.mount:
path: "{{ item.path }}"
src: "/dev/repovg/{{ item.lvol }}"
fstype: xfs
state: mounted
loop:
- { path: '/var/lib/tftpboot', lvol: tftplv }
- { path: '/srv/repo', lvol: repolv }
Packages installation
The filesystems are there, and we can install packages. This is only one task:
- name: Install required packages
ansible.builtin.dnf:
name:
- dhcp-server
- grub2-efi-x64-modules
- grub2-pc-modules
- grub2-tools-extra
- httpd
- tftp-server
state: present
Oh, I have forgotten one package! Exactly, what we need for IBM Power installations - grub2-ppc64le-modules-2.06-104.el9_6.noarch.rpm. As you remember the package is on my web server, and I can easily install it using the same module:
- name: Install IBM Power GRUB2 modules
ansible.builtin.dnf:
name:
- "{{ webserver }}/grub2-ppc64le-modules-2.06-104.el9_6.noarch.rpm"
state: present
HTTPd configuration
Everything is installed, and we can configure what we’ve installed. Let’s start with Apache httpd.
In reality, we must change only two lines in /etc/httpd/conf/httpd.conf.
The first line is easy to find and to change:
- name: Configure DocumentRoot on httpd
ansible.builtin.lineinfile:
path: /etc/httpd/conf/httpd.conf
line: 'DocumentRoot "/srv/repo"'
regex: '^DocumentRoot'
notify: Restart httpd
The second line, <Directory “/srv/rep”>, is more challenging. We can’t search for ‘^<Directory’, because there could be multiple entries with it in the httpd configuration file. That’s why we simply add one more Directory-entry into httpd.conf:
- name: Configure /srv/repo directory
ansible.builtin.blockinfile:
path: /etc/httpd/conf/httpd.conf
marker: "### {mark} ANSIBLE MANAGED BLOCK"
insertbefore: '<Directory "/var/www/html">'
block: |
<Directory "/srv/repo">
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
notify: Restart httpd
In case you like me use SELinux, we must add one more task to re-configure SELinux file contexts:
- name: Configure /srv/repo for SELinux
community.general.sefcontext:
target: '/srv/repo(/.*)?'
setype: httpd_sys_content_t
state: present
- name: Apply SELinux context
ansible.builtin.command:
cmd: restorecon -Rv /srv/repo
TFTP configuration
We must create a directory with all GRUB modules. Unfortunately, there is no Ansible module for the task (or I don’t know it), and we must use the ansible.builtin.command module. But we can check if the command was run already by checking one of the files it creates, like:
- name: Create GRUB2 network directory
ansible.builtin.command:
cmd: grub2-mknetdir --net-directory /var/lib/tftpboot
creates: /var/lib/tftpboot/boot/grub2/powerpc-ieee1275/core.elf
Don’t forget to enable and start the TFTP service:
- name: Enable TFTP
ansible.builtin.systemd_service:
name: tftp.socket
enabled: true
state: started
DHCP configuration
Take the dhcpd.conf configuration file from the previous newsletter and copy it to the target server:
- name: Copy dhcpd.conf
ansible.builtin.copy:
src: dhcpd.conf
dest: /etc/dhcp/dhcpd.conf
owner: root
group: root
mode: '0644'
setype: dhcp_etc_t
notify: Restart dhcpd
FirewallD configuration
Fortunately enough, it is a single task.
- name: Configure FirewallD
ansible.posix.firewalld:
service: "{{ item }}"
permanent: true
state: enabled
loop:
- http
- tftp
Where is our data?
We configured everything we need. The last step is to download ISO images and make them usable for our installations.
First, we need a directory for the ISO images:
- name: Directory for ISO images
ansible.builtin.file:
path: /srv/repo/iso
state: directory
owner: root
group: root
mode: '0755'
setype: httpd_sys_content_t
Now, download the images:
- name: Download ISO images
ansible.builtin.get_url:
url: "https://dl.power-devops.com/iso/{{ item }}"
dest: "/srv/repo/iso/{{ item }}"
owner: root
group: root
mode: '0644'
setype: httpd_sys_content_t
tmp_dest: /srv/repo/iso
loop:
- rhel-9.6-ppc64le-dvd.iso
- SLE-15-SP7-Full-ppc64le-GM-Media1.iso
You can imagine that this is the longest-running task in the entire playbook!
We mount the images as filesystems, but we must create directories first:
- name: Create directories
ansible.builtin.file:
path: "/srv/repo/{{ item.type }}/{{ item.version }}"
state: directory
owner: root
group: root
mode: '0755'
setype: httpd_sys_content_t
loop:
- { type: 'RHEL', version: '9.6-ppc64le' }
- { type: 'SLES', version: '15.7-ppc64le' }
Now we can mount the ISO images:
- name: Mount ISO images
ansible.posix.mount:
path: "/srv/repo/{{ item.path }}"
src: "/srv/repo/iso/{{ item.image }}"
fstype: iso9660
opts: ro,loop
state: mounted
loop:
- { path: 'RHEL/9.6-ppc64le', image: 'rhel-9.6-ppc64le-dvd.iso' }
- { path: 'SLES/15.7-ppc64le', image: 'SLE-15-SP7-Full-ppc64le-GM-Media1.iso' }
The last step is to create TFTP boot directories with kernels and initrd, and copy the corresponding files there:
- name: Create TFTP boot directories
ansible.builtin.file:
path: "/var/lib/tftpboot/{{ item.type }}/{{ item.version }}"
state: directory
owner: root
group: root
mode: '0755'
loop:
- { type: 'RHEL', version: '9.6-ppc64le' }
- { type: 'SLES', version: '15.7-ppc64le' }
- name: Copy kernels and initrd
ansible.builtin.copy:
src: "/srv/repo/{{ item.type }}/{{ item.version }}/{{ item.directory }}/{{ item.file }}"
dest: "/var/lib/tftpboot/{{ item.type }}/{{ item.version }}/{{ item.file }}"
remote_src: true
owner: root
group: root
mode: '0644'
loop:
- { type: 'RHEL', version: '9.6-ppc64le', directory: 'ppc/ppc64', file: 'vmlinuz' }
- { type: 'RHEL', version: '9.6-ppc64le', directory: 'ppc/ppc64', file: 'initrd.img' }
- { type: 'SLES', version: '15.7-ppc64le', directory: 'boot/ppc64le', file: 'linux' }
- { type: 'SLES', version: '15.7-ppc64le', directory: 'boot/ppc64le', file: 'initrd' }
Support the Power DevOps Newsletter!
If you like reading technical articles about IBM Power, AIX, and Linux on IBM Power, consider upgrading to the paid tier to show your support. As a paid subscriber, you not only get regular posts, but you will get additional posts with the full code and further explanations, access to the whole archive of the blog, and take part in our monthly calls where you can ask your questions and propose topics for future newsletters. Be an active member of our community!
You can start installing your Linux on Power systems!
If not taking downloads into account, the whole process of preparing a kickstart server for Linux on Power installations takes ca. 10 minutes or less. There is no excuse if you still install your Linux LPARs manually!
I didn’t include instructions in this newsletter on how to automate adding new Linux LPARs to the configuration. You have the manual instructions from the previous newsletter, which you can use. Or wait till next Friday, and I will write about it.
By the way, do you want to read how to install AIX from your x86 laptop? If so, please write it in the comments, and I will test it and create a playbook for it.
Have fun preparing your Linux journey!
Andrey
Hi, I am Andrey Klyachkin, IBM Champion and IBM AIX Community Advocate. This means I don’t work for IBM. Over the last twenty years, I have worked with many different IBM Power customers all over the world, both on-premise and in the cloud. I specialize in automating IBM Power infrastructures, making them even more robust and agile. I co-authored several IBM Redbooks and IBM Power certifications. I am an active Red Hat Certified Engineer and Instructor.
Follow me on LinkedIn, Twitter and YouTube.
You can meet me at events like IBM TechXchange, the Common Europe Congress, and GSE Germany’s IBM Power Working Group sessions.