Every MIDI link by wired address: flag links joined by name, hold MIDI moves them, refuse non-wired addresses
Tracks had joined NA Ingest, Lighting PC and Studio B by Bonjour name: macOS picks the network each time, so a reconnect could land on Wi-Fi or Dante. The wired check now also names a link joined by name; holding MIDI moves it to the wired address its own MIDI check names (a hold only: auto-fix never moves a working link). Reconnects prefer a wired address over a Wi-Fi or by-name one they last saw. The settings refuse a MIDI link whose address is a name or off the wired network, and the add-a-link screen fills in the wired address. Studio B's link now has its wired address, and Tracks' link to NA Ingest is watched (TRK INGEST). All links on the four Macs were moved and checked: wired IPv4, running. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,7 @@ SUB = {("auto", "windows"): "windows", ("site", "live_services"): "windows", ("t
|
||||
ELEMENT = {"tiles": "tile", "checks": "check", "restart_keys": "restart_key", "windows": "window", "fixes": "fix",
|
||||
"hosts": "host", "gears": "gear"}
|
||||
# Keys the code reads that are the watchdog's own working data, not settings.
|
||||
NOT_SETTINGS = {("check", "ip")}
|
||||
NOT_SETTINGS = {("check", "ip"), ("check", "repoint")} # set by the watchdog on a fix's copy of a check
|
||||
|
||||
|
||||
def dotted(node):
|
||||
|
||||
@@ -418,6 +418,7 @@ class TestChecks(unittest.TestCase):
|
||||
engine = sp.Engine(CONFIG)
|
||||
self.assertEqual(engine.step(self.snap, self.now)["midi"]["x"], "MIDI\nOK") # not read yet: no verdict
|
||||
self.snap.remote_midi["tracks"] = {"NA Tracks": {"port": 5004, "peers": [
|
||||
{"name": "NA Ingest", "address": "", "state": "running"},
|
||||
{"name": "SuperRack-LiveBox", "address": "", "state": "running"},
|
||||
{"name": "Studio B Mac Studio", "address": "", "state": "running"},
|
||||
{"name": "Lighting PC", "address": "", "state": "running"}]}}
|
||||
@@ -469,7 +470,7 @@ class TestChecks(unittest.TestCase):
|
||||
self.snap.remote_midi_time["tracks"] = self.now
|
||||
self.snap.midi["NA Ingest"]["peers"].append({"name": "ProdDash Lighting", "address": "10.6.3.222:5040", "state": "running"})
|
||||
out = sp.check_midi_wired(chk, self.snap, self.ctx)
|
||||
self.assertEqual([r.msg for _, r in out], ["PRODDASH LIGH. NOT WIRED"]) # once, not per session
|
||||
self.assertEqual([r.msg for _, r in out], ["PRODDASH LIGH. NOT WIRED", "STUDIO B MAC. BY NAME"]) # once each, not per session
|
||||
self.snap.remote_midi_time["tracks"] = self.now - 30 # a stale stream: no verdict
|
||||
self.snap.midi["NA Ingest"]["peers"].pop()
|
||||
self.assertEqual([r.level for _, r in sp.check_midi_wired(chk, self.snap, self.ctx)], [sp.OK])
|
||||
@@ -480,6 +481,12 @@ class TestChecks(unittest.TestCase):
|
||||
self.assertEqual([e for e in schema.validate(cfg)[0] if "wired" in e], [])
|
||||
next(t for t in cfg["tiles"] if t["id"] == "midi")["checks"][-1]["nets"] = ["wifi"]
|
||||
self.assertTrue(any("not a network" in e for e in schema.validate(cfg)[0]))
|
||||
next(t for t in cfg["tiles"] if t["id"] == "midi")["checks"][-1]["nets"] = ["10.1.0.0/16"]
|
||||
peer = next(c for c in midi["checks"] if c.get("peer") == "Studio B Mac Studio")
|
||||
for bad in ("\tStudio B Mac Studio\tlocal.", "10.6.9.193:5004"): # a name, Wi-Fi: refused
|
||||
peer["address"] = bad
|
||||
self.assertTrue(any("Studio B Mac Studio" in e and "not on the wired network" in e for e in schema.validate(cfg)[0]), bad)
|
||||
peer["address"] = "10.1.13.175:5004"
|
||||
self.snap.midi["NA Ingest"]["peers"].append({"name": "ProdDash Lighting", "address": "10.6.3.222:5040", "state": "running"})
|
||||
next(t for t in cfg["tiles"] if t["id"] == "midi")["checks"][-1]["nets"] = ["10.1.0.0/16"]
|
||||
tiles, _ = run_steps(sp.Engine(cfg), self.snap, self.now, 4)
|
||||
@@ -1169,7 +1176,8 @@ class TestFixes(unittest.TestCase):
|
||||
|
||||
def test_midi_reconnect_where_the_peer_was_last_seen(self):
|
||||
others = [{"name": "SuperRack-LiveBox", "address": "10.1.13.140:5004", "state": "running"},
|
||||
{"name": "Studio B Mac Studio", "address": "\tStudio B Mac Studio\tlocal.", "state": "running"}]
|
||||
{"name": "NA Ingest", "address": "10.1.1.1:5009", "state": "running"},
|
||||
{"name": "Studio B Mac Studio", "address": "10.1.13.175:5004", "state": "running"}]
|
||||
tracks = {"NA Tracks": {"port": 5004, "peers": others + [
|
||||
{"name": "Lighting PC", "address": "10.1.10.77:5004", "state": "running"}]}}
|
||||
sp.remember_peers(self.snap, "tracks", tracks) # seen at a new address earlier
|
||||
@@ -1193,7 +1201,7 @@ class TestFixes(unittest.TestCase):
|
||||
self.assertTrue(sp.execute_fix(CONFIG, "midi_reconnect", chk))
|
||||
argv, kw = run.call_args.args[0], run.call_args.kwargs
|
||||
self.assertIn("waterschurchmedia@10.1.3.227", argv)
|
||||
self.assertIn("ns['connect_peer']('NA Tracks', 'Lighting PC', '10.1.10.206:5004')", kw["input"])
|
||||
self.assertIn("ns['connect_peer']('NA Tracks', 'Lighting PC', '10.1.10.206:5004', repoint=False)", kw["input"])
|
||||
failed = mock.Mock(returncode=255, stdout="", stderr="ssh: timed out")
|
||||
with mock.patch.object(sp.subprocess, "run", return_value=failed):
|
||||
self.assertFalse(sp.execute_fix(CONFIG, "midi_reconnect", chk))
|
||||
@@ -1230,6 +1238,60 @@ class TestFixes(unittest.TestCase):
|
||||
self.assertEqual([[p["name"] for p in w["peers"]] for w in writes],
|
||||
[["Lighting PC"], ["Lighting PC", "SuperRack-LiveBox"]]) # dropped, then added back
|
||||
|
||||
def test_a_peer_listed_elsewhere_is_moved_to_its_wired_address(self):
|
||||
import coremidi
|
||||
from unittest import mock
|
||||
sess = {"dns-sd-name": "NA Tracks", "port": 5004, "peers": [
|
||||
{"name": "Lighting PC", "address": "10.1.10.206:5004", "state": "running"},
|
||||
{"name": "Studio B Mac Studio", "address": "\tStudio B Mac Studio\tlocal.", "state": "running"}]}
|
||||
writes = []
|
||||
with mock.patch.object(coremidi, "_session_entities", return_value=[(1, sess)]), \
|
||||
mock.patch.object(coremidi, "_string_prop", return_value=None), \
|
||||
mock.patch.object(coremidi, "_set_dict_prop", side_effect=lambda e, k, v: writes.append(v) or 0):
|
||||
self.assertEqual(coremidi.connect_peer("NA Tracks", "Studio B Mac Studio", "10.1.13.175:5004"), "already")
|
||||
self.assertEqual(writes, []) # a plain reconnect never moves a live link
|
||||
self.assertEqual(coremidi.connect_peer("NA Tracks", "Studio B Mac Studio", "10.1.13.175:5004",
|
||||
settle=lambda s: None, repoint=True), "moved")
|
||||
self.assertEqual(coremidi.connect_peer("NA Tracks", "Lighting PC", "10.1.10.206:5004", repoint=True), "already")
|
||||
self.assertEqual([[p["address"] for p in w["peers"]] for w in writes],
|
||||
[["10.1.10.206:5004"], ["10.1.10.206:5004", "10.1.13.175:5004"]]) # dropped, then at the wired address
|
||||
|
||||
def test_midi_reconnects_stay_on_the_wired_network(self):
|
||||
nets = [sp.ipaddress.ip_network("10.1.0.0/16")]
|
||||
self.assertEqual(sp.pick_midi_address(nets, "10.1.10.77:5004", "10.1.10.206:5004"), "10.1.10.77:5004") # moved, still wired
|
||||
self.assertEqual(sp.pick_midi_address(nets, "10.6.3.222:5040", "10.1.1.1:5040"), "10.1.1.1:5040") # last seen on Wi-Fi
|
||||
self.assertEqual(sp.pick_midi_address(nets, "\tNA Ingest\tlocal.", "10.1.1.1:5009"), "10.1.1.1:5009") # last seen by name
|
||||
self.assertEqual(sp.pick_midi_address(nets, "\tNA Ingest\tlocal.", None), "\tNA Ingest\tlocal.") # nothing better known
|
||||
self.assertEqual(sp.pick_midi_address([], "10.6.3.222:5040", "10.1.1.1:5040"), "10.6.3.222:5040") # no rule: as before
|
||||
|
||||
def test_a_link_joined_by_name_is_flagged_and_a_hold_moves_it(self):
|
||||
cfg = json.loads(json.dumps(CONFIG))
|
||||
midi = next(t for t in cfg["tiles"] if t["id"] == "midi")
|
||||
midi["checks"] = [c for c in midi["checks"] if c["type"] != "midi_wired"] + [
|
||||
{"type": "midi_wired", "nets": ["10.1.0.0/16"], "fix": "midi_reconnect"}]
|
||||
for c in midi["checks"]:
|
||||
if c.get("peer") == "Studio B Mac Studio":
|
||||
c["address"] = "10.1.13.175:5004"
|
||||
engine = sp.Engine(cfg)
|
||||
engine.executor = engine.poe_executor = lambda *a: True
|
||||
self.snap.remote_midi["tracks"] = {"NA Tracks": {"port": 5004, "peers": [
|
||||
{"name": "Lighting PC", "address": "10.1.10.206:5004", "state": "running"},
|
||||
{"name": "NA Ingest", "address": "10.1.1.1:5009", "state": "running"},
|
||||
{"name": "SuperRack-LiveBox", "address": "10.1.13.140:5004", "state": "running"},
|
||||
{"name": "Studio B Mac Studio", "address": "\tStudio B Mac Studio\tlocal.", "state": "running"},
|
||||
{"name": "ProdDash Lighting", "address": "10.6.3.222:5040", "state": "running"}]}}
|
||||
now = SUNDAY
|
||||
for t in range(9): # the stream stays fresh
|
||||
self.snap.remote_midi_time["tracks"] = now + t
|
||||
tiles = engine.step(self.snap, now + t)
|
||||
self.assertIn("STUDIO B MAC. BY NAME", tiles["midi"]["x"])
|
||||
plan = [(kind, what, c) for _, kind, what, c in engine.fix_plan("midi", self.snap)]
|
||||
moves = [c for kind, what, c in plan if c.get("repoint")]
|
||||
self.assertEqual([(c["host"], c["session"], c["peer"], c["address"]) for c in moves],
|
||||
[("tracks", "NA Tracks", "Studio B Mac Studio", "10.1.13.175:5004")]) # ProdDash's own: left to it
|
||||
auto = engine.fix_plan("midi", self.snap, SUNDAY + 30, 0, True)
|
||||
self.assertFalse(any(c.get("repoint") for _, _, _, c in auto)) # never by itself: a hold
|
||||
|
||||
def test_an_off_pc_is_woken(self):
|
||||
from unittest import mock
|
||||
woke = []
|
||||
|
||||
Reference in New Issue
Block a user