remote: implement ReorderVirtualMachines and GetPackageSize
This commit is contained in:
parent
db038df8e2
commit
452f8c2088
|
@ -29,9 +29,10 @@ struct VMDetailsView: View {
|
||||||
#else
|
#else
|
||||||
private let regularScreenSizeClass: Bool = true
|
private let regularScreenSizeClass: Bool = true
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@State private var size: Int64 = 0
|
||||||
|
|
||||||
private var sizeLabel: String {
|
private var sizeLabel: String {
|
||||||
let size = data.computeSize(for: vm)
|
|
||||||
return ByteCountFormatter.string(fromByteCount: size, countStyle: .binary)
|
return ByteCountFormatter.string(fromByteCount: size, countStyle: .binary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +110,11 @@ struct VMDetailsView: View {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
.onAppear {
|
||||||
|
Task {
|
||||||
|
size = await data.computeSize(for: vm)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -549,7 +549,7 @@ struct AlertMessage: Identifiable {
|
||||||
/// Calculate total size of VM and data
|
/// Calculate total size of VM and data
|
||||||
/// - Parameter vm: VM to calculate size
|
/// - Parameter vm: VM to calculate size
|
||||||
/// - Returns: Size in bytes
|
/// - Returns: Size in bytes
|
||||||
func computeSize(for vm: VMData) -> Int64 {
|
func computeSize(for vm: VMData) async -> Int64 {
|
||||||
let path = vm.pathUrl
|
let path = vm.pathUrl
|
||||||
guard let enumerator = fileManager.enumerator(at: path, includingPropertiesForKeys: [.totalFileAllocatedSizeKey]) else {
|
guard let enumerator = fileManager.enumerator(at: path, includingPropertiesForKeys: [.totalFileAllocatedSizeKey]) else {
|
||||||
logger.error("failed to create enumerator for \(path)")
|
logger.error("failed to create enumerator for \(path)")
|
||||||
|
@ -633,7 +633,7 @@ struct AlertMessage: Identifiable {
|
||||||
listSelect(vm: vm)
|
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) {
|
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))
|
let status = copyfile(srcURL.path, dstURL.path, nil, copyfile_flags_t(COPYFILE_ALL | COPYFILE_RECURSIVE | COPYFILE_CLONE | COPYFILE_DATA_SPARSE))
|
||||||
if status < 0 {
|
if status < 0 {
|
||||||
|
@ -999,6 +999,7 @@ enum UTMDataError: Error {
|
||||||
case jitStreamerDecodeFailed
|
case jitStreamerDecodeFailed
|
||||||
case jitStreamerAttachFailed
|
case jitStreamerAttachFailed
|
||||||
case jitStreamerUrlInvalid(String)
|
case jitStreamerUrlInvalid(String)
|
||||||
|
case notImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UTMDataError: LocalizedError {
|
extension UTMDataError: LocalizedError {
|
||||||
|
@ -1028,6 +1029,8 @@ extension UTMDataError: LocalizedError {
|
||||||
return NSLocalizedString("Failed to attach to JitStreamer.", comment: "UTMData")
|
return NSLocalizedString("Failed to attach to JitStreamer.", comment: "UTMData")
|
||||||
case .jitStreamerUrlInvalid(let urlString):
|
case .jitStreamerUrlInvalid(let urlString):
|
||||||
return String.localizedStringWithFormat(NSLocalizedString("Invalid JitStreamer attach URL:\n%@", comment: "UTMData"), 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
|
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
|
#endif
|
||||||
|
|
|
@ -333,10 +333,18 @@ extension UTMRemoteClient {
|
||||||
try await _listVirtualMachines(parameters: .init()).items
|
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 {
|
func getQEMUConfiguration(for id: UUID) async throws -> UTMQemuConfiguration {
|
||||||
try await _getQEMUConfiguration(parameters: .init(id: id)).configuration
|
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 {
|
func getPackageDataFile(for id: UUID, name: String) async throws -> URL {
|
||||||
let fm = FileManager.default
|
let fm = FileManager.default
|
||||||
let cacheUrl = try fm.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
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)
|
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 {
|
private func _getQEMUConfiguration(parameters: M.GetQEMUConfiguration.Request) async throws -> M.GetQEMUConfiguration.Reply {
|
||||||
try await M.GetQEMUConfiguration.send(parameters, to: peer)
|
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 {
|
private func _getPackageDataFile(parameters: M.GetPackageDataFile.Request) async throws -> M.GetPackageDataFile.Reply {
|
||||||
try await M.GetPackageDataFile.send(parameters, to: peer)
|
try await M.GetPackageDataFile.send(parameters, to: peer)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,9 @@ enum UTMRemoteMessageServer: UInt8, MessageID {
|
||||||
static let version = 1
|
static let version = 1
|
||||||
case serverHandshake
|
case serverHandshake
|
||||||
case listVirtualMachines
|
case listVirtualMachines
|
||||||
|
case reorderVirtualMachines
|
||||||
case getQEMUConfiguration
|
case getQEMUConfiguration
|
||||||
case updateQEMUConfiguration
|
case getPackageSize
|
||||||
case getPackageDataFile
|
case getPackageDataFile
|
||||||
case startVirtualMachine
|
case startVirtualMachine
|
||||||
case stopVirtualMachine
|
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 {
|
struct GetQEMUConfiguration: Message {
|
||||||
static let id = UTMRemoteMessageServer.getQEMUConfiguration
|
static let id = UTMRemoteMessageServer.getQEMUConfiguration
|
||||||
|
|
||||||
|
@ -95,16 +107,16 @@ extension UTMRemoteMessageServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UpdateQEMUConfiguration: Message {
|
struct GetPackageSize: Message {
|
||||||
static let id = UTMRemoteMessageServer.updateQEMUConfiguration
|
static let id = UTMRemoteMessageServer.getPackageSize
|
||||||
|
|
||||||
struct Request: Serializable, Codable {
|
struct Request: Serializable, Codable {
|
||||||
let id: UUID
|
let id: UUID
|
||||||
let configuration: UTMQemuConfiguration
|
|
||||||
let files: [String: Data]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Reply: Serializable, Codable {}
|
struct Reply: Serializable, Codable {
|
||||||
|
let size: Int64
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GetPackageDataFile: Message {
|
struct GetPackageDataFile: Message {
|
||||||
|
|
|
@ -608,10 +608,12 @@ extension UTMRemoteServer {
|
||||||
return try await _handshake(parameters: .decode(data)).encode()
|
return try await _handshake(parameters: .decode(data)).encode()
|
||||||
case .listVirtualMachines:
|
case .listVirtualMachines:
|
||||||
return try await _listVirtualMachines(parameters: .decode(data)).encode()
|
return try await _listVirtualMachines(parameters: .decode(data)).encode()
|
||||||
|
case .reorderVirtualMachines:
|
||||||
|
return try await _reorderVirtualMachines(parameters: .decode(data)).encode()
|
||||||
case .getQEMUConfiguration:
|
case .getQEMUConfiguration:
|
||||||
return try await _getQEMUConfiguration(parameters: .decode(data)).encode()
|
return try await _getQEMUConfiguration(parameters: .decode(data)).encode()
|
||||||
case .updateQEMUConfiguration:
|
case .getPackageSize:
|
||||||
return try await _updateQEMUConfiguration(parameters: .decode(data)).encode()
|
return try await _getPackageSize(parameters: .decode(data)).encode()
|
||||||
case .getPackageDataFile:
|
case .getPackageDataFile:
|
||||||
return try await _getPackageDataFile(parameters: .decode(data)).encode()
|
return try await _getPackageDataFile(parameters: .decode(data)).encode()
|
||||||
case .startVirtualMachine:
|
case .startVirtualMachine:
|
||||||
|
@ -679,6 +681,20 @@ extension UTMRemoteServer {
|
||||||
return .init(items: items)
|
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 {
|
private func _getQEMUConfiguration(parameters: M.GetQEMUConfiguration.Request) async throws -> M.GetQEMUConfiguration.Reply {
|
||||||
let vm = try await findVM(withId: parameters.id)
|
let vm = try await findVM(withId: parameters.id)
|
||||||
if let config = await vm.config as? UTMQemuConfiguration {
|
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 {
|
private func _getPackageSize(parameters: M.GetPackageSize.Request) async throws -> M.GetPackageSize.Reply {
|
||||||
return .init()
|
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 {
|
private func _getPackageDataFile(parameters: M.GetPackageDataFile.Request) async throws -> M.GetPackageDataFile.Reply {
|
||||||
|
|
Loading…
Reference in New Issue