Jump to contentJump to page navigation: previous page [access key p]/next page [access key n]
documentation.suse.com / SUSE Linux Enterprise Server Documentation / AutoYaST Guide / Managing mass installations with dynamic profiles / ERB templates
Applies to SUSE Linux Enterprise Server 15 SP3

7 ERB templates

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.

7.1 What is ERB?

ERB stands for Embedded Ruby. ERB uses the power of the Ruby programming language to generate different kind 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.

Example 7.1: Including a file using ERB
<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.

Example 7.2: Running commands with Ruby
<% 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.

Example 7.3: Using Ruby structures
<% 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.

7.2 Template helpers

Template helpers are sets of Ruby methods that can be used in the profiles to retrieve information about the installation system.

7.2.1 boot_efi?

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.

Example 7.4: Configuring the boot loader
<% if env.boot_efi? %>
  <loader_type>grub2-efi</loader_type>
<% else %>
  <loader_type>grub2</loader_type>
<% end %>

7.2.2 disks

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

:device

String

Device kernel name (for example, sda).

:model

String

Disk model

:serial

String

Serial number

:size

Integer

Disk size (is a count of disk sectors)

:udev_names

Array<String>

List of disk udev names. You can use any of them to refer to the device.

:vendor

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.

Example 7.5: Using the largest disk
<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>

7.2.3 network_cards

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

:device

String

Device name (for example, eth0 or enp3s0)

:mac

String

MAC address

:active

Boolean

Whether the device is active or not

:link

Boolean

Whether the device is connected or not

:vendor

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.

Example 7.6: Configure the connected network cards
<interfaces t="list">
  <% with_link = network_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>

7.2.4 os_release

The os_release helper returns the operating system information, which is included in the /etc/os-release file.

Key

Type

Value

:id

String

Distribution ID (for example, sles, opensuse-tumbleweed)

:name

String

Distribution name (for example, SLES or openSUSE Tumbleweed)

:version

String

Distribution version (for example, 15.2)

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).

Example 7.7: Reusing the same profile for different distributions
<products t="list">
  <% if os_release[:id] == 'sle' %>
  <product>SLES</product>
  <% else %>
  <product>openSUSE</product>
  <% end %<
</products>

7.2.5 hardware

The hardware helper provides additional hardware information. It returns all the information from hwinfo command. You can use this helper as an escape hatch for those cases in which the information available through the described helpers is not enough. In the next example, 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.

Example 7.8: Filtering USB devices
<% usb_disks = hardware["disk"].select { |d| d["driver"] != "usb-storage" } %>

7.3 Running ERB helpers

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.

Example 7.9: Running helpers
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"

7.4 Rendering ERB profiles

The AutoYaST command line provides a check-profile command that can be used to generate a profile from a 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 if 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.

Example 7.10: Rendering profile
 > sudo yast2 autoyast check-profile filename=http://192.168.1.100/autoinst.erb output=result.xml run-scripts=true run-erb=true
Warning
Warning: 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.

7.5 Debugging ERB profiles

For those cases in which you would like 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.

Example 7.11: Preparing the debug environment
> 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.

Example 7.12: Adding a breakpoint
<% byebug %>
<% if system("dmidecode | grep some-model") %>
  <!-- do something -->
%<% end %>

7.6 ERB vs. rules and classes

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 can not. See more about merging profiles at 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.

Example 7.13: Rules and classes
<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>
Example 7.14: ERB
<% 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>