File zpxe.rexx of Package s390-tools
/* zPXE: REXX PXE Client for System z
zPXE is a PXE client used with Cobbler. It must be run under
z/VM. zPXE uses TFTP to first download a list of profiles,
then a specific kernel, initial RAMdisk, and PARM file. These
files are then punched to start the install process.
zPXE does not require a writeable 191 A disk. Files are
downloaded to a temporary disk (VDISK).
zPXE can also IPL a DASD disk by default. You can specify the
default dasd in ZPXE CONF, as well as the hostname of the Cobbler
server.
---
Copyright 2006-2009, Red Hat, Inc
Brad Hinson <bhinson@redhat.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/* Defaults */
server = '' /* define server in ZPXE CONF */
iplDisk = 100 /* overridden by value in ZPXE CONF */
profilelist = PROFILE LIST T /* VDISK will be defined as T later */
profiledetail = PROFILE DETAIL T
zpxeparm = ZPXE PARM T
zpxeconf = ZPXE CONF T
config = ZPXE CONF
/* For translating strings to lowercase */
upper = xrange('A', 'Z')
lower = xrange('a', 'z')
/* Query user ID. This is used later to determine:
1. Whether a user-specific PXE profile exists.
2. Whether user is disconnected. If so, IPL the default disk.
*/
'pipe cp query' userid() '| var user'
parse value user with id . dsc .
userid = translate(id, lower, upper)
/* Useful settings normally found in PROFILE EXEC */
'cp set run on'
'cp set pf11 retrieve forward'
'cp set pf12 retrieve'
/* Make it possible to interrupt zPXE and to enter CMS even with a
specific user profile present
*/
if (dsc <> 'DSC') then do /* user is connected */
say ''
say 'Enter a non-blank character and ENTER (or two ENTERs) within 10'
say ' seconds to interrupt zPXE.'
'WAKEUP +00:10 (CONS'
/* Check for interrupt */
if rc = 6 then do
say 'Interrupt: entering CMS.'
pull /* Clear Stack */
exit
end
end
/* Check for config file */
if lines(config) > 0 then do
inputline = linein(config) /* first line is server hostname/IP */
parse var inputline . server .
inputline = linein(config) /* second line is DASD disk to IPL */
parse var inputline . iplDisk .
if lines(config) > 0 then do
inputline = linein(config) /* third line is name of system in cobbler */
parse var inputline . userid .
end
end
/* Define temporary disk (VDISK) to store files */
'set vdisk syslim infinite'
'set vdisk userlim infinite'
'detach ffff' /* detach ffff if present */
'define vfb-512 as ffff blk 200000' /* 512 byte block size =~ 100 MB */
queue '1'
queue 'tmpdsk'
'format ffff t' /* format VDISK as file mode t */
/* Link TCPMAINT disk for access to TFTP */
'link tcpmaint 592 592 rr'
'access 592 e'
/* Check whether a user-specific PXE profile exists.
If so, proceed with this. Otherwise, continue and
show the system-wide profile menu.
*/
call GetTFTP '/s390x/s_'userid 'profile.detail.t'
if lines(profiledetail) > 0 then do
/* Get user PARM and CONF containing network info */
call GetTFTP '/s390x/s_'userid'_parm' 'zpxe.parm.t'
call GetTFTP '/s390x/s_'userid'_conf' 'zpxe.conf.t'
vmfclear /* clear screen */
call CheckServer /* print server name */
say 'Profile 'userid' found'
say ''
bootRc = ParseSystemRecord() /* parse file for boot action */
if bootRc = 0 then
'cp ipl' iplDisk /* boot default DASD */
else do
call DownloadBinaries /* download kernel and initrd */
say 'Starting install...'
say ''
call PunchFiles /* punch files to begin install */
exit
end /* if bootRc = 0 */
end /* if user-specific profile found */
/* Download initial profile list */
call GetTFTP '/s390x/profile_list' 'profile.list.t'
vmfclear /* clear screen */
call CheckServer /* print server name */
say 'zPXE MENU' /* show menu */
say '---------'
count = 0
do while lines(profilelist) > 0 /* display one profile per line */
count = count + 1
inputline = linein(profilelist)
parse var inputline profile.count
say count'. 'profile.count
end
if (count = 0) then
say '** Error connecting to server: no profiles found **'
count = count + 1
say count'. Exit to CMS shell [IPL CMS]'
say ''
say ''
say 'Enter Choice -->'
say 'or press <Enter> to boot from disk [DASD 'iplDisk']'
/* Check if user is disconnected, indicating
logon by XAUTOLOG. In this case, IPL the
default disk.
*/
if (dsc = 'DSC') then do /* user is disconnected */
say 'User disconnected. Booting from DASD 'iplDisk'...'
'cp ipl' iplDisk
end
else do /* user is interactive -> prompt */
parse upper pull answer .
select
when (answer = count)
then do
say 'Exiting to CMS shell...'
exit
end
when (answer = '') /* IPL by default */
then do
say 'Booting from DASD 'iplDisk'...'
'cp ipl' iplDisk
end
when (answer < 0) | (answer > count) /* invalid respone */
then do
say 'Invalid choice, exiting to CMS shell.'
exit
end
when (answer > 0) & (answer < count) /* valid response */
then do
call GetTFTP '/s390x/p_'profile.answer 'profile.detail.t'
/* get profile-based PARM and CONF files */
call GetTFTP '/s390x/p_'profile.answer'_parm' 'zpxe.parm.t'
call GetTFTP '/s390x/p_'profile.answer'_conf' 'zpxe.conf.t'
vmfclear /* clear screen */
say 'Using profile 'answer' ['profile.answer']'
say ''
call DownloadBinaries /* download kernel and initrd */
say 'Starting install...'
say ''
call PunchFiles
end /* valid answer */
otherwise
say 'Invalid choice, exiting to CMS shell.'
exit
end /* Select */
end
exit
/* Procedure CheckServer
Print error message if server is not defined. Otherwise
show server name
*/
CheckServer:
if server = '' then
say '** Error: No host defined in ZPXE.CONF **'
else say 'Connected to server 'server
say ''
return 0 /* CheckServer */
/* Procedure GetTFTP
Use CMS TFTP client to download files
path: remote file location
filename: local file name
transfermode [optional]: 'ascii' or 'octet'
*/
GetTFTP:
parse arg path filename transfermode
if transfermode <> '' then
queue 'mode' transfermode
queue 'get 'path filename
queue 'quit'
'set cmstype ht' /* suppress tftp output */
tftp server
'set cmstype rt'
return 0 /* GetTFTP */
/* Procedure DownloadBinaries
Download kernel and initial RAMdisk. Convert both
to fixed record length 80.
*/
DownloadBinaries:
inputline = linein(profiledetail) /* first line is kernel */
parse var inputline kernelpath
say 'Downloading kernel ['kernelpath']...'
call GetTFTP kernelpath 'kernel.img.t' octet
inputline = linein(profiledetail) /* second line is initrd */
parse var inputline initrdpath
say 'Downloading initrd ['initrdpath']...'
call GetTFTP initrdpath 'initrd.img.t' octet
inputline = linein(profiledetail) /* third line is ks kernel arg */
parse var inputline ksline
call lineout zpxeparm, ksline /* add ks line to end of parm */
call lineout zpxeparm /* close file */
/* convert to fixed record length */
'pipe < KERNEL IMG T | fblock 80 00 | > KERNEL IMG T'
'pipe < INITRD IMG T | fblock 80 00 | > INITRD IMG T'
return 0 /* DownloadBinaries */
/* Procedure PunchFiles
Punch the kernel, initial RAMdisk, and PARM file.
Then IPL to start the install process.
*/
PunchFiles:
'spool punch *'
'close reader'
'purge reader all' /* clear reader contents */
'punch kernel img t (noh' /* punch kernel */
'punch zpxe parm t (noh' /* punch PARM file */
'punch initrd img t (noh' /* punch initrd */
'change reader all keep' /* keep files in reader */
'ipl 00c clear' /* IPL the reader */
return 0 /* PunchFiles */
/* Procedure ParseSystemRecord
Open system record file to look for local boot flag.
Return 0 if local flag found (guest will IPL default DASD).
Return 1 otherwise (guest will download kernel/initrd and install).
*/
ParseSystemRecord:
inputline = linein(profiledetail) /* get first line */
parse var inputline systemaction .
call lineout profiledetail /* close file */
if systemaction = 'local' then
return 0
else
return 1
/* End ParseSystemRecord */