Skip to content

Commit ad312a5

Browse files
committed
FEATURE: Halt the install (sles) with an error message on the console and in the message queue if we are unable to create a RAID. To override, set the attribute 'halt_install_on_error=False'.
1 parent 06dcbc7 commit ad312a5

5 files changed

Lines changed: 123 additions & 82 deletions

File tree

common/src/stack/storage-config/bin/configure-controllers.py

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#
99

1010
import sys
11+
from subprocess import CalledProcessError
1112

1213
sys.path.append('/tmp')
1314
from stack_site import *
@@ -71,15 +72,13 @@ def getController():
7172
## MAIN
7273
##
7374

74-
if 'nukecontroller' in attributes:
75-
nukecontroller = str2bool(attributes['nukecontroller'])
76-
else:
77-
nukecontroller = False
75+
# Halt the install with an error message on the console and in the message queue
76+
# if we are unable to create a RAID.
77+
# To override, set the attribute 'halt_install_on_error=False'.
7878

79-
if 'secureerase' in attributes:
80-
secureerase = str2bool(attributes['secureerase'])
81-
else:
82-
secureerase = False
79+
halt_on_error = str2bool(attributes.get('halt_install_on_error', True))
80+
nukecontroller = str2bool(attributes.get('nukecontroller', False))
81+
secureerase = str2bool(attributes.get('secureerase', False))
8382

8483
#
8584
# if 'secureerase' is true, then that implies that 'nukecontroller' is true
@@ -181,11 +180,15 @@ def getController():
181180
#
182181
continue
183182

184-
if raidlevel == 'hotspare' and arrayid == 'global':
185-
ctrl.doGlobalHotSpare(adapter, enclosure, hotspares, options)
186-
else:
187-
ctrl.doRaid(raidlevel, adapter, enclosure, slots, hotspares,
188-
options)
183+
try:
184+
if raidlevel == 'hotspare' and arrayid == 'global':
185+
ctrl.doGlobalHotSpare(adapter, enclosure, hotspares, options, check=halt_on_error)
186+
else:
187+
ctrl.doRaid(raidlevel, adapter, enclosure, slots, hotspares, options, check=halt_on_error)
188+
except CalledProcessError as e:
189+
print(' '.join(e.cmd))
190+
print(f'output: {e.stdout}')
191+
sys.exit(1)
189192

190193
for slot in slots + hotspares:
191194
try:
@@ -235,26 +238,30 @@ def getController():
235238
enclosure = ctrl.getEnclosure(adapter)
236239

237240
if 'arrayid' in o.keys():
238-
if o['arrayid'] == '*':
239-
if raidlevel == '1':
240-
#
241-
# special case for arrayid == '*' and raidlevel 1 -- put the remaining
242-
# disks into RAID 1 pairs
243-
#
244-
while len(freeslots[adapter]) > 1:
245-
disks = [ freeslots[adapter][0], freeslots[adapter][1] ]
246-
ctrl.doRaid(1, adapter, enclosure, disks, [], options)
247-
248-
freeslots[adapter].remove(disks[0])
249-
freeslots[adapter].remove(disks[1])
241+
try:
242+
if o['arrayid'] == '*':
243+
if raidlevel == '1':
244+
#
245+
# special case for arrayid == '*' and raidlevel 1 -- put the remaining
246+
# disks into RAID 1 pairs
247+
#
248+
while len(freeslots[adapter]) > 1:
249+
disks = [ freeslots[adapter][0], freeslots[adapter][1] ]
250+
ctrl.doRaid(1, adapter, enclosure, disks, [], options, check=halt_on_error)
251+
252+
freeslots[adapter].remove(disks[0])
253+
freeslots[adapter].remove(disks[1])
254+
else:
255+
# JBOD Mode for the remainder
256+
for slot in freeslots[adapter]:
257+
ctrl.doRaid(0, adapter, enclosure, [ slot ],
258+
[], options, check=halt_on_error)
259+
260+
# RAID mode - Single array for remaining disks
250261
else:
251-
# JBOD Mode for the remainder
252-
for slot in freeslots[adapter]:
253-
ctrl.doRaid(0, adapter, enclosure, [ slot ],
254-
[], options)
255-
256-
# RAID mode - Single array for remaining disks
257-
else:
258-
ctrl.doRaid(raidlevel, adapter, enclosure, freeslots[adapter],
259-
hotspares, options)
260-
262+
ctrl.doRaid(raidlevel, adapter, enclosure, freeslots[adapter],
263+
hotspares, options, check=halt_on_error)
264+
except CalledProcessError as e:
265+
print(' '.join(e.cmd))
266+
print(f'output: {e.stdout}')
267+
sys.exit(1)

