ERB templates are for embedding Ruby code within an AutoYaST profile to modify the profile during the installation. With this approach, you can inspect the system and adjust the profile by setting values, adding or skipping sections, and so on.
To activate the ERB processing, the profile must have the extension
.erb
(for example,
autoyast.xml.erb
). Hence, it is not possible to
combine rules/classes and ERB templates.
ERB stands for Embedded Ruby. ERB uses the power of the Ruby programming language to generate different kinds of content. With ERB, you can include some Ruby code in your profiles to adapt them at runtime, depending on the installation system.
When using ERB, the Ruby code is enclosed between <%
and %>
signs. Use an equals sign,
=
, to include command output in the resulting profile.
<bootloader> <% require "open-uri" %> <%= URI.open("http://192.168.1.1/profiles/bootloader-common.xml").read %> </bootloader> <!-- this line gets replaced with the content of bootloader-common.xml -->
You can use Ruby facilities to run arbitrary commands. If you want
to get the output of a command, then enclose it between backticks.
If you want to know whether a command was successful or not, run the
command with the system
function.
<% files = `ls` %> <!-- files contains the output of the command (for instance "file1\nfile2\nfile3") --> <% success = system("dmidecode | grep some-model") %> <!-- success contains true or false -->
Also, you can use more advanced Ruby code structures such as conditions and loops.
<% ip_forward = File.read("/proc/sys/net/ipv4/ip_forward").strip %> <% if ip_forward == "1" %> <!-- something --> <% end %> <% files = `ls /tmp/config/*.xml` %> <% files.split.each do |file| %> <%= file.read %> <% end %>
AutoYaST offers a small set of helper functions to retrieve
information from the underlying system, like disks
or
network_cards
. You can check the list of helpers and
their values in the Section 7.2, “Template helpers” section.
Template helpers are sets of Ruby methods that can be used in the profiles to retrieve information about the installation system.
boot_efi?
#Edit source
boot_efi?
is a boolean helper that returns
whether the system is booted using EFI. In the example below, the
profile configures the bootloader according to the current boot mode.
<% if env.boot_efi? %> <loader_type>grub2-efi</loader_type> <% else %> <loader_type>grub2</loader_type> <% end %>
disks
#Edit source
The disks
helper returns a list of the detected disks.
Each element of the list contains some basic information like the device
name or the size.
Key |
Type |
Value |
---|---|---|
|
String |
Device kernel name (for example, |
|
String |
Disk model |
|
String |
Serial number |
|
Integer |
Disk size (is a count of disk sectors) |
|
Array<String> |
List of disk udev names. You can use any of them to refer to the device. |
|
String |
Disk vendor's name |
The profile in the example below installs the system on the largest disk.
It sorts the list of existing disks by size and takes the last one. Then it
uses the :device
key as value for the
device
element.
<partitioning t="list"> <drive> <% disk = disks.sort_by { |d| d[:size] }.last %> <!-- find the largest disk --> <device><%= disk[:device] %></device> <!-- print the disk device name --> <initialize t="boolean">true</initialize> <use>all</use> </drive> </partitioning>
network_cards
#Edit source
The network_cards
helper returns a list of network
cards, including their names, status information (for example, if they are
connected or not).
Key |
Type |
Value |
---|---|---|
|
String |
Device name (for example, |
|
String |
MAC address |
|
Boolean |
Whether the device is active or not |
|
Boolean |
Whether the device is connected or not |
|
String |
Disk vendor's name |
The following example finds the first network card that is connected to the network and configures it to use DHCP.
<interfaces t="list"> <% with_link = netword_cards.sort_by { |n| n[:name] }.find { |n| n[:link] } %> <% if with_link %> <interface> <device><%= with_link[:device] %></device> <startmode>auto</startmode> <bootproto>dhcp</bootproto> </interface> <% end > </interfaces>
os_release
#Edit source
The os_release
helper returns the operating system
information, which is included in the /etc/os-release
file.
Key |
Type |
Value |
---|---|---|
|
String |
Distribution ID (for example,
|
|
String |
Distribution name (for example,
|
|
String |
Distribution version (for example,
|
You might use this information to decide which product to install, using pretty much the same profile for all of them (SLE or openSUSE distributions).
<products t="list"> <% if os_release[:id] == 'sle' %> <product>SLES</product> <% else %> <product>openSUSE</product> <% end %> </products>
hardware
#Edit source
The hardware
helper provides additional hardware information.
It returns all the information from the hwinfo
command. You can
use this helper as a fallback for those cases in which the information available
through the described helpers is not enough. In the next example, the
hardware
helper is used to filter USB devices. Check
Section 7.3, “Running ERB helpers” to learn how to inspect all the information
provided by the hardware
helper.
<% usb_disks = hardware["disk"].select { |d| d["driver"] != "usb-storage" } %>
You can use the Ruby console to run AutoYaST ERB
helpers and find out what they offer. All ERB helpers are
accessed through an instance of the
Y2Autoinstallation::Y2ERB::TemplateEnvironment
class. Start the
Ruby interactive interpreter by running, as root:
irb -ryast -rautoinstall/y2erb
.
irb > env = Y2Autoinstallation::Y2ERB::TemplateEnvironment.new # the env variable gives access to the helpers irb > env.disks => [{:vendor=>"WDC", :device=>"sda", ...}, {:vendor=>"TOSHIBA", :device=>"sdb", ...}, ...] irb > env.hardware.keys => ["architecture", "bios", "bios_video", ...] irb > env.hardware["architecture"] => "x86_64"
The AutoYaST command line provides a check-profile
command that can be used
to generate a profile from an ERB file. This command asks AutoYaST to parse, run the ERB code, and
generate the resulting profile. You can inspect the rendered profile to check that everything
worked as expected. See the command help for all the options it supports:
autoyast check-profile --help
.
In the following example, check-profile
asks AutoYaST to download and parse
the profile, interpret the ERB code and run the pre-scripts. The result
will be dumped to the result.xml
file.
>
sudo
yast2 autoyast check-profile filename=http://192.168.1.100/autoinst.erb output=result.xml run-scripts=true run-erb=true
check-profile
permissions
In most cases, check-profile
requires root permissions,
so be careful when running pre-installation scripts and ERB profiles as root.
Use only profiles that you trust.
For those cases in which you want to stop the ERB evaluation
and check what is happening, YaST offers integration with the
byebug
debugger. Install the rubygem(byebug)
package and set
the Y2DEBUGGER
environment variable to 1.
>
sudo
zypper --non-interactive in "rubygem(byebug)">
sudo
Y2DEBUGGER=1 yast2 autoyast check-profile ...
Adding breakpoints is as easy as adding <% byebug %> where you want to stop.
For more information about byebug
, see
https://github.com/deivid-rodriguez/byebug.
<% byebug %> <% if system("dmidecode | grep some-model") %> <!-- do something --> %<% end %>
Although both ERB and rules/classes enable generating profiles
dynamically, in general ERB profiles are easier to read and
understand. One important difference is that rules and classes can
merge profiles,
and ERB cannot. See more about merging profiles in
Chapter 6, Rules and classes. On the other hand, ERB brings all
the power of a high-level language, Ruby. Let's see an example
using both. In the following example, we want to place /home
directory in /dev/sdb
if it exists.
<rule> <custom1> <script> if blkid | grep /dev/sdb > /dev/null; then echo -n "yes" else echo -n "no" fi; </script> <match>yes</match> <match_type>exact</match_type> </custom1> <result> <profile>classes/sdb_home.xml</profile> <dont_merge config:type="list"> <element>partition</element> </dont_merge> </result> </rule>
<% home_in_sdb = disks.map { |d| d[:device] }.include?("sdb") %> <partitioning config:type="list"> <drive> ... </drive> <% if home_in_sdb %> <drive> <device>/dev/sdb</device> <disklabel>none</disklabel> <partitions t="list"> <partition> <format t="boolean">true</format> <filesystem t="symbol">xfs</filesystem> <mount>/home</mount> </partition> </partitions> </drive> <% end %> </partitioning>