/// <summary> /// <para> /// Adds a new disk to a virtual machine. /// </para> /// <note> /// The virtual machine must be stopped. /// </note> /// </summary> /// <param name="virtualMachine">The target virtual machine.</param> /// <param name="disk">The disk information.</param> public void AddDisk(XenVirtualMachine virtualMachine, XenVirtualDisk disk) { Covenant.Requires <ArgumentNullException>(virtualMachine != null, nameof(virtualMachine)); Covenant.Requires <ArgumentNullException>(disk != null, nameof(disk)); var vmDisks = client.SafeInvokeItems("vm-disk-list", $"vm={virtualMachine.Uuid}").Items; var diskIndex = vmDisks.Count(disk => disk.TryGetValue("userdevice", out var device)); // Count only VDB (virtual block devices) var extraSR = client.Repository.Find(name: disk.StorageRepository, mustExist: true); // Create the disk. client.SafeInvoke("vm-disk-add", $"uuid={virtualMachine.Uuid}", $"sr-uuid={extraSR.Uuid}", $"disk-size={disk.Size}", $"device={diskIndex}"); // Set the disk's name and description. vmDisks = client.SafeInvokeItems("vm-disk-list", $"vm={virtualMachine.Uuid}").Items; var vdi = vmDisks.FirstOrDefault(properties => properties.ContainsKey("name-label") && properties["name-label"] == "Created by xe"); if (vdi == null) { throw new XenException($"Cannot locate the new node [{disk.Name}] disk."); } var vdiUuid = vdi["uuid"]; var diskName = disk.Name ?? "disk"; var diskDescription = disk.Description ?? string.Empty; client.SafeInvoke("vdi-param-set", $"uuid={vdiUuid}", $"name-label={diskName}", $"name-description={diskDescription}"); }
/// <summary> /// Starts a stopped virtual machine resumes a suspended or paused virtual machine. /// This does nothing when the virtual machine is already running. /// </summary> /// <param name="virtualMachine">The target virtual machine.</param> /// <exception cref="XenException">Thrown if the operation failed.</exception> public void Start(XenVirtualMachine virtualMachine) { Covenant.Requires <ArgumentNullException>(virtualMachine != null, nameof(virtualMachine)); var vm = Find(uuid: virtualMachine.Uuid); if (vm == null) { throw new XenException($"Virtual machine not found: {virtualMachine.NameLabel}"); } switch (vm.PowerState) { case XenVmPowerState.Running: return; case XenVmPowerState.Halted: client.SafeInvoke("vm-start", $"uuid={virtualMachine.Uuid}"); break; case XenVmPowerState.Paused: client.SafeInvoke("vm-resume", $"uuid={virtualMachine.Uuid}"); break; default: case XenVmPowerState.Unknown: throw new XenException($"Unexpected virtual machine power state: {virtualMachine.NameLabel}:{vm.PowerState}"); } }
/// <summary> /// Returns the number of disks attached to a virtual machine. /// </summary> /// <param name="virtualMachine">The target virtual machine.</param> /// <returns>The number of attached disks.</returns> public int DiskCount(XenVirtualMachine virtualMachine) { Covenant.Requires <ArgumentNullException>(virtualMachine != null, nameof(virtualMachine)); var vmDisks = client.SafeInvokeItems("vm-disk-list", $"vm={virtualMachine.Uuid}").Items; // Count only VDB (virtual block devices) so we don't double count any disks. return(vmDisks.Count(disk => disk.TryGetValue("userdevice", out var device))); }
/// <summary> /// Reboots a virtual machine. /// </summary> /// <param name="virtualMachine">The target virtual machine.</param> /// <param name="force">Optionally forces the virtual machine to reboot.</param> /// <exception cref="XenException">Thrown if the operation failed.</exception> public void Reboot(XenVirtualMachine virtualMachine, bool force = false) { Covenant.Requires <ArgumentNullException>(virtualMachine != null, nameof(virtualMachine)); if (force) { client.SafeInvoke("vm-reboot", $"uuid={virtualMachine.Uuid}", "--force"); } else { client.SafeInvoke("vm-reboot", $"uuid={virtualMachine.Uuid}"); } }
/// <summary> /// Shuts down a virtual machine. /// </summary> /// <param name="virtualMachine">The target virtual machine.</param> /// <param name="turnOff"> /// <para> /// Optionally just turns the VM off without performing a graceful shutdown first. /// </para> /// <note> /// <b>WARNING!</b> This could result in the loss of unsaved data. /// </note> /// </param> /// <exception cref="XenException">Thrown if the operation failed.</exception> public void Shutdown(XenVirtualMachine virtualMachine, bool turnOff = false) { Covenant.Requires <ArgumentNullException>(virtualMachine != null, nameof(virtualMachine)); if (turnOff) { client.SafeInvoke("vm-shutdown", $"uuid={virtualMachine.Uuid}", "--force"); } else { client.SafeInvoke("vm-shutdown", $"uuid={virtualMachine.Uuid}"); } }
/// <summary> /// Removes a virtual machine and its drives. /// </summary> /// <param name="virtualMachine">The target virtual machine.</param> /// <param name="keepDrives">Optionally retains the VM disks.</param> public void Remove(XenVirtualMachine virtualMachine, bool keepDrives = false) { Covenant.Requires <ArgumentNullException>(virtualMachine != null, nameof(virtualMachine)); // Power down the VM and the remove it. client.SafeInvoke("vm-reset-powerstate", $"uuid={virtualMachine.Uuid}", "--force"); if (keepDrives) { client.SafeInvoke("vm-destroy", $"uuid={virtualMachine.Uuid}", "--force"); } else { client.SafeInvoke("vm-uninstall", $"uuid={virtualMachine.Uuid}", "--force"); } }
/// <summary> /// Suspends a virtual machine by persisting its memory to disk and stopping /// the virtual machine so that it can be restarted where it left off later. /// </summary> /// <param name="virtualMachine">The target virtual machine.</param> /// <exception cref="XenException">Thrown if the operation failed.</exception> public void Suspend(XenVirtualMachine virtualMachine) { Covenant.Requires <ArgumentNullException>(virtualMachine != null, nameof(virtualMachine)); client.SafeInvoke("vm-suspend", $"uuid={virtualMachine.Uuid}", "--force"); }