forked from SUSE/s390-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
zpxe.rexx
528 lines (434 loc) · 16.8 KB
/
zpxe.rexx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
/* 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 <[email protected]>
Copyright 2012, 2017, SUSE Linux,
Mark Post <[email protected]>
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
/* Save the value of the trace state */
tvar_o=trace()
tvar_c=tvar_o
/* Was this script invoked with "debug" as one of the parameters? */
debug=0
if arg() then
do
parse upper arg uparg
do sub=1 to words(uparg)
if word(uparg,sub) = "DEBUG" then
do
debug=1;
trace i;
tvar_c=trace()
end;
else do /* This is a do/end in case we want to add to it later */
trace e
tvar_c=trace()
end
end
end
/* Set some defaults */
/* These values are intended to be modified by the site using this */
/* script to match their environment. */
userid=''
server=''
iplDisk=''
server_def = 'internal.tftp.server' /* define default TFTP server */
iplDisk_def = '150' /* define default IPL DASD */
FM='T' /* Default file mode is T */
profilelist = 'PROFILE LIST' FM /* Disk will be accessed as FM later */
profiledetail = 'PROFILE DETAIL' FM
zpxeparm = 'ZPXE PARM' FM
zpxeconf = 'ZPXE CONF' FM
config = 'ZPXE CONF A'
seconds=10 /* The default amount of time to wait for console input */
workDiskType='VFB-512'
workDiskSize=200000 /* This is approximately 97MB of space. */
/* For TDISK instead of VDISK, comment out the previous two lines and */
/* uncomment the following two lines.*/
/* workDisk='T3390' */
/* workDiskSize=138 */
/* 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.
*/
if debug then say 'Debugging, so we will skip the wait and just run.'
else do
say
say 'Enter a non-blank character and ENTER (or two ENTERs)',
'within' seconds 'seconds to interrupt zPXE.'
ADDRESS CMS 'WAKEUP +00:'seconds '(CONS'
/* Check for the interrupt code */
if rc = 6 then do
say 'Interrupt received: exiting to CMS...'
ADDRESS CMS 'DESBUF' /* Clear the stack */
exit
end
end
/* 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 \ debug 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 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 \ debug then trace off
'CP DETACH FFFF' /* detach ffff if present */
trace value tvar_c
'CP SET EMSG ON'
'CP DEFINE' workDiskType' AS FFFF' workDiskSize
queue '1'
queue 'tmpdsk'
if \ debug then /* If debug was not specified, then */
ADDRESS CMS 'set cmstype ht' /* suppress format output */
ADDRESS CMS 'format ffff' FM /* format VDISK as file mode FM */
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.'FM
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' FM '(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 */
trace value tvar_o
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' FM '(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.'FM
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.'FM
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.'FM
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' FM '(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.'FM
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.'FM
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.'FM
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 \ debug 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 \ debug 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.'FM octet
if CheckDownload(kernelpath kernel img FM) <> 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.'FM octet
if CheckDownload(initrdpath initrd img FM) <> 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 'FM' | fblock 80 00 | > KERNEL IMG' FM
ADDRESS CMS 'pipe < INITRD IMG 'FM' | fblock 80 00 | > INITRD IMG' FM
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' FM '( noheader' /* punch kernel */
ADDRESS CMS 'punch zpxe parm' FM '( noheader' /* punch PARM file */
ADDRESS CMS 'punch initrd img' FM '( noheader' /* punch initrd */
ADDRESS CMS 'release' FM '(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 */