fix(chv): restore 路径补 H7 net 校验+登记,seed 暂存文件走 atomic_file
restore_vm 此前既不校验 net 冲突、也不登记 net.json: 已登记 VM 的 tap/mac/ip 会被恢复出的 VM 静默复用,且 恢复出的 VM 自身不登记、后来的 wake 也看不到它,两边 都漏判。现与 wake 同款:_validate_restore 在起 VMM 前 校验冲突(net_tap_conflict + vm_failed 审计), _restore_boot 在 restore 成功后登记 net.json,登记失败 审计 net_registry_write_failed + 拒绝恢复(P2-19 语义, stop_vm 清半恢复 VM)。 hostkeys.write_seed/_apply_seed_extras 的 meta-data、 user-data、network-config 改走 atomic_file(mkstemp 0600 → 写 → fsync → rename):user-data 含 host 私钥明文, 直接写 + 事后 chmod 会在 chmod 前以 umask 权限短暂可读; 半截文件本身到不了 ISO(唯一消费者 genisoimage 只在 write_seed 返回后跑),原子写同时堵住权限窗口与半截残留。 回归测试 6 个:restore 冲突拒起/恢复后登记/登记失败 拒起/无 net_json 不登记;seed 走原子写(0600、无 tmp 残留)/写失败不留半截文件。CHV 全套件 395 测试通过。
This commit is contained in:
1 parent
f448ecb74b
commit
06c3e172fe
5 files changed
+211
-18
No files matched your search
@@ -312,6 +312,77 @@ class RestoreSnapshotTest(CloseupCase):
|
||||
out2 = ex.wake_from_hibernate(self._task(), url)
|
||||
self.assertEqual(out2["mode"], "restored")
|
||||
|
||||
def _register_other_vm_net(self, vm="chv-other"):
|
||||
vm_dir = os.path.join(self.state_dir, vm)
|
||||
os.makedirs(vm_dir, exist_ok=True)
|
||||
with open(os.path.join(vm_dir, "net.json"), "w") as fh:
|
||||
json.dump([{"tap": "tap0",
|
||||
"mac": "aa:bb:cc:dd:ee:01",
|
||||
"ip": "192.168.10.10",
|
||||
"mask": ""}], fh)
|
||||
|
||||
def test_restore_net_conflict_refused_before_launch(self):
|
||||
# feat-chv-cover100:restore 也须过 H7 冲突校验——
|
||||
# 此前 restore 路径不校验不登记,恢复出的 VM 会与
|
||||
# 已登记 VM 静默复用 tap/mac/ip,且自身不登记、后
|
||||
# 来的 wake 也看不到它,两边都漏判。
|
||||
url, _ = self._snap_url()
|
||||
self._register_other_vm_net()
|
||||
ex = self.make_exec(env_extra={
|
||||
"CHV_NET_JSON": '[{"tap": "tap0", '
|
||||
'"mac": "aa:bb:cc:dd:ee:02"}]'})
|
||||
with self.assertRaises(ChvFailure) as ctx:
|
||||
ex.restore_vm(self._task(), url)
|
||||
self.assertEqual(ctx.exception.kind, "net_tap_conflict")
|
||||
self.assertIn("vm_failed", self._audit_events())
|
||||
self.assertEqual(self.launches, [])
|
||||
self.assertFalse(os.path.exists(
|
||||
os.path.join(self.state_dir, "chv-abc123", "net.json")))
|
||||
|
||||
def test_restore_registers_net_entries(self):
|
||||
# feat-chv-cover100:恢复成功后登记 net.json,后来
|
||||
# 的 wake 才能看到恢复出的 VM 占用的 tap/mac/ip。
|
||||
url, _ = self._snap_url()
|
||||
ex = self.make_exec(env_extra={
|
||||
"CHV_NET_JSON": '[{"tap": "tap0", '
|
||||
'"mac": "aa:bb:cc:dd:ee:01", '
|
||||
'"ip": "192.168.10.10"}]'})
|
||||
out = ex.restore_vm(self._task(), url)
|
||||
self.assertEqual(out["mode"], "restored")
|
||||
with open(os.path.join(self.state_dir, "chv-abc123",
|
||||
"net.json")) as fh:
|
||||
entries = json.load(fh)
|
||||
self.assertEqual(entries[0]["tap"], "tap0")
|
||||
self.assertEqual(entries[0]["mac"], "aa:bb:cc:dd:ee:01")
|
||||
self.assertEqual(entries[0]["ip"], "192.168.10.10")
|
||||
|
||||
def test_restore_net_register_failure_refuses(self):
|
||||
# feat-chv-cover100:登记失败与 wake 同款审计+拒绝
|
||||
#(P2-19 语义),restore_vm 的 except 会 stop_vm
|
||||
# 清掉半恢复 VM(shutdown 已发出)。
|
||||
url, _ = self._snap_url()
|
||||
blocker = os.path.join(self.state_dir, "chv-abc123")
|
||||
os.makedirs(self.state_dir, exist_ok=True)
|
||||
with open(blocker, "w") as fh:
|
||||
fh.write("x")
|
||||
ex = self.make_exec(env_extra={
|
||||
"CHV_NET_JSON": '[{"tap": "tap0"}]'})
|
||||
with self.assertRaises(ChvFailure) as ctx:
|
||||
ex.restore_vm(self._task(), url)
|
||||
self.assertEqual(ctx.exception.kind,
|
||||
"net_registry_write_failed")
|
||||
self.assertIn("net_registry_write_failed",
|
||||
self._audit_events())
|
||||
self.assertTrue(any(c[-1] == "shutdown" for c in self.calls))
|
||||
|
||||
def test_restore_without_net_json_writes_no_registry(self):
|
||||
url, _ = self._snap_url()
|
||||
ex = self.make_exec()
|
||||
out = ex.restore_vm(self._task(), url)
|
||||
self.assertEqual(out["mode"], "restored")
|
||||
self.assertFalse(os.path.exists(
|
||||
os.path.join(self.state_dir, "chv-abc123", "net.json")))
|
||||
|
||||
def test_restore_provisioned_missing_known_hosts(self):
|
||||
url, _ = self._snap_url()
|
||||
ex = self.make_exec(env_extra={"CHV_HOST_KEY_MODE": "provisioned"})
|
||||
|
||||
Reference in new issue
Block a user