Skip to content

Commit 61c1363

Browse files
authored
Merge pull request #503 from sammj/m-m-multidisk
OpTestQemu: Better PHB handling, multi-disk setups, proper NIC support
2 parents e5295ee + 0571ae5 commit 61c1363

File tree

2 files changed

+123
-20
lines changed

2 files changed

+123
-20
lines changed

common/OpTestQemu.py

+91-20
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
@@ -71,6 +71,7 @@ def __init__(self, qemu_binary=None, pnor=None, skiboot=None,
7171
self.block_setup_term = block_setup_term # allows caller specific control of when to block setup_term
7272
self.setup_term_quiet = 0 # tells setup_term to not throw exceptions, like when system off
7373
self.setup_term_disable = 0 # flags the object to abandon setup_term operations, like when system off
74+
self.mac_str = '52:54:00:22:34:56'
7475

7576
# state tracking, reset on boot and state changes
7677
# console tracking done on System object for the system console
@@ -101,6 +102,10 @@ def disable_setup_term_quiet(self):
101102
self.setup_term_quiet = 0
102103
self.setup_term_disable = 0
103104

105+
# Because this makes sense for the console
106+
def update_disks(self, disks):
107+
self.disks = disks
108+
104109
def close(self):
105110
self.util.clear_state(self)
106111
try:
@@ -141,22 +146,81 @@ def connect(self):
141146
if self.initramfs is not None:
142147
cmd = cmd + " -initrd %s" % (self.initramfs)
143148

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+
# So in the powernv QEMU model we have 3 PHBs with one slot free each.
150+
# We can add a pcie bridge to each of these, and each bridge has 31
151+
# slots.. if you see where I'm going..
152+
cmd = (cmd
153+
+ " -device pcie-pci-bridge,id=pcie.3,bus=pcie.0,addr=0x0"
154+
+ " -device pcie-pci-bridge,id=pcie.4,bus=pcie.1,addr=0x0"
155+
+ " -device pcie-pci-bridge,id=pcie.5,bus=pcie.2,addr=0x0"
156+
)
157+
158+
# Put the NIC in slot 2 of the second PHB (1st is reserved for later)
159+
cmd = (cmd
160+
+ " -netdev user,id=u1 -device e1000e,netdev=u1,mac={},bus=pcie.4,addr=2"
161+
.format(self.mac_str)
149162
)
163+
prefilled_slots = 1
164+
150165
if self.cdrom is not None:
151-
# Put the CDROM on the second PHB
166+
# Put the CDROM in slot 3 of the second PHB
152167
cmd = (cmd
153168
+ " -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"
169+
+ " -device virtio-blk-pci,drive=cdrom01,id=virtio02,bus=pcie.4,addr=3"
155170
)
171+
prefilled_slots += 1
172+
173+
bridges = []
174+
bridges.append({'bus': 3, 'n_devices': 0, 'bridged' : False})
175+
bridges.append({'bus': 4, 'n_devices': prefilled_slots, 'bridged' : False})
176+
bridges.append({'bus': 5, 'n_devices': 0, 'bridged' : False})
177+
178+
# For any amount of disks we have, start finding spots for them in the PHBs
179+
if self.disks:
180+
diskid = 0
181+
bid = 0
182+
for disk in self.disks:
183+
bridge = bridges[bid]
184+
if bridge['n_devices'] >= 30:
185+
# This bridge is full
186+
if bid == len(bridges) - 1:
187+
# All bridges full, find one to extend
188+
if [x for x in bridges if x['bridged'] == False] == []:
189+
# We messed up and filled up all our slots
190+
raise OpTestError("Oops! We ran out of slots!")
191+
for i in range(0, bid):
192+
if not bridges[i]['bridged']:
193+
# We can add a bridge here
194+
parent = bridges[i]['bus']
195+
new = bridges[-1]['bus'] + 1
196+
print("Adding new bridge {} on bridge {}".format(new, parent))
197+
bridges.append({'bus': new, 'n_devices' : 0, 'bridged' : False})
198+
cmd = cmd + " -device pcie-pci-bridge,id=pcie.{},bus=pcie.{},addr=0x1".format(new, parent)
199+
bid = bid + 1
200+
bridges[i]['bridged'] = True
201+
bridge = bridges[bid]
202+
break
203+
else:
204+
# Just move to the next one, subsequent bridge should
205+
# always have slots
206+
bid = bid + 1
207+
bridge = bridges[bid]
208+
if bridge['n_devices'] >= 30:
209+
raise OpTestError("Lost track of our PCI bridges!")
210+
211+
# Got a bridge, let's go!
212+
# Valid bridge slots are 1..31, but keep 1 free for more bridges
213+
addr = 2 + bridge['n_devices']
214+
print("Adding disk {} on bus {} at address {}".format(diskid, bridge['bus'], addr))
215+
cmd = cmd + " -drive file={},id=disk{},if=none".format(disk.name, diskid)
216+
cmd = cmd + " -device virtio-blk-pci,drive=disk{},id=virtio{},bus=pcie.{},addr={}".format(diskid, diskid, bridge['bus'], hex(addr))
217+
diskid += 1
218+
bridge['n_devices'] += 1
219+
156220
# typical host ip=10.0.2.2 and typical skiroot 10.0.2.15
157221
# use skiroot as the source, no sshd in skiroot
222+
158223
fru_path = os.path.join(OpTestConfiguration.conf.basedir, "test_binaries", "qemu_fru")
159-
cmd = cmd + " -nic user,model=virtio-net-pci"
160224
cmd = cmd + " -device ipmi-bmc-sim,id=bmc0,frudatafile=" + fru_path + " -device isa-ipmi-bt,bmc=bmc0,irq=10"
161225
cmd = cmd + " -serial none -device isa-serial,chardev=s1 -chardev stdio,id=s1,signal=off"
162226
print(cmd)
@@ -236,6 +300,7 @@ class OpTestQemu():
236300
def __init__(self, conf=None, qemu_binary=None, pnor=None, skiboot=None,
237301
kernel=None, initramfs=None, cdrom=None,
238302
logfile=sys.stdout):
303+
self.disks = []
239304
# need the conf object to properly bind opened object
240305
# we need to be able to cleanup/close the temp file in signal handler
241306
self.conf = conf
@@ -267,31 +332,28 @@ def __init__(self, conf=None, qemu_binary=None, pnor=None, skiboot=None,
267332
" and then retry.")
268333
raise e
269334

335+
self.disks.append(self.conf.args.qemu_scratch_disk)
270336
atexit.register(self.__del__)
271337
self.console = QemuConsole(qemu_binary=qemu_binary,
272338
pnor=pnor,
273339
skiboot=skiboot,
274340
kernel=kernel,
275341
initramfs=initramfs,
276342
logfile=logfile,
277-
hda=self.conf.args.qemu_scratch_disk.name,
278-
cdrom=cdrom)
343+
disks=self.disks, cdrom=cdrom)
279344
self.ipmi = QemuIPMI(self.console)
280345
self.system = None
281346

282347
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:
348+
for fd in self.disks:
349+
log.debug("OpTestQemu cleaning up qemu_scratch_disk={}"
350+
.format(self.conf.args.qemu_scratch_disk))
286351
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")
352+
fd.close()
292353
except Exception as e:
293354
log.error("OpTestQemu cleanup, ignoring Exception={}"
294355
.format(e))
356+
self.disks = []
295357

296358
def set_system(self, system):
297359
self.console.system = system
@@ -332,3 +394,12 @@ def supports_ipmi_dcmi(self):
332394

333395
def has_ipmi_sel(self):
334396
return False
397+
398+
def add_temporary_disk(self, size):
399+
self.console.close()
400+
401+
fd = tempfile.NamedTemporaryFile(delete=True)
402+
self.disks.append(fd)
403+
create_hda = subprocess.check_call(["qemu-img", "create",
404+
"-fqcow2", fd.name, size])
405+
self.console.update_disks(self.disks)

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)