mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-21 15:18:19 +00:00
perf(blackboxtest): stop listening once a DHCP step has its reply
Every DHCP step listened for its whole timeout, even after the reply it waited for had arrived. The 15 DISCOVERs of pxe-arch-matrix.conf took 30 seconds on a management node that answers in milliseconds. A step now stops at the first reply of the type it expects. A step that asserts on offers, or expects none, still listens for the whole timeout, because it must hear every server or prove that none answered. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
@@ -228,8 +228,9 @@ class Wire(object):
|
||||
def send(self, frame):
|
||||
self.socket.send(frame)
|
||||
|
||||
def collect(self, session, deadline):
|
||||
"""Every reply to this session's xid until `deadline`.
|
||||
def collect(self, session, deadline, stop=()):
|
||||
"""Every reply to this session's xid until `deadline`, or until a
|
||||
reply of a type in `stop` arrives.
|
||||
|
||||
A reply of an unexpected type is kept, so a NAK where an ACK was
|
||||
wanted is reported as a NAK rather than as a timeout.
|
||||
@@ -237,7 +238,8 @@ class Wire(object):
|
||||
found = []
|
||||
while True:
|
||||
remaining = deadline - time.monotonic()
|
||||
if remaining <= 0 or not select.select([self.socket], [], [], remaining)[0]:
|
||||
if remaining <= 0 or any(r.fields["msgtype"] in stop for r in found) \
|
||||
or not select.select([self.socket], [], [], remaining)[0]:
|
||||
return found
|
||||
frame = self.socket.recv(2048)
|
||||
if frame is None:
|
||||
@@ -293,7 +295,8 @@ def run_step(step, session, context, timeout, retries):
|
||||
sent = describe(step, session, trans, context)
|
||||
session.wire.send(build_frame(step, session, trans, context,
|
||||
session.wire.scapy))
|
||||
replies = session.wire.collect(session, time.monotonic() + timeout)
|
||||
replies = session.wire.collect(session, time.monotonic() + timeout,
|
||||
stop_on(step, wanted))
|
||||
if any(r.fields["msgtype"] in wanted for r in replies) \
|
||||
or (replies and not wanted):
|
||||
break
|
||||
@@ -328,6 +331,18 @@ def run_step(step, session, context, timeout, retries):
|
||||
return chosen, extras, sent
|
||||
|
||||
|
||||
def stop_on(step, wanted):
|
||||
"""The reply types that end a step's listening window early.
|
||||
|
||||
A step that counts `offers` must hear every server, so it listens for the
|
||||
whole timeout. So does `expect = none`, where silence is the result.
|
||||
"""
|
||||
if any("offers" in (a.target, a.value)
|
||||
for a in step.assertions):
|
||||
return frozenset()
|
||||
return wanted
|
||||
|
||||
|
||||
def distinct_servers(replies):
|
||||
"""One reply per answering server.
|
||||
|
||||
|
||||
@@ -164,7 +164,8 @@ class FakeWire(object):
|
||||
def send(self, frame):
|
||||
self.sent += 1
|
||||
|
||||
def collect(self, session, deadline):
|
||||
def collect(self, session, deadline, stop=()):
|
||||
self.stop = stop
|
||||
return self.answers.pop(0) if self.answers else []
|
||||
|
||||
def close(self):
|
||||
@@ -239,6 +240,16 @@ class StepLoop(unittest.TestCase):
|
||||
self.assertEqual(extras["met"], met)
|
||||
self.assertEqual(session.state, dhcp.INIT)
|
||||
|
||||
def test_a_step_stops_listening_once_its_reply_arrived(self):
|
||||
# Unless it counts the servers that answered, or proves silence.
|
||||
cases = (("", {"OFFER"}), ("assert =\n offers == 1\n", set()),
|
||||
("expect = none\n", set()))
|
||||
for extra, stop in cases:
|
||||
step, = self.step("[step d]\ntype = discover\n" + extra)
|
||||
session = dhcp.Session(FakeWire([reply("OFFER")]))
|
||||
dhcp.run_step(step, session, subst.Context(), 0.1, 1)
|
||||
self.assertEqual(set(session.wire.stop), stop, extra)
|
||||
|
||||
def test_a_step_mac_replaces_the_random_one(self):
|
||||
step, = self.step("[step d]\ntype = discover\nmac = 02-00-00-00-00-AA\n")
|
||||
session = dhcp.Session(FakeWire())
|
||||
|
||||
Reference in New Issue
Block a user