common/src/stack/storage-config/lib/controller_hpssacli.py

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,29 @@
66
class CLI:
77
debug = 0
88

9-
def run(self, args):
9+
def run(self, args, check=False):
10+
'''
11+
check=False was added to allow this subprocess wrapper to halt the install on error.
12+
currently, only commands related to creating RAID volumes will ever have check=True
13+
other commands may be expected to fail
14+
'''
15+
1016
if not os.path.exists('/opt/stack/sbin/hpssacli'):
1117
return []
1218

1319
cmd = [ '/opt/stack/sbin/hpssacli', 'ctrl' ]
1420
cmd.extend(args)
1521

16-
file = open('/tmp/hpssacli.log', 'a')
17-
file.write('cmd: %s\n' % ' '.join(cmd))
22+
with open('/tmp/hpssacli.log', 'a') as fi:
23+
fi.write('cmd: %s\n' % ' '.join(cmd))
1824

19-
p = subprocess.run(cmd, stdin=subprocess.PIPE)
20-
result = p.stdout.decode()
25+
p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8', check=check)
26+
result = p.stdout
2127

22-
file.write('result:\n')
23-
for line in result:
24-
file.write('%s' % line)
25-
file.write('\n\n')
26-
file.close()
28+
fi.write('result:\n')
29+
for line in result:
30+
fi.write('%s' % line)
31+
fi.write('\n\n')
2732

2833
return result
2934

@@ -165,9 +170,7 @@ def doSecureErase(self, enclosure, adapter, slot):
165170
#
166171
return
167172

168-
def doRaid(self, raidlevel, adapter, enclosure, slots, hotspares,
169-
flags):
170-
173+
def doRaid(self, raidlevel, adapter, enclosure, slots, hotspares, flags, check):
171174
drives = []
172175
for slot in slots:
173176
#
@@ -201,7 +204,7 @@ def doRaid(self, raidlevel, adapter, enclosure, slots, hotspares,
201204
f = flags.split()
202205
cmd.extend(f)
203206

204-
result = self.run(cmd)
207+
result = self.run(cmd, check=check)
205208

206209
if hotspares:
207210
#
@@ -227,9 +230,9 @@ def doRaid(self, raidlevel, adapter, enclosure, slots, hotspares,
227230

228231
cmd = [ 'slot=%d' % adapter, 'array', arrayid,
229232
'add', 'spares=%s' % ','.join(spares) ]
230-
result = self.run(cmd)
233+
result = self.run(cmd, check=check)
231234

232-
def doGlobalHotSpare(self, adapter, enclosure, hotspares, options):
235+
def doGlobalHotSpare(self, adapter, enclosure, hotspares, options, check):
233236
spares = []
234237
for slot in hotspares:
235238
enclosure = self.getEnclosure(adapter, slot)
@@ -241,5 +244,11 @@ def doGlobalHotSpare(self, adapter, enclosure, hotspares, options):
241244
if options:
242245
f = options.split()
243246
cmd.extend(f)
244-
result = self.run(cmd)
245-
247+
result = self.run(cmd, check=check)
248+
249+
if __name__ == '__main__':
250+
s = CLI()
251+
a = s.getAdapter()
252+
if a is not None:
253+
print(s.getEnclosure(a))
254+
print(s.getSlots(a))

common/src/stack/storage-config/lib/controller_megacli.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,24 @@ class CLI:
99
'cached', 'direct', 'endskcache', 'disdskcache',
1010
'cachedbadbbu', 'nocachedbadbbu' ]
1111

12-
def run(self, args):
12+
def run(self, args, check=False):
13+
'''
14+
check=False was added to allow this subprocess wrapper to halt the install on error.
15+
currently, only commands related to creating RAID volumes will ever have check=True
16+
other commands may be expected to fail
17+
'''
18+
1319
cmd = [ '/opt/stack/sbin/MegaCli' ]
1420
cmd.extend(args)
15-
file = open('/tmp/MegaCli.log', 'a+')
16-
file.write('cmd: %s\n' % ' '.join(cmd))
17-
file.close()
21+
with open('/tmp/MegaCli.log', 'a+') as fi:
22+
fi.write('cmd: %s\n' % ' '.join(cmd))
1823
cmd.extend(['-AppLogFile','/tmp/MegaCli.log'])
1924

2025
result = []
2126

22-
p = subprocess.run(cmd, stdout=subprocess.PIPE)
27+
p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8', check=check)
2328

24-
for line in p.stdout.decode().splitlines():
29+
for line in p.stdout.splitlines():
2530
tokens = line.split(':', 1)
2631
if len(tokens) != 2:
2732
continue
@@ -82,8 +87,7 @@ def getSlots(self, adapter):
8287

8388
return slots
8489

85-
def doStrippedRaid(self, raidlevel, adapter, enclosure, slots,
86-
hotspares, flags):
90+
def doStrippedRaid(self, raidlevel, adapter, enclosure, slots, hotspares, flags, check):
8791

8892
if raidlevel == '10':
8993
cmd = [ '-CfgSpanAdd', '-r10' ]
@@ -139,7 +143,7 @@ def doStrippedRaid(self, raidlevel, adapter, enclosure, slots,
139143

140144
cmd.append('-force')
141145
cmd.append('-a%d' % adapter)
142-
results = self.run(cmd)
146+
results = self.run(cmd, check=check)
143147

144148
#
145149
# apply any flags that are not able to be set in
@@ -159,7 +163,7 @@ def doStrippedRaid(self, raidlevel, adapter, enclosure, slots,
159163
for f in setpropflags:
160164
cmd = [ '-LDSetProp', f, '-L%d' % vid,
161165
'-a%d' % adapter ]
162-
results = self.run(cmd)
166+
results = self.run(cmd, check=check)
163167

164168
#
165169
# support for dedicated hot spares for 10, 50 and 60
@@ -170,8 +174,7 @@ def doStrippedRaid(self, raidlevel, adapter, enclosure, slots,
170174
' '.join(options))
171175

172176

173-
def doRaid(self, raidlevel, adapter, enclosure, slots, hotspares,
174-
flags):
177+
def doRaid(self, raidlevel, adapter, enclosure, slots, hotspares, flags, check):
175178

176179
if raidlevel in [ '10', '50', '60' ]:
177180
self.doStrippedRaid(raidlevel, adapter, enclosure,
@@ -207,7 +210,7 @@ def doRaid(self, raidlevel, adapter, enclosure, slots, hotspares,
207210

208211
cmd.append('-force')
209212
cmd.append('-a%d' % adapter)
210-
results = self.run(cmd)
213+
results = self.run(cmd, check=check)
211214

212215
#
213216
# apply any flags that are not able to be set in
@@ -226,10 +229,10 @@ def doRaid(self, raidlevel, adapter, enclosure, slots, hotspares,
226229
for f in setpropflags:
227230
cmd = [ '-LDSetProp', f, '-L%d' % vid,
228231
'-a%d' % adapter ]
229-
results = self.run(cmd)
232+
results = self.run(cmd, check=check)
230233

231234

232-
def doGlobalHotSpare(self, adapter, enclosure, hotspares, flags):
235+
def doGlobalHotSpare(self, adapter, enclosure, hotspares, flags, check):
233236
for hotspare in hotspares:
234237
cmd = [ '-PDHSP', '-Set', '-PhysDrv',
235238
'[%s:%d]' % (enclosure, hotspare),
@@ -238,5 +241,11 @@ def doGlobalHotSpare(self, adapter, enclosure, hotspares, flags):
238241
if flags:
239242
cmd.append(flags)
240243

241-
self.run(cmd)
244+
self.run(cmd, check=check)
242245

246+
if __name__ == '__main__':
247+
s = CLI()
248+
a = s.getAdapter()
249+
if a is not None:
250+
print(s.getEnclosure(a))
251+
print(s.getSlots(a))

common/src/stack/storage-config/lib/controller_storcli.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@
77

88
class CLI:
99

10-
def run(self, args, json_out = False):
10+
def run(self, args, json_out=False, check=False):
11+
'''
12+
check=False was added to allow this subprocess wrapper to halt the install on error.
13+
currently, only commands related to creating RAID volumes will ever have check=True
14+
other commands may be expected to fail
15+
'''
16+
1117
cmd = [ '/opt/stack/sbin/storcli' ]
1218
cmd.extend(args)
1319
try:
@@ -19,12 +25,12 @@ def run(self, args, json_out = False):
1925
if json_out:
2026
cmd.append('J')
2127

22-
f = open('/tmp/MegaSAS.log','a')
23-
f.write('cmd: %s\n\n' % ' '.join(cmd))
24-
p = subprocess.run(cmd, stdout=subprocess.PIPE)
25-
o = p.stdout.decode()
26-
f.write('%s\n\n' % o)
27-
f.close()
28+
with open('/tmp/MegaSAS.log','a') as fi:
29+
fi.write('cmd: %s\n\n' % ' '.join(cmd))
30+
p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8', check=check)
31+
o = p.stdout
32+
fi.write('%s\n\n' % o)
33+
2834
if json_out:
2935
j = json.loads(o)
3036
result = j['Controllers'][0]
@@ -115,8 +121,7 @@ def getSlots(self, adapter):
115121

116122
return slots
117123

118-
def doRaid(self, raidlevel, adapter, enclosure, slots, hotspares,
119-
flags):
124+
def doRaid(self, raidlevel, adapter, enclosure, slots, hotspares, flags, check):
120125
args = ['/c%d' % adapter, 'add','vd',
121126
'type=r%s' % raidlevel]
122127

@@ -174,9 +179,9 @@ def doRaid(self, raidlevel, adapter, enclosure, slots, hotspares,
174179
args.append('spares=%s' % ','.join(hs))
175180

176181
args.append('force')
177-
self.run(args)
182+
self.run(args, check=check)
178183

179-
def doGlobalHotSpare(self, adapter, enclosure, hotspares, options):
184+
def doGlobalHotSpare(self, adapter, enclosure, hotspares, options, check):
180185
if enclosure:
181186
loc = '/c%d/e%d' % (adapter, enclosure)
182187
else:
@@ -187,11 +192,11 @@ def doGlobalHotSpare(self, adapter, enclosure, hotspares, options):
187192
if options:
188193
f = options.split()
189194
args.extend(f)
190-
self.run(args)
195+
self.run(args, check=check)
191196

192197

193198
if __name__ == '__main__':
194-
s = StorCLI()
199+
s = CLI()
195200
a = s.getAdapter()
196201
if a is not None:
197202
print(s.getEnclosure(a))

sles/src/stack/images/common/sles-stacki.img-patches/usr/lib/YaST2/startup/First-Stage/F08-stacki

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ echo "tracker = ${FRONTEND}:3825" > /tmp/stack.conf
5959
# configure the hardware disk array controller first
6060
#
6161
/opt/stack/bin/configure-controllers.py
62+
if [ $? -ne 0 ]
63+
then
64+
touch /tmp/wait
65+
echo "Unable to complete storage controller configuration."
66+
while [ -f /tmp/wait ]
67+
do
68+
sleep 1
69+
/opt/stack/bin/smq-publish -chealth '{"state": "controller config failed - check console or reinstall with halt_install_on_error=False"}'
70+
done
71+
fi
72+
6273

6374
udevadm settle --timeout=60
6475

0 commit comments

Comments
 (0)