/* 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 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') /* Useful settings normally found in PROFILE EXEC */ 'cp set run on' 'cp set pf11 retrieve forward' 'cp set pf12 retrieve' /* 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 . end /* Define temporary disk (VDISK) to store files */ 'detach ffff' /* detach ffff if present */ 'define vfb-512 as ffff blk 100000' /* 512 byte block size =~ 50 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' /* 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) /* 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 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 */