part 4 缺陷 3:并发 cell 撞毁——唯一身份 + run_cell 接线
RETEST3 缺陷 3:固定 ve-chv-cell 名 + 固定 10.200.0.1/24, 第二个 cell 在 nspawn 建 veth 时即 File exists/Address already assigned。 - CellNet + allocate_cell_net():每次 run_cell 在 flock 锁下 (/run/chv/.cell-alloc.lock,30 秒超时)分配唯一身份: machine chv-cell-<xx>(11 字符)、宿主 ve-chv-cell-<xx> (14 字符,接口名 15 字符上限内)、10.200.<xx>.0/24; 扫描活接口 + 地址 + machinectl 在册名,避让注销滞后的下标。 - 配网命令表(host/cell/cleanup)与 build_nspawn_argv 接受 net/cell_net 参数;None 时仍用旧常量(argv 子命令与旧测试)。 - run_cell 重写:分配身份 -> 生成专属文件 -> 起步门 payload -> 等 veth(20 秒)-> 宿主配网 -> 轮询 init pid(10 秒) -> 写 pid 文件 -> 单元内配网 -> 开门 -> 等退出码; nspawn 已死返回其退出码;finally 清 NAT/pid/专属文件。 共享网络模式跳过门与身份(无配网可等)。 - broker:pid ns 对照改多 cell 版(peer_allowed_multi + _read_cell_pids:cell-<machine>.pid glob + 兼容旧 cell.pid; 对端 ns 与任一活单元同 ns 即放行;残留 pid 文件只跳过, 全部读不到才拒绝,不降级)。 新测试:CellAllocTest、CellPart4NetworkTest、 CellPart4ArgvTest(cell_net 两法)、BrokerMultiTest。
This commit is contained in:
1 parent
2be69af92c
commit
7a227323f2
3 files changed
+524
-47
No files matched your search
@@ -835,6 +835,25 @@ class CellPart4ArgvTest(unittest.TestCase):
|
||||
|
||||
|
||||
|
||||
def test_cell_net_machine_and_no_legacy_name(self):
|
||||
net = CELL.CellNet(index=3, machine="chv-cell-03",
|
||||
host_iface="ve-chv-cell-03",
|
||||
host_ip="10.200.3.1", cell_ip="10.200.3.2",
|
||||
subnet="10.200.3.0/24")
|
||||
argv = CELL.build_nspawn_argv(["true"], cell_net=net)
|
||||
self.assertIn("--machine=chv-cell-03", argv)
|
||||
self.assertNotIn("--machine=chv-cell", argv)
|
||||
# 旧常量身份仍是默认(argv 子命令与旧测试不改)。
|
||||
self.assertIn("--machine=chv-cell",
|
||||
CELL.build_nspawn_argv(["true"]))
|
||||
|
||||
def test_legacy_cell_net(self):
|
||||
net = CELL.legacy_cell_net()
|
||||
self.assertEqual(net.machine, "chv-cell")
|
||||
self.assertEqual(net.host_iface, "ve-chv-cell")
|
||||
self.assertEqual(net.subnet, "10.200.0.0/24")
|
||||
|
||||
|
||||
class CellFilesTest(unittest.TestCase):
|
||||
"""part 4:单元专属 resolv.conf/hosts 生成。"""
|
||||
|
||||
@@ -890,5 +909,166 @@ class CellFilesTest(unittest.TestCase):
|
||||
hosts_file=os.path.join(tmp, "missing-hosts"))
|
||||
|
||||
|
||||
class CellPart4NetworkTest(unittest.TestCase):
|
||||
"""part 4:配网命令表走唯一身份。"""
|
||||
|
||||
def _net(self, index=5):
|
||||
return CELL.CellNet(
|
||||
index=index, machine="chv-cell-%02x" % index,
|
||||
host_iface="ve-chv-cell-%02x" % index,
|
||||
host_ip="10.200.%d.1" % index,
|
||||
cell_ip="10.200.%d.2" % index,
|
||||
subnet="10.200.%d.0/24" % index)
|
||||
|
||||
def test_host_commands_use_cell_net(self):
|
||||
net = self._net(5)
|
||||
flat = [" ".join(c)
|
||||
for c in CELL.host_network_commands("enp0s5", net)]
|
||||
self.assertIn("ip addr add 10.200.5.1/24 dev ve-chv-cell-05",
|
||||
flat)
|
||||
self.assertTrue(any(
|
||||
"iptables -t nat -A POSTROUTING -s 10.200.5.0/24 "
|
||||
"-o enp0s5 -j MASQUERADE" in line for line in flat))
|
||||
# 旧常量仍是默认。
|
||||
legacy = [" ".join(c)
|
||||
for c in CELL.host_network_commands("enp0s5")]
|
||||
self.assertIn("ip addr add 10.200.0.1/24 dev ve-chv-cell",
|
||||
legacy)
|
||||
|
||||
def test_cell_commands_use_cell_net(self):
|
||||
net = self._net(7)
|
||||
flat = [" ".join(c)
|
||||
for c in CELL.cell_network_commands(4321, net)]
|
||||
self.assertIn("nsenter -t 4321 -n ip addr add 10.200.7.2/24 "
|
||||
"dev host0", flat)
|
||||
self.assertIn("nsenter -t 4321 -n ip route add default via "
|
||||
"10.200.7.1", flat)
|
||||
|
||||
|
||||
class CellAllocTest(unittest.TestCase):
|
||||
"""part 4:锁下分配唯一 cell 身份。"""
|
||||
|
||||
class _FakeProc:
|
||||
def __init__(self, returncode=0, stdout=""):
|
||||
self.returncode = returncode
|
||||
self.stdout = stdout
|
||||
|
||||
def _fake_run(self, link_out="", addr_out="", machinectl_out="",
|
||||
machinectl_rc=0):
|
||||
def run(cmd, **kwargs):
|
||||
if cmd[:3] == ["ip", "-o", "link"]:
|
||||
return self._FakeProc(0, link_out)
|
||||
if cmd[:3] == ["ip", "-o", "addr"]:
|
||||
return self._FakeProc(0, addr_out)
|
||||
if cmd[0] == "machinectl":
|
||||
return self._FakeProc(machinectl_rc, machinectl_out)
|
||||
raise AssertionError("意外命令:%r" % (cmd,))
|
||||
return run
|
||||
|
||||
def test_allocates_lowest_free_index(self):
|
||||
link = ("2: ve-chv-cell-00@if2: <UP> mtu 1500\n"
|
||||
"3: ve-chv-cell-01@if3: <UP> mtu 1500\n")
|
||||
addr = ("2: ve-chv-cell-00 inet 10.200.0.1/24 scope global\n"
|
||||
"3: ve-chv-cell-01 inet 10.200.1.1/24 scope global\n")
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
lock = os.path.join(tmp, "lock")
|
||||
net = CELL.allocate_cell_net(
|
||||
run=self._fake_run(link, addr), lock_path=lock)
|
||||
self.assertEqual(net.index, 2)
|
||||
self.assertEqual(net.machine, "chv-cell-02")
|
||||
self.assertEqual(net.host_iface, "ve-chv-cell-02")
|
||||
self.assertEqual(net.host_ip, "10.200.2.1")
|
||||
self.assertEqual(net.cell_ip, "10.200.2.2")
|
||||
self.assertEqual(net.subnet, "10.200.2.0/24")
|
||||
self.assertLessEqual(len(net.host_iface), 15)
|
||||
|
||||
def test_machinectl_registered_index_skipped(self):
|
||||
# 接口已消失、machine 仍在注销:避让该下标。
|
||||
run = self._fake_run(
|
||||
"", "", machinectl_out="chv-cell-00 1234 ...\n")
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
net = CELL.allocate_cell_net(
|
||||
run=run, lock_path=os.path.join(tmp, "lock"))
|
||||
self.assertEqual(net.index, 1)
|
||||
|
||||
def test_machinectl_missing_proceeds(self):
|
||||
def run(cmd, **kwargs):
|
||||
if cmd[0] == "machinectl":
|
||||
raise OSError("无此命令")
|
||||
return self._FakeProc(0, "")
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
net = CELL.allocate_cell_net(
|
||||
run=run, lock_path=os.path.join(tmp, "lock"))
|
||||
self.assertEqual(net.index, 0)
|
||||
|
||||
def test_ip_failure_is_loud(self):
|
||||
def run(cmd, **kwargs):
|
||||
return self._FakeProc(1, "")
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
with self.assertRaises(RuntimeError):
|
||||
CELL.allocate_cell_net(
|
||||
run=run, lock_path=os.path.join(tmp, "lock"))
|
||||
|
||||
|
||||
class BrokerMultiTest(unittest.TestCase):
|
||||
"""part 4:broker 对多 cell 的 pid ns 对照。"""
|
||||
|
||||
def _fake_proc(self, root, mapping):
|
||||
for pid, ns in mapping.items():
|
||||
d = os.path.join(root, str(pid), "ns")
|
||||
os.makedirs(d)
|
||||
os.symlink("pid:[%s]" % ns, os.path.join(d, "pid"))
|
||||
|
||||
def test_peer_allowed_if_matches_any_cell(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
self._fake_proc(tmp, {100: "111", 101: "222", 200: "222"})
|
||||
ok, _ = BROKER.peer_allowed_multi(
|
||||
1000, 200, [100, 101], proc_root=tmp)
|
||||
self.assertTrue(ok)
|
||||
|
||||
def test_peer_denied_if_matches_none(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
self._fake_proc(tmp, {100: "111", 101: "222", 200: "333"})
|
||||
ok, reason = BROKER.peer_allowed_multi(
|
||||
1000, 200, [100, 101], proc_root=tmp)
|
||||
self.assertFalse(ok)
|
||||
self.assertIn("pid ns", reason)
|
||||
|
||||
def test_stale_pid_file_skipped_not_fatal(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
# 100 已退出(无 /proc 项),101 活着且同 ns。
|
||||
self._fake_proc(tmp, {101: "222", 200: "222"})
|
||||
ok, _ = BROKER.peer_allowed_multi(
|
||||
1000, 200, [100, 101], proc_root=tmp)
|
||||
self.assertTrue(ok)
|
||||
|
||||
def test_all_ns_unreadable_rejects(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
self._fake_proc(tmp, {200: "222"})
|
||||
ok, _ = BROKER.peer_allowed_multi(
|
||||
1000, 200, [100, 101], proc_root=tmp)
|
||||
self.assertFalse(ok)
|
||||
|
||||
def test_no_pid_files_uid_only(self):
|
||||
ok, reason = BROKER.peer_allowed_multi(1000, 200, [])
|
||||
self.assertTrue(ok)
|
||||
self.assertIn("未做 pid ns 对照", reason)
|
||||
|
||||
def test_read_cell_pids_glob_and_legacy(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
with open(os.path.join(tmp, "cell-chv-cell-00.pid"),
|
||||
"w") as fh:
|
||||
fh.write("100")
|
||||
with open(os.path.join(tmp, "cell-chv-cell-01.pid"),
|
||||
"w") as fh:
|
||||
fh.write("101")
|
||||
with open(os.path.join(tmp, "cell.pid"), "w") as fh:
|
||||
fh.write("101")
|
||||
with open(os.path.join(tmp, "cell-chv-cell-02.pid"),
|
||||
"w") as fh:
|
||||
fh.write("not-a-pid")
|
||||
self.assertEqual(BROKER._read_cell_pids(tmp), [100, 101])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user