remote: implement ReorderVirtualMachines and GetPackageSize

This commit is contained in:
osy 2024-02-15 23:50:00 -08:00
parent db038df8e2
commit 452f8c2088
5 changed files with 124 additions and 14 deletions

View File

@ -30,8 +30,9 @@ struct VMDetailsView: View {
private let regularScreenSizeClass: Bool = true
#endif
@State private var size: Int64 = 0
private var sizeLabel: String {
let size = data.computeSize(for: vm)
return ByteCountFormatter.string(fromByteCount: size, countStyle: .binary)
}
@ -109,6 +110,11 @@ struct VMDetailsView: View {
}
#endif
}
.onAppear {
Task {
size = await data.computeSize(for: vm)
}
}
}
}
}

View File

@ -549,7 +549,7 @@ struct AlertMessage: Identifiable {
/// Calculate total size of VM and data
/// - Parameter vm: VM to calculate size
/// - Returns: Size in bytes
func computeSize(for vm: VMData) -> Int64 {
func computeSize(for vm: VMData) async -> Int64 {
let path = vm.pathUrl
guard let enumerator = fileManager.enumerator(at: path, includingPropertiesForKeys: [.totalFileAllocatedSizeKey]) else {
logger.error("failed to create enumerator for \(path)")
@ -633,7 +633,7 @@ struct AlertMessage: Identifiable {
listSelect(vm: vm)
}
func copyItemWithCopyfile(at srcURL: URL, to dstURL: URL) async throws {
private func copyItemWithCopyfile(at srcURL: URL, to dstURL: URL) async throws {
try await Task.detached(priority: .userInitiated) {
let status = copyfile(srcURL.path, dstURL.path, nil, copyfile_flags_t(COPYFILE_ALL | COPYFILE_RECURSIVE | COPYFILE_CLONE | COPYFILE_DATA_SPARSE))
if status < 0 {
@ -999,6 +999,7 @@ enum UTMDataError: Error {
case jitStreamerDecodeFailed
case jitStreamerAttachFailed
case jitStreamerUrlInvalid(String)
case notImplemented
}
extension UTMDataError: LocalizedError {
@ -1028,6 +1029,8 @@ extension UTMDataError: LocalizedError {
return NSLocalizedString("Failed to attach to JitStreamer.", comment: "UTMData")
case .jitStreamerUrlInvalid(let urlString):
return String.localizedStringWithFormat(NSLocalizedString("Invalid JitStreamer attach URL:\n%@", comment: "UTMData"), urlString)
case .notImplemented:
return NSLocalizedString("This functionality is not yet implemented.", comment: "UTMData")
}
}
}
@ -1122,5 +1125,59 @@ class UTMRemoteData: UTMData {
session.fatalError = message
}
}
override func listMove(fromOffsets: IndexSet, toOffset: Int) {
let ids = fromOffsets.map({ virtualMachines[$0].id })
Task {
try await remoteClient.server.reorderVirtualMachines(fromIds: ids, toOffset: toOffset)
}
super.listMove(fromOffsets: fromOffsets, toOffset: toOffset)
}
override func save(vm: VMData) async throws {
throw UTMDataError.notImplemented
}
override func discardChanges(for vm: VMData) throws {
throw UTMDataError.notImplemented
}
override func create<Config: UTMConfiguration>(config: Config) async throws -> VMData {
throw UTMDataError.notImplemented
}
@discardableResult
override func delete(vm: VMData, alsoRegistry: Bool) async throws -> Int? {
throw UTMDataError.notImplemented
}
@discardableResult
override func clone(vm: VMData) async throws -> VMData {
throw UTMDataError.notImplemented
}
override func export(vm: VMData, to url: URL) async throws {
throw UTMDataError.notImplemented
}
override func move(vm: VMData, to url: URL) async throws {
throw UTMDataError.notImplemented
}
override func template(vm: VMData) async throws {
throw UTMDataError.notImplemented
}
override func computeSize(for vm: VMData) async -> Int64 {
(try? await remoteClient.server.getPackageSize(for: vm.id)) ?? 0
}
override func importUTM(from url: URL, asShortcut: Bool) async throws {
throw UTMDataError.notImplemented
}
override func mountSupportTools(for vm: any UTMVirtualMachine) async throws {
throw UTMDataError.notImplemented
}
}
#endif

View File

@ -333,10 +333,18 @@ extension UTMRemoteClient {
try await _listVirtualMachines(parameters: .init()).items
}
func reorderVirtualMachines(fromIds ids: [UUID], toOffset offset: Int) async throws {
try await _reorderVirtualMachines(parameters: .init(ids: ids, offset: offset))
}
func getQEMUConfiguration(for id: UUID) async throws -> UTMQemuConfiguration {
try await _getQEMUConfiguration(parameters: .init(id: id)).configuration
}
func getPackageSize(for id: UUID) async throws -> Int64 {
try await _getPackageSize(parameters: .init(id: id)).size
}
func getPackageDataFile(for id: UUID, name: String) async throws -> URL {
let fm = FileManager.default
let cacheUrl = try fm.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
@ -400,10 +408,19 @@ extension UTMRemoteClient {
try await M.ListVirtualMachines.send(parameters, to: peer)
}
@discardableResult
private func _reorderVirtualMachines(parameters: M.ReorderVirtualMachines.Request) async throws -> M.ReorderVirtualMachines.Reply {
try await M.ReorderVirtualMachines.send(parameters, to: peer)
}
private func _getQEMUConfiguration(parameters: M.GetQEMUConfiguration.Request) async throws -> M.GetQEMUConfiguration.Reply {
try await M.GetQEMUConfiguration.send(parameters, to: peer)
}
private func _getPackageSize(parameters: M.GetPackageSize.Request) async throws -> M.GetPackageSize.Reply {
try await M.GetPackageSize.send(parameters, to: peer)
}
private func _getPackageDataFile(parameters: M.GetPackageDataFile.Request) async throws -> M.GetPackageDataFile.Reply {
try await M.GetPackageDataFile.send(parameters, to: peer)
}

View File

@ -21,8 +21,9 @@ enum UTMRemoteMessageServer: UInt8, MessageID {
static let version = 1
case serverHandshake
case listVirtualMachines
case reorderVirtualMachines
case getQEMUConfiguration
case updateQEMUConfiguration
case getPackageSize
case getPackageDataFile
case startVirtualMachine
case stopVirtualMachine
@ -83,6 +84,17 @@ extension UTMRemoteMessageServer {
}
}
struct ReorderVirtualMachines: Message {
static let id = UTMRemoteMessageServer.reorderVirtualMachines
struct Request: Serializable, Codable {
let ids: [UUID]
let offset: Int
}
struct Reply: Serializable, Codable {}
}
struct GetQEMUConfiguration: Message {
static let id = UTMRemoteMessageServer.getQEMUConfiguration
@ -95,16 +107,16 @@ extension UTMRemoteMessageServer {
}
}
struct UpdateQEMUConfiguration: Message {
static let id = UTMRemoteMessageServer.updateQEMUConfiguration
struct GetPackageSize: Message {
static let id = UTMRemoteMessageServer.getPackageSize
struct Request: Serializable, Codable {
let id: UUID
let configuration: UTMQemuConfiguration
let files: [String: Data]
}
struct Reply: Serializable, Codable {}
struct Reply: Serializable, Codable {
let size: Int64
}
}
struct GetPackageDataFile: Message {

View File

@ -608,10 +608,12 @@ extension UTMRemoteServer {
return try await _handshake(parameters: .decode(data)).encode()
case .listVirtualMachines:
return try await _listVirtualMachines(parameters: .decode(data)).encode()
case .reorderVirtualMachines:
return try await _reorderVirtualMachines(parameters: .decode(data)).encode()
case .getQEMUConfiguration:
return try await _getQEMUConfiguration(parameters: .decode(data)).encode()
case .updateQEMUConfiguration:
return try await _updateQEMUConfiguration(parameters: .decode(data)).encode()
case .getPackageSize:
return try await _getPackageSize(parameters: .decode(data)).encode()
case .getPackageDataFile:
return try await _getPackageDataFile(parameters: .decode(data)).encode()
case .startVirtualMachine:
@ -679,6 +681,20 @@ extension UTMRemoteServer {
return .init(items: items)
}
private func _reorderVirtualMachines(parameters: M.ReorderVirtualMachines.Request) async throws -> M.ReorderVirtualMachines.Reply {
await Task { @MainActor in
let vms = data.virtualMachines
let source = parameters.ids.reduce(into: IndexSet(), { indexSet, id in
if let index = vms.firstIndex(where: { $0.id == id }) {
indexSet.insert(index)
}
})
let destination = min(max(0, parameters.offset), vms.count)
data.listMove(fromOffsets: source, toOffset: destination)
return .init()
}.value
}
private func _getQEMUConfiguration(parameters: M.GetQEMUConfiguration.Request) async throws -> M.GetQEMUConfiguration.Reply {
let vm = try await findVM(withId: parameters.id)
if let config = await vm.config as? UTMQemuConfiguration {
@ -688,8 +704,10 @@ extension UTMRemoteServer {
}
}
private func _updateQEMUConfiguration(parameters: M.UpdateQEMUConfiguration.Request) async throws -> M.UpdateQEMUConfiguration.Reply {
return .init()
private func _getPackageSize(parameters: M.GetPackageSize.Request) async throws -> M.GetPackageSize.Reply {
let vm = try await findVM(withId: parameters.id)
let size = await data.computeSize(for: vm)
return .init(size: size)
}
private func _getPackageDataFile(parameters: M.GetPackageDataFile.Request) async throws -> M.GetPackageDataFile.Reply {