Cobbler (v3.3.7) OpenSUSE Deployment Guide
This guide assumes that you have a Fedora 34 server or workstation system configured to run Cobbler v3.3.7 similarly to the Cobbler v3.3.6 Beginner’s guide.
Table of Contents
- Objective
- OpenSUSE Leap 15 PXE Deployment
- Taking it Further (Leap Micro Deployment)
- Tips & Troubleshooting
Objective
Starting where the Beginner’s guide left off, further configure the Cobbler v3.3.7 server to deploy OpenSUSE Leap 15 server via PXE network boot, using the same system and network environment. This guide assumes that you still have selinux
and firewalld
configured and enabled as described in the Beginner’s guide.
OpenSUSE Leap 15 PXE Deployment
This guide has been tested with openSUSE Leap 15.4, 15.5, & 15.6, which can be obtained from the sites below:
This guide will detail Leap 15.6 specifically; start by downloading the installation media on the Cobbler server:
cd ~/Downloads
wget https://provo-mirror.opensuse.org/distribution/leap/15.6/iso/openSUSE-Leap-15.6-DVD-x86_64-Media.iso
Mount the installation media and run the cobbler import
:
[ -d /mnt/SUSE ] || mkdir /mnt/SUSE
mount -t iso9660 -o loop,ro ~/Downloads/openSUSE-Leap-15.6-DVD-x86_64-Media.iso /mnt/SUSE
cobbler import --name=SUSE-15.6 --arch=x86_64 --path=/mnt/SUSE
Update the kernel-options
for the newly imported Cobbler Distro:
cobbler distro edit --name SUSE-15.6-x86_64 --kernel-options 'install=$tree'
Create the necessary autoinstall template files, based on the originals included with cobbler, to generate a compatible autoyast.xml
autoinstall file
- TIP: execute the below text blocks on the Cobbler server CLI one-by-one exactly as shown below to edit the files appropriately; the final files (including the optional chagnes made in the next step) will be included at the very bottom of this guide for reference
cat /var/lib/cobbler/templates/sample_autoyast.xml | \
sed 's,english,us,' | \
grep -v runlevel | grep -v "<default>3</default>" | \
sed 's,networking.xml,suse-15.6-networking.xml,' | \
sed 's,suse_scriptwrapper.xml,suse-15.6_scriptwrapper.xml,' | \
tee /var/lib/cobbler/templates/suse-15.6-autoyast.xml
cat /var/lib/cobbler/snippets/networking.xml | \
grep -v dhcp_resolv | \
sed 's,^.*<keep_install_network.*/keep_install_network>,## Figure out if we'\''re automating OS installation for a system or a profile\n#if $getVar('\''system_name'\''\,'\'\'') != '\'\''\n <keep_install_network config:type="boolean">false</keep_install_network>\n#else\n <keep_install_network config:type="boolean">true</keep_install_network>\n#end if,' | \
sed 's,<managed.*/managed>,<backend>network_manager</backend>,' | \
sed 's,<nameservers config:type="list">,#if $name_servers and $name_servers[0] != "":\n <nameservers config:type="list">,' | \
sed 's,</nameservers>,</nameservers>\n #end if,' | \
sed '/ <net-udev config:type="list">/,/ #if $getVar("system_name","") != ""/c\ \ \ \ #if $getVar("system_name","") != ""\n\ \ \ \ <net-udev config:type="list">' | \
sed -z 's,#end if\n </net-udev>,</net-udev>\n #end if,' | \
tee /var/lib/cobbler/snippets/suse-15.6-networking.xml
cat /var/lib/cobbler/snippets/suse_scriptwrapper.xml | \
grep -v network_needed | \
grep -v interpreter | \
tee /var/lib/cobbler/snippets/suse-15.6_scriptwrapper.xml
cp /var/lib/cobbler/snippets/autoinstall_start /var/lib/cobbler/snippets/autoinstall_start.bkp
cp /var/lib/cobbler/snippets/autoinstall_done /var/lib/cobbler/snippets/autoinstall_done.bkp
sed -i -z 's,#if $run_install_triggers\n #if $breed == .redhat.,#if $run_install_triggers\n #if $breed == '\''redhat'\'' or $breed == '\''suse'\'',' /var/lib/cobbler/snippets/autoinstall_start /var/lib/cobbler/snippets/autoinstall_done
Optionally, further configure the autoyast template to install the sudo
, openssh
, & chrony
packages, setup system NTP client, set the timezone precisely, enable the sshd
systemd service, and configure a new linux user suse with the same password as the root user.
sed 's,^ </language>, </language>\n <timezone>\n <hwclock>UTC</hwclock>\n <timezone>EST5EDT</timezone>\n </timezone>\n <software>\n <packages config:type="list">\n <package>chrony</package>\n <package>sudo</package>\n <package>openssh</package>\n </packages>\n </software>\n <services-manager>\n <services>\n <enable config:type="list">\n <service>sshd</service>\n </enable>\n </services>\n </services-manager>,' /var/lib/cobbler/templates/suse-15.6-autoyast.xml
sed 's,$SNIPPET(.hosts.xml.),$SNIPPET('\''hosts.xml'\'')\n <ntp-client>\n <ntp_policy>auto</ntp_policy>\n <ntp_servers config:type="list">\n <ntp_server>\n <address>us.pool.ntp.org</address>\n <iburst config:type="boolean">false</iburst>\n <offline config:type="boolean">false</offline>\n </ntp_server>\n <ntp_server>\n <address>2.opensuse.pool.ntp.org</address>\n <iburst config:type="boolean">false</iburst>\n <offline config:type="boolean">false</offline>\n </ntp_server>\n </ntp_servers>\n <ntp_sync>15</ntp_sync>\n </ntp-client>,' /var/lib/cobbler/templates/suse-15.6-autoyast.xml
sed 's,</user>,</user>\n <user>\n <encrypted config:type="boolean">true</encrypted>\n <username>suse</username>\n <user_password>$default_password_crypted</user_password>\n </user>,' /var/lib/cobbler/templates/suse-15.6-autoyast.xml
Configure the Cobbler Profile to use the new autoinstall (autoyast.xml
) template:
cobbler profile edit --name SUSE-15.6-x86_64 --autoinstall suse-15.6-autoyast.xml
Finally, create a new Cobbler System to automatically boot and install openSUSE Leap 15.6, replacing the “aa:bb:cc:dd:ee:ff” with the MAC address of your PXE client, being sure not to use a duplicate MAC or IP addresse of any other Cobbler System, and then sync up Cobbler:
cobbler system add --name SUSE-15.6 --profile SUSE-15.6-x86_64 --netboot-enabled true --hostname SUSE-15-6 --interface eth0 --static true --mac-address "aa:bb:cc:dd:ee:ff" --ip-address 10.0.0.15 --gateway 10.0.0.1 --netmask 255.255.255.0 --name-servers "10.0.0.1 1.1.1.1 10.0.0.10"
cobbler sync
The PXE Client VM can now be powered on, and should automatically boot to PXE and install openSUSE Leap 15.6 to the VM HDD using the “suse-15.6-autoyast.xml” autoyast template created above.
Taking it Further (Leap Micro Deployment)
With openSUSE Leap 15 deploying successfully as described above, the Cobbler server is also prepped to deploy openSUSE Leap Micro (v5.5 tested)
Download the installation media and use the same autoinstall file and options to configure a new Cobbler System for deploying openSUSE Leap Micro 5.5
cd ~/Downloads && wget https://slc-mirror.opensuse.org/distribution/leap-micro/5.5/product/iso/openSUSE-Leap-Micro-5.5-DVD-x86_64-Media.iso
mount -t iso9660 -o loop,ro ~/Downloads/openSUSE-Leap-Micro-5.5-DVD-x86_64-Media.iso /mnt/SUSE
cobbler import --name=Leap-micro-5.5 --arch=x86_64 --path=/mnt/SUSE
cobbler distro edit --name Leap-micro-5.5-x86_64 --kernel-options 'install=$tree'
cobbler profile edit --name Leap-micro-5.5-x86_64 --autoinstall suse-15.6-autoyast.xml
cobbler system add --name Leap-micro --profile Leap-micro-5.5-x86_64 --netboot-enabled true --enable-ipxe false --hostname Leap-micro --interface eth0 --static true --mac-address "aa:bb:cc:dd:ee:ff" --ip-address 10.0.0.15 --gateway 10.0.0.1 --netmask 255.255.255.0 --name-servers "10.0.0.1 1.1.1.1 10.0.0.10"
cobbler sync
Tips & Troubleshooting
-
The Cobbler 3.3.6 Beginner’s Guide Tips & Troubleshooting section contains some basic recommendations and limitations of Cobbler which will not be repeated here.
-
References
-
https://cobbler.readthedocs.io/en/v3.3.6/cobblerd.html#autoinstallation-autoyast-kickstart
-
https://doc.opensuse.org/documentation/leap/archive/15.4/autoyast/html/book-autoyast/Invoking.html
-
https://doc.opensuse.org/documentation/leap/archive/15.4/autoyast/single-html/book-autoyast/
-
https://documentation.suse.com/sles/15-SP5/html/SLES-all/book-autoyast.html
-
https://documentation.suse.com/en-us/sles/15-SP4/html/SLES-all/cha-autoyast-create-control-file.html
-
https://doc.opensuse.org/documentation/leap/archive/15.4/autoyast/single-html/book-autoyast/
-
https://opensuse-autoinstall.opensuse.narkive.com/8tBZNWIG/debugging-autoyast
-
https://opensuse-autoinstall.opensuse.narkive.com/441KAfMq/how-to-configure-services-using-autoyast
-
-
Edited files
-
/var/lib/cobbler/templates/suse-15.6-autoyast.xml
<?xml version="1.0"?> <!DOCTYPE profile> <profile xmlns="http://www.suse.com/1.0/yast2ns" xmlns:config="http://www.suse.com/1.0/configns"> <deploy_image> <image_installation config:type="boolean">false</image_installation> </deploy_image> ## without the next 6 lines autoyast will ask for confirmation bevore installation <general> <mode> <confirm config:type="boolean">false</confirm> <final_reboot config:type="boolean">true</final_reboot> </mode> </general> $SNIPPET('addons.xml') $SNIPPET('hosts.xml') <ntp-client> <ntp_policy>auto</ntp_policy> <ntp_servers config:type="list"> <ntp_server> <address>us.pool.ntp.org</address> <iburst config:type="boolean">false</iburst> <offline config:type="boolean">false</offline> </ntp_server> <ntp_server> <address>2.opensuse.pool.ntp.org</address> <iburst config:type="boolean">false</iburst> <offline config:type="boolean">false</offline> </ntp_server> </ntp_servers> <ntp_sync>15</ntp_sync> </ntp-client> $SNIPPET('kdump.xml') <keyboard> <keymap>us</keymap> </keyboard> <language> <language>en_US</language> <languages></languages> </language> <timezone> <hwclock>UTC</hwclock> <timezone>EST5EDT</timezone> </timezone> <software> <packages config:type="list"> <package>chrony</package> <package>sudo</package> <package>openssh</package> </packages> </software> <services-manager> <services> <enable config:type="list"> <service>sshd</service> </enable> </services> </services-manager> <login_settings/> $SNIPPET('suse-15.6-networking.xml') <users config:type="list"> <user> <encrypted config:type="boolean">true</encrypted> <fullname>root</fullname> <gid>0</gid> <home>/root</home> <password_settings> <expire></expire> <flag></flag> <inact></inact> <max></max> <min></min> <warn></warn> </password_settings> <shell>/bin/bash</shell> <uid>0</uid> <user_password>$default_password_crypted</user_password> <username>root</username> </user> <user> <encrypted config:type="boolean">true</encrypted> <username>suse</username> <user_password>$default_password_crypted</user_password> </user> </users> <scripts> ## we have to include the pre-scripts tag to get autoinstall_start included <pre-scripts config:type="list"> #set global $wrappedscript = 'autoinstall_start' $SNIPPET('suse-15.6_scriptwrapper.xml') ## SuSE has an annoying habit on ppc64 of changing the system ## boot order after installation. This makes it non-trivial to ## automatically re-install future OS. #set global $wrappedscript = 'save_boot_device' $SNIPPET('suse-15.6_scriptwrapper.xml') </pre-scripts> <post-scripts config:type="list"> ## ## This plugin wrapper provides the flexibility to call pure shell ## snippets which can be used directly on autoinst file and with ## wrapper on SuSE. ## ## To use it ## - exchange name_of_pure_shell_snippet with the name of this shell snippet ## - and remove the '##' in front of the line with suse-15.6_scriptwrapper.xml ## #set global $wrappedscript = 'name_of_pure_shell_snippet' ## $SNIPPET('suse-15.6_scriptwrapper.xml') ## SuSE has an annoying habit on ppc64 of changing the system ## boot order after installation. This makes it non-trivial to ## automatically re-install future OS. #set global $wrappedscript = 'restore_boot_device' $SNIPPET('suse-15.6_scriptwrapper.xml') </post-scripts> ## we have to include the init-scripts tag to get autoinstall_done included <init-scripts config:type="list"> #set global $wrappedscript = 'autoinstall_done' $SNIPPET('suse-15.6_scriptwrapper.xml') </init-scripts> </scripts> </profile>
-
/var/lib/cobbler/snippets/suse-15.6-networking.xml
#set $osversion = $getVar("os_version","") #set $hostname = $getVar("hostname","") #if $hostname == "" #set $hostname = $getVar("system_name","cobbler") #end if #if $getVar("dns_name_eth0","") != "" #set $my_hostname = $hostname.split('.',1)[:1][0] #set $my_domainname = $dns_name_eth0.split('.',1)[1:][0] #else #set $my_hostname = $hostname #set $my_domainname = "site" #end if <networking> ## Figure out if we're automating OS installation for a system or a profile #if $getVar('system_name','') != '' <keep_install_network config:type="boolean">false</keep_install_network> #else <keep_install_network config:type="boolean">true</keep_install_network> #end if <dhcp_options> <dhclient_client_id></dhclient_client_id> <dhclient_hostname_option></dhclient_hostname_option> </dhcp_options> <dns> <dhcp_hostname config:type="boolean">false</dhcp_hostname> <write_hostname config:type="boolean">false</write_hostname> <resolv_conf_policy></resolv_conf_policy> <hostname>$my_hostname</hostname> <domain>$my_domainname</domain> #if $getVar("name_servers_search",[]) != [] <searchlist config:type="list"> #for $sd in $name_servers_search <search>$sd</search> #end for </searchlist> #end if #if $name_servers and $name_servers[0] != "": <nameservers config:type="list"> #for $ns in $name_servers <nameserver>$ns</nameserver> #end for </nameservers> #end if </dns> <interfaces config:type="list"> #if $getVar("system_name","") != "" #import re #set $vlanpattern = $re.compile("[a-zA-Z0-9]+[\.][0-9]+") #set $ikeys = $interfaces.keys() #for $iface in $ikeys #set $idata = $interfaces[$iface] #set $mac = $idata.get("mac_address", "").lower() #set $static = $idata.get("static", "") #set $ip = $idata.get("ip_address", "") #set $netmask = $idata.get("netmask", "") #set $static_routes = $idata.get("static_routes", "") #set $iface_type = $idata.get("interface_type", "").lower() #set $iface_master = $idata.get("interface_master", "") #set $bonding_opts = $idata.get("bonding_opts", "").lower() #set $ipv6_address = $idata.get("ipv6_address", "") #set $ipv6_secondaries = $idata.get("ipv6_secondaries", "") #set $ipv6_mtu = $idata.get("ipv6_mtu", "") #set $ipv6_default_gateway = $idata.get("ipv6_default_gateway", "") #set $ipv6_static_routes = $idata.get("ipv6_static_routes", "") ## start of interface section <interface> #if $iface_type in ("bond", "master") <bonding_master>yes</bonding_master> <bonding_module_opts>$bonding_opts</bonding_module_opts> #set $loop_ikeys = $interfaces.keys() #set $loop_counter = 0 #for $loop_iface in $loop_ikeys #set $loop_idata = $interfaces[$loop_iface] #set $loop_interface_type = $loop_idata.get("interface_type", "").lower() #if $loop_interface_type in ("slave","bond_slave") #if $loop_idata["interface_master"] != "" #if $loop_idata["interface_master"].lower() == $iface.lower() <bonding_slave$loop_counter>$loop_iface</bonding_slave$loop_counter> #set $loop_counter += 1 #end if #end if #end if #end for #if $static <bootproto>static</bootproto> #else <bootproto>dhcp</bootproto> #end if <device>$iface</device> <ipaddr>$ip</ipaddr> <netmask>$netmask</netmask> <startmode>auto</startmode> <usercontrol>no</usercontrol> #elif $iface_type in ("slave","bond_slave","bridge_slave") <bootproto>none</bootproto> <device>$iface</device> #if $osversion == "sles12" or re.match('^sles12sp[1234]$', $osversion) <startmode>hotplug</startmode> #else <startmode>off</startmode> #end if <usercontrol>no</usercontrol> #elif $iface_type in ("","na") #if $static <bootproto>static</bootproto> #else <bootproto>dhcp</bootproto> #end if <device>$iface</device> <lladdr>$mac</lladdr> <ipaddr>$ip</ipaddr> <netmask>$netmask</netmask> <startmode>auto</startmode> <usercontrol>no</usercontrol> #end if ## =================================================================== ## VLAN configuration ## =================================================================== #if $vlanpattern.match($iface) #set [$etherdevice, $vlanid] = $iface.split(".") <etherdevice>$etherdevice</etherdevice> <vlan_id>$vlanid</vlan_id> #end if ## =================================================================== ## IPv6 support ## =================================================================== #if $ipv6_address != "" <aliases> <alias0> <IPADDR>$ipv6_address</IPADDR> <LABEL>a0</LABEL> <PREFIXLEN>64</PREFIXLEN> </alias0> #if $ipv6_secondaries != "" #set $s = 1 #for $alias in $ipv6_secondaries <alias${s}> <IPADDR>$alias</IPADDR> <LABEL>a${s}</LABEL> <PREFIXLEN>64</PREFIXLEN> </alias${s}> #set $s += 1 #end for #end if </aliases> #end if </interface> #end for #end if </interfaces> <backend>network_manager</backend> #if $getVar("system_name","") != "" <net-udev config:type="list"> #set $ikeys = $interfaces.keys() #for $iface in $ikeys #set $idata = $interfaces[$iface] #set $mac = $idata["mac_address"] #set $interface_type = $idata["interface_type"] #if $mac.lower() != "" #if $interface_type.lower() not in ["bond","bridge"] <rule> <name>$iface</name> <rule>ATTR{address}</rule> <value>$mac.lower()</value> </rule> #end if #end if #end for </net-udev> #end if <routing> <ip_forward config:type="boolean">false</ip_forward> #if $getVar("system_name","") != "" <routes config:type="list"> <route> <destination>default</destination> <netmask>-</netmask> <device>-</device> <gateway>$gateway</gateway> </route> ## =================================================================== ## IPv4 static route setup ## =================================================================== #for $iface in $ikeys #set $idata = $interfaces[$iface] #set $static_routes = $idata.get("static_routes", "") #for $route in $static_routes #set routepattern = $re.compile("[0-9/.]+:[0-9.]+") #if $routepattern.match($route) #set $routebits = $route.split(":") #set [$network, $router] = $route.split(":") <route> <destination>$network</destination> <netmask>-</netmask> <device>$iface</device> <gateway>$router</gateway> </route> #end if #end for #end for ## =================================================================== ## IPv6 routing setup ## =================================================================== #for $iface in $ikeys #set $idata = $interfaces[$iface] #set $ipv6_static_routes = $idata.get("ipv6_static_routes", "") #set $ipv6_default_gateway = $idata.get("ipv6_default_gateway", "") #if $ipv6_default_gateway != "" <route> <destination>default</destination> <netmask>-</netmask> <device>$iface</device> <gateway>$ipv6_default_gateway</gateway> </route> #end if #for $route in $ipv6_static_routes #set routepattern = $re.compile("[0-9a-fA-F:/]+,[0-9a-fA-F:]+") #if $routepattern.match($route) #set $routebits = $route.split(",") #set [$network, $router] = $route.split(",") <route> <destination>$network</destination> <netmask>-</netmask> <device>$iface</device> <gateway>$router</gateway> </route> #end if #end for #end for </routes> #end if </routing> </networking>
-
/var/lib/cobbler/snippets/suse-15.6_scriptwrapper.xml
<script> ## <debug config:type="boolean">true</debug> ## <feedback config:type="boolean">false</feedback> ## <location></location> #set $filename = $wrappedscript.replace('/', '_') <filename>$filename</filename> <source><![CDATA[ $SNIPPET($wrappedscript) ]]></source> </script>
-
/var/lib/cobbler/snippets/autoinstall_start
#set system_name = $getVar('system_name','') #set profile_name = $getVar('profile_name','') #if $system_name != '' #set object_type = 'system' #set object_name = $system_name #else if $profile_name != '' #set object_type = 'profile' #set object_name = $profile_name #else #set object_type = '' #set object_name = '' #end if #set breed = $getVar('breed','') #set srv = $getVar('http_server','') #set run_install_triggers = $getVar('run_install_triggers','') #set runpre = "" #if $object_type != '' ## RUN PRE TRIGGER #if $run_install_triggers #if $breed == 'redhat' or $breed == 'suse' #set runpre = "\ncurl \"http://%s/cblr/svc/op/trig/mode/pre/%s/%s\" -o /dev/null" % (srv, object_type, object_name) #else #set runpre = "\nwget \"http://%s/cblr/svc/op/trig/mode/pre/%s/%s\" -O /dev/null" % (srv, object_type, object_name) #end if #end if #end if #echo $runpre
-
/var/lib/cobbler/snippets/autoinstall_done
#set system_name = $getVar('system_name','') #set profile_name = $getVar('profile_name','') #if $system_name != '' #set object_type = 'system' #set object_name = $system_name #else if $profile_name != '' #set object_type = 'profile' #set object_name = $profile_name #else #set object_type = '' #set object_name = '' #end if #set breed = $getVar('breed','') #set os_version = $getVar('os_version','') #set srv = $getVar('http_server','') #set autoinstall = $getVar('autoinstall','') #set run_install_triggers = $getVar('run_install_triggers','') #set pxe_just_once = $getVar('pxe_just_once','') #set nopxe = "" #set save_autoinstall = "" #set runpost = "" #if $system_name != '' ## PXE JUST ONCE #if $pxe_just_once #if $breed == 'redhat' #set nopxe = "\ncurl \"http://%s/cblr/svc/op/nopxe/system/%s\" -o /dev/null" % (srv, system_name) #else if $breed == 'vmware' and $os_version == 'esx4' #set nopxe = "\ncurl \"http://%s/cblr/svc/op/nopxe/system/%s\" -o /dev/null" % (srv, system_name) #else #set nopxe = "\nwget \"http://%s/cblr/svc/op/nopxe/system/%s\" -O /dev/null" % (srv, system_name) #end if #end if #end if #if $object_type != '' ## SAVE AUTO INSTALLATION #if $autoinstall != '' #if $breed == 'redhat' #set save_autoinstall = "\ncurl \"http://%s/cblr/svc/op/autoinstall/%s/%s\" -o /root/cobbler.ks" % (srv, object_type, object_name) #else if $breed == 'suse' #set save_autoinstall = "\ncurl \"http://%s/cblr/svc/op/autoinstall/%s/%s\" -o /root/cobbler.xml" % (srv, object_type, object_name) #else if $breed == 'vmware' and $os_version == 'esx4' #set save_autoinstall = "\ncurl \"http://%s/cblr/svc/op/autoinstall/%s/%s\" -o /root/cobbler.ks" % (srv, object_type, object_name) #else if $breed == 'vmware' #set save_autoinstall = "\nwget \"http://%s/cblr/svc/op/autoinstall/%s/%s\" -O /var/log/cobbler.ks" % (srv, object_type, object_name) #else if $breed == 'debian' or $breed == 'ubuntu' #set save_autoinstall = "\nwget \"http://%s/cblr/svc/op/autoinstall/%s/%s\" -O /var/log/cobbler.seed" % (srv, object_type, object_name) #end if #end if ## RUN POST TRIGGER #if $run_install_triggers #if $breed == 'redhat' or $breed == 'suse' #set runpost = "\ncurl \"http://%s/cblr/svc/op/trig/mode/post/%s/%s\" -o /dev/null" % (srv, object_type, object_name) #else if $breed == 'vmware' and $os_version == 'esx4' #set runpost = "\ncurl \"http://%s/cblr/svc/op/trig/mode/post/%s/%s\" -o /dev/null" % (srv, object_type, object_name) #else #set runpost = "\nwget \"http://%s/cblr/svc/op/trig/mode/post/%s/%s\" -O /dev/null" % (srv, object_type, object_name) #end if #end if #end if #echo $nopxe #echo $save_autoinstall #echo $runpost
-
Enjoy Reading This Article?
Here are some more articles you might like to read next: