Skip to content

Commit 6e78eea

Browse files
committed
OpTestQemu: Add PCI bridges to support more devices.
The powernv QEMU model only has 3 free slots which doesn't leave a lot of room for tests that want more complicated setups. Instead fill each of these slots with a PHB which we can insert further devices into. This also extends the scratch disk concept to support an arbitrary number of disks. The Petitboot10000Disks testcase provides an example of this functionality. The intended use case for this is to extend the Petitboot testcases to test against different source devices, filesystems, and configuration formats. For particularly ambitious testcases the QEMU setup will also dynamically insert more PHBs into the model to support an arbitrary number of devices. Signed-off-by: Samuel Mendoza-Jonas <[email protected]>
1 parent e5295ee commit 6e78eea

File tree

2 files changed

+117
-21
lines changed

2 files changed

+117
-21
lines changed

Diff for: common/OpTestQemu.py

+85-21
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@ class QemuConsole():
5151
def __init__(self, qemu_binary=None, pnor=None, skiboot=None,
5252
prompt=None, kernel=None, initramfs=None,
5353
block_setup_term=None, delaybeforesend=None,
54-
logfile=sys.stdout, hda=None, cdrom=None):
54+
logfile=sys.stdout, disks=None, cdrom=None):
5555
self.qemu_binary = qemu_binary
5656
self.pnor = pnor
5757
self.skiboot = skiboot
5858
self.kernel = kernel
5959
self.initramfs = initramfs
60-
self.hda = hda
60+
self.disks = disks
6161
self.state = ConsoleState.DISCONNECTED
6262
self.logfile = logfile
6363
self.delaybeforesend = delaybeforesend
@@ -101,6 +101,10 @@ def disable_setup_term_quiet(self):
101101
self.setup_term_quiet = 0
102102
self.setup_term_disable = 0
103103

104+
# Because this makes sense for the console
105+
def update_disks(self, disks):
106+
self.disks = disks
107+
104108
def close(self):
105109
self.util.clear_state(self)
106110
try:
@@ -141,22 +145,75 @@ def connect(self):
141145
if self.initramfs is not None:
142146
cmd = cmd + " -initrd %s" % (self.initramfs)
143147

144-
if self.hda is not None:
145-
# Put the disk on the first PHB
146-
cmd = (cmd
147-
+ " -drive file={},id=disk01,if=none".format(self.hda)
148-
+ " -device virtio-blk-pci,drive=disk01,id=virtio01,bus=pcie.0,addr=0"
149-
)
148+
# So in the powernv QEMU model we have 3 PHBs with one slot free each.
149+
# We can add a pcie bridge to each of these, and each bridge has 31
150+
# slots.. if you see where I'm going..
151+
cmd = (cmd
152+
+ " -device pcie-pci-bridge,id=pcie.3,bus=pcie.0,addr=0x0"
153+
+ " -device pcie-pci-bridge,id=pcie.4,bus=pcie.1,addr=0x0"
154+
+ " -device pcie-pci-bridge,id=pcie.5,bus=pcie.2,addr=0x0"
155+
)
156+
157+
prefilled_slots = 0
150158
if self.cdrom is not None:
151-
# Put the CDROM on the second PHB
159+
# Put the CDROM in slot 2 of the second PHB (1 is reserved for later)
152160
cmd = (cmd
153161
+ " -drive file={},id=cdrom01,if=none,media=cdrom".format(self.cdrom)
154-
+ " -device virtio-blk-pci,drive=cdrom01,id=virtio02,bus=pcie.1,addr=0"
162+
+ " -device virtio-blk-pci,drive=cdrom01,id=virtio02,bus=pcie.4,addr=2"
155163
)
164+
prefilled_slots += 1
165+
166+
bridges = []
167+
bridges.append({'bus': 3, 'n_devices': 0, 'bridged' : False})
168+
bridges.append({'bus': 4, 'n_devices': prefilled_slots, 'bridged' : False})
169+
bridges.append({'bus': 5, 'n_devices': 0, 'bridged' : False})
170+
171+
# For any amount of disks we have, start finding spots for them in the PHBs
172+
if self.disks:
173+
diskid = 0
174+
bid = 0
175+
for disk in self.disks:
176+
bridge = bridges[bid]
177+
if bridge['n_devices'] >= 30:
178+
# This bridge is full
179+
if bid == len(bridges) - 1:
180+
# All bridges full, find one to extend
181+
if [x for x in bridges if x['bridged'] == False] == []:
182+
# We messed up and filled up all our slots
183+
raise OpTestError("Oops! We ran out of slots!")
184+
for i in range(0, bid):
185+
if not bridges[i]['bridged']:
186+
# We can add a bridge here
187+
parent = bridges[i]['bus']
188+
new = bridges[-1]['bus'] + 1
189+
print("Adding new bridge {} on bridge {}".format(new, parent))
190+
bridges.append({'bus': new, 'n_devices' : 0, 'bridged' : False})
191+
cmd = cmd + " -device pcie-pci-bridge,id=pcie.{},bus=pcie.{},addr=0x1".format(new, parent)
192+
bid = bid + 1
193+
bridges[i]['bridged'] = True
194+
bridge = bridges[bid]
195+
break
196+
else:
197+
# Just move to the next one, subsequent bridge should
198+
# always have slots
199+
bid = bid + 1
200+
bridge = bridges[bid]
201+
if bridge['n_devices'] >= 30:
202+
raise OpTestError("Lost track of our PCI bridges!")
203+
204+
# Got a bridge, let's go!
205+
# Valid bridge slots are 1..31, but keep 1 free for more bridges
206+
addr = 2 + bridge['n_devices']
207+
print("Adding disk {} on bus {} at address {}".format(diskid, bridge['bus'], addr))
208+
cmd = cmd + " -drive file={},id=disk{},if=none".format(disk.name, diskid)
209+
cmd = cmd + " -device virtio-blk-pci,drive=disk{},id=virtio{},bus=pcie.{},addr={}".format(diskid, diskid, bridge['bus'], hex(addr))
210+
diskid += 1
211+
bridge['n_devices'] += 1
212+
156213
# typical host ip=10.0.2.2 and typical skiroot 10.0.2.15
157214
# use skiroot as the source, no sshd in skiroot
215+
158216
fru_path = os.path.join(OpTestConfiguration.conf.basedir, "test_binaries", "qemu_fru")
159-
cmd = cmd + " -nic user,model=virtio-net-pci"
160217
cmd = cmd + " -device ipmi-bmc-sim,id=bmc0,frudatafile=" + fru_path + " -device isa-ipmi-bt,bmc=bmc0,irq=10"
161218
cmd = cmd + " -serial none -device isa-serial,chardev=s1 -chardev stdio,id=s1,signal=off"
162219
print(cmd)
@@ -236,6 +293,7 @@ class OpTestQemu():
236293
def __init__(self, conf=None, qemu_binary=None, pnor=None, skiboot=None,
237294
kernel=None, initramfs=None, cdrom=None,
238295
logfile=sys.stdout):
296+
self.disks = []
239297
# need the conf object to properly bind opened object
240298
# we need to be able to cleanup/close the temp file in signal handler
241299
self.conf = conf
@@ -267,31 +325,28 @@ def __init__(self, conf=None, qemu_binary=None, pnor=None, skiboot=None,
267325
" and then retry.")
268326
raise e
269327

