Accepting request 459343 from openSUSE:Factory:zSystems
New package per "Factory first" policy. Please list me as bug owner and maintainer, if possible. OBS-URL: https://build.opensuse.org/request/show/459343 OBS-URL: https://build.opensuse.org/package/show/Base:System/s390-tools?expand=0&rev=1
This commit is contained in:
501
zpxe.rexx
Normal file
501
zpxe.rexx
Normal file
@@ -0,0 +1,501 @@
|
||||
/* zPXE: REXX PXE Client for System z
|
||||
|
||||
zPXE is a PXE client used with Cobbler or a just a plain TFTP server.
|
||||
It must be run under z/VM. zPXE uses TFTP to first download a
|
||||
user-specific profile (if one exists), or a list of available profiles.
|
||||
From the profile a specific kernel, initial RAMdisk, and PARM file are
|
||||
then downloaded and 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 from a DASD volume by default. You can specify the
|
||||
default DASD device in ZPXE CONF, as well as the hostname or IP address
|
||||
of the Cobbler or TFTP server.
|
||||
---
|
||||
|
||||
Copyright 2006-2009, Red Hat, Inc
|
||||
Brad Hinson <bhinson@redhat.com>
|
||||
|
||||
Copyright 2012, SUSE Linux,
|
||||
Mark Post <mpost@suse.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
|
||||
*/
|
||||
|
||||
|
||||
/* Set the default environment for "safety" reasons. */
|
||||
ADDRESS COMMAND
|
||||
|
||||
/* Make it possible to interrupt zPXE and to enter CMS no matter how
|
||||
the guest was started, if there is a system-specific profile
|
||||
or not, etc.
|
||||
*/
|
||||
say
|
||||
say 'Enter a non-blank character and ENTER (or two ENTERs) within 10',
|
||||
'seconds to interrupt zPXE.'
|
||||
ADDRESS CMS 'WAKEUP +00:10 (CONS'
|
||||
/* Check for the interrupt code */
|
||||
if rc = 6 then do
|
||||
say 'Interrupt received: exiting to CMS...'
|
||||
pull /* Clear the stack */
|
||||
exit
|
||||
end
|
||||
|
||||
/* Was this script invoked with "debug" as one of the parameters? */
|
||||
nodebug=1
|
||||
if arg() then do
|
||||
parse upper arg uparg
|
||||
if index(uparg,'DEBUG') <> 0 then do
|
||||
trace i
|
||||
nodebug=0
|
||||
end
|
||||
else do /* This is a do/end in case we want to add to it later */
|
||||
trace e
|
||||
end
|
||||
end
|
||||
|
||||
/* Set some defaults */
|
||||
userid=''
|
||||
server=''
|
||||
iplDisk=''
|
||||
server_def = 'internal.tftp.server' /* define default TFTP server */
|
||||
iplDisk_def = '150' /* define default IPL DASD */
|
||||
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 A'
|
||||
|
||||
/* For translating strings to lowercase */
|
||||
lower = xrange('a','i')xrange('j','r')xrange('s','z')
|
||||
upper = xrange('A','I')xrange('J','R')xrange('S','Z')
|
||||
|
||||
/* Query user ID. This is used to determine:
|
||||
1. Whether a user-specific PXE profile exists.
|
||||
2. Whether user is disconnected.
|
||||
The logic that gets followed will vary based on the results.
|
||||
*/
|
||||
ADDRESS CMS 'QUERY USER' userid() '(STACK'
|
||||
parse pull userid_def dash dsc
|
||||
if dsc = 'DSC' then disconnected=1 /* user is disconnected */
|
||||
else disconnected=0
|
||||
|
||||
/* Yeah, this call to translate looks backward, but it's not. Sorry. */
|
||||
userid_def = translate(userid_def, lower, upper)
|
||||
|
||||
/* Useful settings normally found in PROFILE EXEC */
|
||||
'CP SET RUN ON'
|
||||
'CP SET PF11 RETRIEVE FORWARD'
|
||||
'CP SET PF12 RETRIEVE'
|
||||
|
||||
/* Useful setting for a script that may run unattended */
|
||||
'CP TERM HOLD OFF'
|
||||
|
||||
/* We want to have a way to figure out what went wrong if something
|
||||
isn't working.
|
||||
*/
|
||||
'CP SPOOL CONSOLE STOP CLOSE' /* Close any existing spooled console. */
|
||||
'CP SPOOL CONSOLE START' /* Start spooling the console for this run. */
|
||||
|
||||
if nodebug then ADDRESS CMS 'VMFCLEAR' /* clear screen */
|
||||
|
||||
/* The following two commands that were in the original script are */
|
||||
/* almost certainly not going to work for anyone that only has CP */
|
||||
/* privilege class G */
|
||||
/* 'set vdisk syslim infinite' */
|
||||
/* 'set vdisk userlim infinite' */
|
||||
|
||||
/* Define a temporary disk (VDISK) to store files and CMS FORMAT it */
|
||||
/* If your site doesn't allow this, but does allow TDISKs, change the */
|
||||
/* DEFINE command to T3390 instead */
|
||||
'CP SET EMSG OFF'
|
||||
if nodebug then trace off
|
||||
'CP DETACH FFFF' /* detach ffff if present */
|
||||
if nodebug then trace e
|
||||
else trace i
|
||||
'CP SET EMSG ON'
|
||||
'CP DEFINE VFB-512 AS FFFF BLK 144000' /* 512 byte block size ~70 MB */
|
||||
queue '1'
|
||||
queue 'tmpdsk'
|
||||
if nodebug then /* If debug was not specified, then */
|
||||
ADDRESS CMS 'set cmstype ht' /* suppress format output */
|
||||
ADDRESS CMS 'format ffff t' /* format VDISK as file mode t */
|
||||
ADDRESS CMS 'set cmstype rt' /* Resume seeing command output */
|
||||
say 'DASD FFFF has been CMS formatted'
|
||||
|
||||
/* Check for the ZPXE CONF A config file and use whatever is there in
|
||||
preference over the defaults in this script */
|
||||
call GetZPXECONF
|
||||
|
||||
/* For any values not found in ZPXE CONF A, or if it doesn't exist, use
|
||||
the default values specified in this script. */
|
||||
if server = '' then do
|
||||
say 'Setting TFTP server to 'server_def
|
||||
server = server_def
|
||||
end
|
||||
if iplDisk = '' then do
|
||||
say 'Setting IPL disk to default of 'iplDisk_def
|
||||
iplDisk = iplDisk_def
|
||||
end
|
||||
if userid = '' then do
|
||||
say 'Setting userid to default of 'userid_def
|
||||
userid = userid_def
|
||||
end
|
||||
|
||||
/* Link to TCPMAINT's 592 disk for access to the TFTP command */
|
||||
say
|
||||
ADDRESS CMS 'exec vmlink tcpmaint 592'
|
||||
|
||||
say
|
||||
say 'Connecting to server 'server /* print server name */
|
||||
|
||||
/* Check whether a user-specific PXE profile exists. */
|
||||
call GetTFTP '/s390x/s_'userid 'profile.detail.t'
|
||||
if lines(profiledetail) > 0 then call ProcessUserProfile
|
||||
else do /* no user-specific profile was found */
|
||||
say 'No profile found for' userid
|
||||
if disconnected then do /* user is disconnected */
|
||||
ADDRESS CMS 'release t (detach'
|
||||
ADDRESS CMS 'exec vmlink tcpmaint 592 <detach>'
|
||||
say 'User is disconnected. Booting from DASD 'iplDisk'...'
|
||||
'CP IPL' iplDisk
|
||||
end
|
||||
else call ProcessGenericProfiles /* user is interactive -> prompt */
|
||||
end /* no user-specific profile was found */
|
||||
|
||||
exit
|
||||
/* */
|
||||
/* Subroutines called from the main script */
|
||||
/* */
|
||||
|
||||
|
||||
/* Procedure GetZPXECONF
|
||||
*/
|
||||
GetZPXECONF:
|
||||
|
||||
if lines(config) > 0 then do
|
||||
say config "was found"
|
||||
do while lines(config) > 0
|
||||
inputline = linein(config)
|
||||
parse upper var inputline keyword value .
|
||||
select
|
||||
when (keyword = 'HOST') then do /* line is server hostname/IP */
|
||||
server = value
|
||||
if server = '' then say config "didn't have an IP address for",
|
||||
"the TFTP server."
|
||||
else say ' Setting TFTP server to 'server
|
||||
end
|
||||
|
||||
when (keyword = 'IPLDISK') then do /* line is default IPL disk */
|
||||
iplDisk = value
|
||||
if iplDisk = '' then say config "didn't have an IPL Disk parm."
|
||||
else say ' Setting IPL disk to 'iplDisk
|
||||
end
|
||||
|
||||
otherwise do /* line is userid to use instead of the default */
|
||||
userid = translate(keyword,lower,upper) /* Still not backward */
|
||||
say ' Setting userid to 'userid
|
||||
end
|
||||
|
||||
end /* select */
|
||||
end /* do while lines(config) > 0 */
|
||||
end /* if lines(config) > 0 */
|
||||
return /* GetZPXECONF */
|
||||
|
||||
|
||||
/* Procedure ProcessUserProfile
|
||||
*/
|
||||
ProcessUserProfile:
|
||||
|
||||
say 'Profile for 'userid' found'
|
||||
say
|
||||
bootRc = ParseSystemRecord() /* parse file for boot action */
|
||||
if bootRc = 0 then do
|
||||
say 'The profile said we should boot from local disk.'
|
||||
ADDRESS CMS 'release t (detach'
|
||||
ADDRESS CMS 'exec vmlink tcpmaint 592 <detach>'
|
||||
say 'IPLing from' iplDisk
|
||||
'CP IPL' iplDisk /* boot from default DASD */
|
||||
end /* if bootRc = 0 */
|
||||
else do /* The profile should contain pointers to kernel, etc.*/
|
||||
abort=0
|
||||
|
||||
/* Get the user PARM file that contains network info */
|
||||
say 'Downloading parameter file [/s390x/s_'userid'_parm]...'
|
||||
call GetTFTP '/s390x/s_'userid'_parm' 'zpxe.parm.t'
|
||||
if CheckDownload('s_'userid'_parm' zpxeparm) <> 0 then
|
||||
abort=1
|
||||
|
||||
/* Get the user CONF file that currently isn't used for anything */
|
||||
say 'Downloading conf file [/s390x/s_'userid'_conf]...'
|
||||
call GetTFTP '/s390x/s_'userid'_conf' 'zpxe.conf.t'
|
||||
if CheckDownload('s_'userid'_conf' zpxeconf) <> 0 then
|
||||
abort=1
|
||||
|
||||
if abort then do
|
||||
say 'Aborting PXE boot.'
|
||||
exit 99
|
||||
end
|
||||
|
||||
call DownloadBinaries /* download kernel and initrd */
|
||||
say 'Starting install...'
|
||||
say
|
||||
call PunchFiles /* punch files to begin install */
|
||||
exit
|
||||
end /* he profile should contain pointers to kernel, etc */
|
||||
|
||||
|
||||
/* ProcessGenericProfiles
|
||||
*/
|
||||
ProcessGenericProfiles:
|
||||
/* Download the list of generic profiles available */
|
||||
say 'Downloading the profile list [/s390x/profile_list]...'
|
||||
call GetTFTP '/s390x/profile_list' 'profile.list.t'
|
||||
if CheckDownload('profile_list' profilelist) <> 0 then do
|
||||
say '** **'
|
||||
say '** No profile list found **'
|
||||
say '** Possible error connecting to server? **'
|
||||
say '** **'
|
||||
exit 99
|
||||
end
|
||||
|
||||
/* Display a menu of the generic profiles */
|
||||
say
|
||||
say 'zPXE MENU'
|
||||
say '---------'
|
||||
|
||||
/* Display one profile per line */
|
||||
do count = 1 by 1 while lines(profilelist) > 0
|
||||
inputline = linein(profilelist)
|
||||
parse var inputline profile.count
|
||||
say count'. 'profile.count
|
||||
end
|
||||
|
||||
/* Add two non-profile selections to the menu */
|
||||
say count'. Don''t continue, exit to CMS'
|
||||
say
|
||||
say
|
||||
say 'Enter Choice -->'
|
||||
say 'or press <Enter> to boot from disk [DASD 'iplDisk']'
|
||||
|
||||
parse pull answer .
|
||||
select
|
||||
when answer = count then do /* Exit to CMS was selected */
|
||||
say
|
||||
say 'Exiting to CMS...'
|
||||
exit
|
||||
end
|
||||
|
||||
when answer = '' then do /* IPL from default disk */
|
||||
ADDRESS CMS 'release t (detach'
|
||||
ADDRESS CMS 'exec vmlink tcpmaint 592 <detach>'
|
||||
say 'Booting from DASD 'iplDisk'...'
|
||||
'CP IPL' iplDisk
|
||||
end
|
||||
|
||||
when (answer > 0) & (answer < count) then do /* valid response */
|
||||
abort=0
|
||||
|
||||
say 'Downloading generic profile [/s390x/p_'profile.answer']...'
|
||||
call GetTFTP '/s390x/p_'profile.answer 'profile.detail.t'
|
||||
if CheckDownload('p_'profile.answer profiledetail) <> 0 then
|
||||
abort=1
|
||||
|
||||
say 'Downloading generic parameter file',
|
||||
'[/s390x/p_'profile.answer'_parm]...'
|
||||
call GetTFTP '/s390x/p_'profile.answer'_parm' 'zpxe.parm.t'
|
||||
if CheckDownload('p_'profile.answer'_parm' zpxeparm) <> 0 then
|
||||
abort=1
|
||||
|
||||
say 'Downloading generic conf file',
|
||||
'[/s390x/p_'profile.answer'_conf]...'
|
||||
call GetTFTP '/s390x/p_'profile.answer'_conf' 'zpxe.conf.t'
|
||||
if CheckDownload('p_'profile.answer'_conf' zpxeconf) <> 0 then
|
||||
abort=1
|
||||
|
||||
if abort then do
|
||||
say 'Aborting PXE boot.'
|
||||
exit 99
|
||||
end
|
||||
|
||||
/* We have to add the HostIP parameter to the parm file, since that is
|
||||
going to vary for each guest, so we can't hard-code it in the generic
|
||||
profiles. We use the numeric part of the guest name, which starts in
|
||||
column 6, after "LINUX". But we have to watch out for leading zeros,
|
||||
since that will appear as an octal number to Linux. So, we use the
|
||||
fact that Rexx/Regina doesn't care about leading zeros, but will
|
||||
remove them when used in an arithmetic statement, such as follows. */
|
||||
lastoctet=substr(userid,6)
|
||||
lastoctet=lastoctet+0 /* Adding a zero won't change the value */
|
||||
hostipparm=' HostIP=10.121.157.'lastoctet
|
||||
call lineout zpxeparm, hostipparm
|
||||
call lineout zpxeparm /* close the output file */
|
||||
|
||||
if nodebug then ADDRESS CMS 'VMFCLEAR' /* clear screen */
|
||||
say
|
||||
say 'Using profile 'answer' ['profile.answer']'
|
||||
say
|
||||
call DownloadBinaries /* download kernel and initrd */
|
||||
|
||||
say 'Starting install...'
|
||||
say
|
||||
|
||||
call PunchFiles
|
||||
|
||||
end /* valid response */
|
||||
|
||||
otherwise do /* The user entered something that wasn't in the list */
|
||||
say 'Invalid choice, exiting to CMS...'
|
||||
exit
|
||||
end
|
||||
|
||||
end /* Select */
|
||||
|
||||
|
||||
/* 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'
|
||||
|
||||
if nodebug then
|
||||
ADDRESS CMS 'set cmstype ht' /* suppress TFTP output */
|
||||
ADDRESS CMS 'tftp' server
|
||||
ADDRESS CMS 'set cmstype rt'
|
||||
|
||||
return /* GetTFTP */
|
||||
|
||||
|
||||
/* Procedure CheckDownload
|
||||
TFTP is dumb, so you can't ever tell if a file was actually retrieved
|
||||
or not from the return code.
|
||||
path: The filename (including path) that was to be retrieved
|
||||
via TFTP
|
||||
filename: The local CMS filename that should have received it.
|
||||
*/
|
||||
CheckDownload:
|
||||
|
||||
parse arg path filename
|
||||
if lines(filename) = 0 then do
|
||||
say 'The' path 'file was not successfully retrieved'
|
||||
return 99
|
||||
end
|
||||
else return 0
|
||||
|
||||
/* End CheckDownload */
|
||||
|
||||
|
||||
/* 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
|
||||
if kernelpath = '' then do
|
||||
say 'The path to the kernel is null. Aborting...'
|
||||
exit 99
|
||||
end
|
||||
say 'Downloading kernel ['kernelpath']...'
|
||||
call GetTFTP kernelpath 'kernel.img.t' octet
|
||||
if CheckDownload(kernelpath kernel img t) <> 0 then do
|
||||
say 'Aborting PXE boot.'
|
||||
exit 99
|
||||
end
|
||||
|
||||
inputline = linein(profiledetail) /* second line is initrd */
|
||||
parse var inputline initrdpath
|
||||
if initrdpath = '' then do
|
||||
say 'The path to the initrd is null. Aborting...'
|
||||
exit 99
|
||||
end
|
||||
say 'Downloading initrd ['initrdpath']...'
|
||||
call GetTFTP initrdpath 'initrd.img.t' octet
|
||||
if CheckDownload(initrdpath initrd img t) <> 0 then do
|
||||
say 'Aborting PXE boot.'
|
||||
exit 99
|
||||
end
|
||||
|
||||
inputline = linein(profiledetail) /* third line is kernel parms */
|
||||
parse var inputline kparms
|
||||
if kparms <> '' then do /* If there are parms, add them to the end */
|
||||
call lineout zpxeparm, kparms /* add ks line to end of parm */
|
||||
call lineout zpxeparm /* close file */
|
||||
end
|
||||
|
||||
/* Convert to fixed record length since they're going to be run
|
||||
through the virtual card reader. */
|
||||
ADDRESS CMS 'pipe < KERNEL IMG T | fblock 80 00 | > KERNEL IMG T'
|
||||
ADDRESS CMS 'pipe < INITRD IMG T | fblock 80 00 | > INITRD IMG T'
|
||||
ADDRESS CMS 'pipe < ' zpxeparm ' | fblock 80 SPACE | > ' zpxeparm
|
||||
|
||||
return /* DownloadBinaries */
|
||||
|
||||
|
||||
/* Procedure PunchFiles
|
||||
Punch the kernel, initial RAMdisk, and PARM file.
|
||||
Then IPL to start the install process.
|
||||
*/
|
||||
PunchFiles:
|
||||
|
||||
'CP SPOOL PUNCH *'
|
||||
'CP CLOSE READER'
|
||||
'CP PURGE READER ALL' /* clear reader contents */
|
||||
|
||||
ADDRESS CMS 'punch kernel img t ( noheader' /* punch kernel */
|
||||
ADDRESS CMS 'punch zpxe parm t ( noheader' /* punch PARM file */
|
||||
ADDRESS CMS 'punch initrd img t ( noheader' /* punch initrd */
|
||||
ADDRESS CMS 'release t (detach' /* release and detach the VDISK */
|
||||
ADDRESS CMS 'exec vmlink tcpmaint 592 <detach>' /* and this disk */
|
||||
|
||||
'CP CHANGE READER ALL KEEP NOHOLD' /* keep files in reader */
|
||||
'CP IPL 00C CLEAR' /* IPL the reader */
|
||||
|
||||
return /* 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 .
|
||||
/* Close the file to reset the read pointer to the beginning. Yes I
|
||||
know that calling lineout to close a file seems weird, but it's
|
||||
how Rexx/Regina works. */
|
||||
call lineout profiledetail
|
||||
|
||||
if systemaction = 'local' then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
|
||||
/* End ParseSystemRecord */
|
Reference in New Issue
Block a user