[WIP] finish implementing cross pve-host disk transfers and mounting
This commit is contained in:
parent
9eddb96402
commit
0545c8337b
@ -44,6 +44,8 @@ export class Volumes extends Controller {
|
|||||||
mountpoint = rawMountpoint
|
mountpoint = rawMountpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('Mount options', this.request.input('options'))
|
||||||
|
|
||||||
vol = await this.provisioner.mountVolume(vol, mountpoint)
|
vol = await this.provisioner.mountVolume(vol, mountpoint)
|
||||||
return vol.toAPI()
|
return vol.toAPI()
|
||||||
}
|
}
|
||||||
@ -54,7 +56,24 @@ export class Volumes extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async transfer(vol: Volume, toNode: Node) {
|
public async transfer(vol: Volume, toNode: Node) {
|
||||||
|
const fromNode = await vol.getNode()
|
||||||
|
|
||||||
|
if ( fromNode.pveId === toNode.pveId ) {
|
||||||
|
// The volume is already attached to the target node, so we're done
|
||||||
|
} else if ( fromNode.pveHost === toNode.pveHost ) {
|
||||||
|
// If the from/to nodes are on the same physical host, just transfer the volume directly
|
||||||
vol = await this.provisioner.transferVolume(vol, toNode)
|
vol = await this.provisioner.transferVolume(vol, toNode)
|
||||||
|
} else {
|
||||||
|
// If the nodes are on different physical hosts, we need to create a temporary container
|
||||||
|
// on shared storage to attach the volume to. We'll then migrate that container to the
|
||||||
|
// target physical host.
|
||||||
|
let carrier = await this.provisioner.provisionCarrierContainer(fromNode)
|
||||||
|
vol = await this.provisioner.transferVolume(vol, carrier)
|
||||||
|
carrier = await this.provisioner.migrateNode(carrier, toNode.pveHost)
|
||||||
|
vol = await this.provisioner.transferVolume(vol, toNode)
|
||||||
|
await this.provisioner.unprovisionCarrierContainer(carrier)
|
||||||
|
}
|
||||||
|
|
||||||
return vol.toAPI()
|
return vol.toAPI()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ export class ConfigLines extends Collection<string> {
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class Node extends Model<Node> {
|
export class Node extends Model<Node> {
|
||||||
protected static table = 'p5x_nodes'
|
protected static table = 'p5x_nodes'
|
||||||
protected static key = 'node_id'
|
protected static key = 'id'
|
||||||
|
|
||||||
public static async getMaster(): Promise<Node> {
|
public static async getMaster(): Promise<Node> {
|
||||||
const master = await Node.query<Node>()
|
const master = await Node.query<Node>()
|
||||||
|
@ -208,14 +208,14 @@ export class Provisioner {
|
|||||||
return volume
|
return volume
|
||||||
}
|
}
|
||||||
|
|
||||||
public async provisionCarrierContainer(node: Node): Promise<string> {
|
public async provisionCarrierContainer(node: Node): Promise<Node> {
|
||||||
// Get the Proxmox API!
|
// Get the Proxmox API!
|
||||||
const proxmox = await this.getApi()
|
const proxmox = await this.getApi()
|
||||||
const nodeName = node.pveHost.split('/')[1]
|
const nodeName = node.pveHost.split('/')[1]
|
||||||
const pveHost = proxmox.nodes.$(nodeName)
|
const pveHost = proxmox.nodes.$(nodeName)
|
||||||
|
|
||||||
// Ensure the empty filesystem template exists
|
// Ensure the empty filesystem template exists
|
||||||
const host = await node.getHost() // fixme this is wrong -- need pve host
|
const host = await this.getPVEHost(node.unqualifiedPVEHost())
|
||||||
const fs = await host.getFilesystem()
|
const fs = await host.getFilesystem()
|
||||||
const stat = await fs.stat({ storePath: '/var/lib/vz/template/cache/p5x-empty.tar.xz' })
|
const stat = await fs.stat({ storePath: '/var/lib/vz/template/cache/p5x-empty.tar.xz' })
|
||||||
if ( !stat.exists ) {
|
if ( !stat.exists ) {
|
||||||
@ -239,7 +239,7 @@ export class Provisioner {
|
|||||||
cores: 1,
|
cores: 1,
|
||||||
description: 'Temporary container managed by P5x',
|
description: 'Temporary container managed by P5x',
|
||||||
hostname: name,
|
hostname: name,
|
||||||
memory: 10, // in MB
|
memory: 16, // in MB
|
||||||
start: false,
|
start: false,
|
||||||
storage: await Setting.loadOneRequired('pveStoragePool'),
|
storage: await Setting.loadOneRequired('pveStoragePool'),
|
||||||
tags: 'p5x',
|
tags: 'p5x',
|
||||||
@ -249,7 +249,30 @@ export class Provisioner {
|
|||||||
await this.waitForNodeTask(nodeName, createUPID)
|
await this.waitForNodeTask(nodeName, createUPID)
|
||||||
|
|
||||||
this.logging.info(`Created carrier container: ${name}`)
|
this.logging.info(`Created carrier container: ${name}`)
|
||||||
return name
|
const carrierNode = this.container.makeNew<Node>(Node)
|
||||||
|
carrierNode.pveId = carrierVMID
|
||||||
|
carrierNode.hostname = name
|
||||||
|
carrierNode.assignedIp = carrierIP
|
||||||
|
carrierNode.assignedSubnet = ipRange.subnet
|
||||||
|
carrierNode.pveHost = node.pveHost
|
||||||
|
await carrierNode.save()
|
||||||
|
return carrierNode
|
||||||
|
}
|
||||||
|
|
||||||
|
public async unprovisionCarrierContainer(node: Node): Promise<void> {
|
||||||
|
this.logging.info(`Deleting carrier container ${node.pveId}`)
|
||||||
|
const api = await this.getApi()
|
||||||
|
const upid = await api
|
||||||
|
.nodes.$(node.unqualifiedPVEHost())
|
||||||
|
.lxc.$(node.pveId)
|
||||||
|
.$delete({
|
||||||
|
purge: true,
|
||||||
|
'destroy-unreferenced-disks': true,
|
||||||
|
})
|
||||||
|
|
||||||
|
await this.waitForNodeTask(node.unqualifiedPVEHost(), upid)
|
||||||
|
await node.delete()
|
||||||
|
this.logging.success(`Deleted carrier container ${node.pveId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async provisionNode(group: HostGroup): Promise<Node> {
|
public async provisionNode(group: HostGroup): Promise<Node> {
|
||||||
@ -615,6 +638,27 @@ export class Provisioner {
|
|||||||
return vol
|
return vol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async migrateNode(node: Node, qualifiedPveHost: string): Promise<Node> {
|
||||||
|
this.logging.info(`Migrating node ${node.pveId} from ${node.pveHost} to ${qualifiedPveHost}`)
|
||||||
|
const api = await this.getApi()
|
||||||
|
const originalUnqualifiedPveHost = node.unqualifiedPVEHost()
|
||||||
|
|
||||||
|
node.pveHost = qualifiedPveHost
|
||||||
|
const upid = await api
|
||||||
|
.nodes.$(originalUnqualifiedPveHost)
|
||||||
|
.lxc.$(node.pveId)
|
||||||
|
.migrate.$post({
|
||||||
|
target: node.unqualifiedPVEHost(),
|
||||||
|
})
|
||||||
|
|
||||||
|
this.logging.info(`Waiting for migrate task: ${upid}`)
|
||||||
|
await this.waitForNodeTask(originalUnqualifiedPveHost, upid)
|
||||||
|
|
||||||
|
await node.save()
|
||||||
|
this.logging.success(`Migrated node ${node.pveId} from ${node.pveHost} to ${qualifiedPveHost}`)
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
public async getPVEHost(pveHost: string): Promise<Host> {
|
public async getPVEHost(pveHost: string): Promise<Host> {
|
||||||
const api = await this.getApi()
|
const api = await this.getApi()
|
||||||
const ifaces = await api.nodes.$(pveHost).network.$get()
|
const ifaces = await api.nodes.$(pveHost).network.$get()
|
||||||
|
Loading…
Reference in New Issue
Block a user