328+
self.disks.append(self.conf.args.qemu_scratch_disk)
270329
atexit.register(self.__del__)
271330
self.console = QemuConsole(qemu_binary=qemu_binary,
272331
pnor=pnor,
273332
skiboot=skiboot,
274333
kernel=kernel,
275334
initramfs=initramfs,
276335
logfile=logfile,
277-
hda=self.conf.args.qemu_scratch_disk.name,
278-
cdrom=cdrom)
336+
disks=self.disks, cdrom=cdrom)
279337
self.ipmi = QemuIPMI(self.console)
280338
self.system = None
281339

282340
def __del__(self):
283-
log.debug("OpTestQemu cleaning up qemu_scratch_disk={}"
284-
.format(self.conf.args.qemu_scratch_disk))
285-
if self.conf.args.qemu_scratch_disk:
341+
for fd in self.disks:
342+
log.debug("OpTestQemu cleaning up qemu_scratch_disk={}"
343+
.format(self.conf.args.qemu_scratch_disk))
286344
try:
287-
self.conf.args.qemu_scratch_disk.close()
288-
self.conf.args.qemu_scratch_disk = None
289-
# if this was a temp file it will be deleted upon close
290-
# optest_handler closes if signal encountered
291-
log.debug("OpTestQemu closed qemu_scratch_disk")
345+
fd.close()
292346
except Exception as e:
293347
log.error("OpTestQemu cleanup, ignoring Exception={}"
294348
.format(e))
349+
self.disks = []
295350

296351
def set_system(self, system):
297352
self.console.system = system
@@ -332,3 +387,12 @@ def supports_ipmi_dcmi(self):
332387

333388
def has_ipmi_sel(self):
334389
return False
390+
391+
def add_temporary_disk(self, size):
392+
self.console.close()
393+
394+
fd = tempfile.NamedTemporaryFile(delete=True)
395+
self.disks.append(fd)
396+
create_hda = subprocess.check_call(["qemu-img", "create",
397+
"-fqcow2", fd.name, size])
398+
self.console.update_disks(self.disks)

Diff for: testcases/Petitboot10000Disks.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env python2
2+
3+
import time
4+
import unittest
5+
6+
import OpTestConfiguration
7+
from common.OpTestUtil import OpTestUtil
8+
from common.OpTestSystem import OpSystemState
9+
from common.OpTestError import OpTestError
10+
from common.OpTestKeys import OpTestKeys as keys
11+
12+
class ManyDisksTestCase(unittest.TestCase):
13+
def setUp(self):
14+
conf = OpTestConfiguration.conf
15+
self.system = conf.system()
16+
self.bmc = conf.bmc()
17+
18+
if OpTestConfiguration.conf.args.bmc_type != "qemu":
19+
self.skipTest("10,000 disks requires QEMU")
20+
21+
# Realistically you probably don't have a machine on hand that has the
22+
# memory to do 10,000 disks. This starts at five, change this number to
23+
# push it further.
24+
for i in range(1,5):
25+
self.bmc.add_temporary_disk("500K")
26+
27+
self.system.goto_state(OpSystemState.PETITBOOT_SHELL)
28+
29+
def testListDisks(self):
30+
c = self.system.console
31+
32+
c.run_command("ls -l /dev/vd* | wc -l")

0 commit comments

Comments
 (0)