tstest/tailmac: add NIC hot-swap, disconnected NIC, and screenshot server
Add NIC attachment hot-swap support to Host.app: VZNetworkDevice.attachment is writable at runtime, so --disconnected-nic creates a NIC with no attachment, and --attach-network hot-swaps it to a vnet dgram socket after boot/restore. macOS detects link-up and does DHCP. Refactor TailMacConfigHelper: extract createDgramAttachment() and createDisconnectedNetworkDeviceConfiguration() from the monolithic createSocketNetworkDeviceConfiguration(). Add --screenshot-port flag for headless mode. Host.app serves GET /screenshot as JPEG via a localhost HTTP server, capturing the VZVirtualMachineView via CGWindowListCreateImage. The Go test harness polls these to push live thumbnails to the web dashboard. Also: SIGINT handler in headless mode for clean VM state save. Updates #13038 Change-Id: I42fba0ecd760371b4ec5b26a0557e3dd0ba9ecae Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
5c1738fd56
commit
c0e6ffed0d
@@ -81,7 +81,7 @@ class VMController: NSObject, VZVirtualMachineDelegate {
|
||||
return macPlatform
|
||||
}
|
||||
|
||||
func createVirtualMachine(headless: Bool = false) {
|
||||
func createVirtualMachine(headless: Bool = false, disconnectedNIC: Bool = false) {
|
||||
let virtualMachineConfiguration = VZVirtualMachineConfiguration()
|
||||
|
||||
virtualMachineConfiguration.platform = createMacPlaform()
|
||||
@@ -91,11 +91,14 @@ class VMController: NSObject, VZVirtualMachineDelegate {
|
||||
virtualMachineConfiguration.graphicsDevices = [helper.createGraphicsDeviceConfiguration()]
|
||||
virtualMachineConfiguration.storageDevices = [helper.createBlockDeviceConfiguration()]
|
||||
if headless {
|
||||
// In headless mode, use only the socket-based NIC. This matches
|
||||
// the single-NIC configuration used when creating the base VM.
|
||||
// Using a different NIC count would make saved state restoration
|
||||
// fail silently.
|
||||
virtualMachineConfiguration.networkDevices = [helper.createSocketNetworkDeviceConfiguration()]
|
||||
if disconnectedNIC {
|
||||
// Create a NIC with no attachment. The NIC exists in the hardware
|
||||
// config (so saved state is compatible) but appears disconnected.
|
||||
// Call attachNetwork() after restore to hot-swap the attachment.
|
||||
virtualMachineConfiguration.networkDevices = [helper.createDisconnectedNetworkDeviceConfiguration()]
|
||||
} else {
|
||||
virtualMachineConfiguration.networkDevices = [helper.createSocketNetworkDeviceConfiguration()]
|
||||
}
|
||||
} else {
|
||||
virtualMachineConfiguration.networkDevices = [helper.createNetworkDeviceConfiguration(), helper.createSocketNetworkDeviceConfiguration()]
|
||||
}
|
||||
@@ -117,6 +120,22 @@ class VMController: NSObject, VZVirtualMachineDelegate {
|
||||
virtualMachine.delegate = self
|
||||
}
|
||||
|
||||
/// Hot-swap the NIC attachment on a running VM. The VM must have been
|
||||
/// created with disconnectedNIC=true. After calling this, the guest
|
||||
/// sees the link come up and does DHCP.
|
||||
func attachNetwork(serverSocket: String, clientID: String) {
|
||||
guard let nic = virtualMachine.networkDevices.first else {
|
||||
print("attachNetwork: no network devices")
|
||||
return
|
||||
}
|
||||
guard let attachment = helper.createDgramAttachment(serverSocket: serverSocket, clientID: clientID) else {
|
||||
print("attachNetwork: failed to create attachment")
|
||||
return
|
||||
}
|
||||
nic.attachment = attachment
|
||||
print("attachNetwork: NIC attachment swapped to \(serverSocket)")
|
||||
}
|
||||
|
||||
|
||||
func startVirtualMachine() {
|
||||
virtualMachine.start(completionHandler: { (result) in
|
||||
|
||||
Reference in New Issue
Block a user