2017-01-16 20:59:50 +01:00
|
|
|
|
/* Copyright (c) Citrix Systems, Inc.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Redistribution and use in source and binary forms,
|
|
|
|
|
* with or without modification, are permitted provided
|
|
|
|
|
* that the following conditions are met:
|
|
|
|
|
*
|
|
|
|
|
* * Redistributions of source code must retain the above
|
|
|
|
|
* copyright notice, this list of conditions and the
|
|
|
|
|
* following disclaimer.
|
|
|
|
|
* * Redistributions in binary form must reproduce the above
|
|
|
|
|
* copyright notice, this list of conditions and the
|
|
|
|
|
* following disclaimer in the documentation and/or other
|
|
|
|
|
* materials provided with the distribution.
|
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
|
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
|
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
|
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
|
|
|
|
using DiscUtils;
|
2020-08-27 18:35:53 +02:00
|
|
|
|
using XenAdmin;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using XenAdmin.Core;
|
2020-08-31 20:04:55 +02:00
|
|
|
|
using XenAdmin.Network;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using XenAPI;
|
|
|
|
|
using XenOvf;
|
|
|
|
|
using XenOvf.Definitions;
|
|
|
|
|
using XenOvf.Definitions.XENC;
|
2013-09-25 11:28:38 +02:00
|
|
|
|
using XenOvf.Utilities;
|
|
|
|
|
|
2020-08-27 18:35:53 +02:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
namespace XenOvfTransport
|
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
public class Import
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2014-01-06 13:31:19 +01:00
|
|
|
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
2013-09-25 11:28:38 +02:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
#region PUBLIC
|
|
|
|
|
|
2020-08-31 22:46:02 +02:00
|
|
|
|
public static void Process(IXenConnection connection, EnvelopeType ovfObj, string pathToOvf, Action<string> OnUpdate, string passCode = null, string applianceName = null, bool metaDataOnly = false)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
var xenSession = connection.Session;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (xenSession == null)
|
|
|
|
|
throw new InvalidOperationException(Messages.ERROR_NOT_CONNECTED);
|
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
int vifDeviceIndex = 0;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
#region CHECK ENCRYPTION
|
2020-08-31 20:04:55 +02:00
|
|
|
|
string encryptionClass = null;
|
|
|
|
|
string encryptionVersion = null;
|
|
|
|
|
int encryptionKeySize;
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (OVF.HasEncryption(ovfObj))
|
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
if (passCode == null)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
throw new InvalidDataException(Messages.ERROR_NO_PASSWORD);
|
|
|
|
|
}
|
|
|
|
|
string fileuuids = null;
|
|
|
|
|
SecuritySection_Type[] securitysection = OVF.FindSections<SecuritySection_Type>((ovfObj).Sections);
|
2013-09-25 12:10:28 +02:00
|
|
|
|
if (securitysection != null && securitysection.Length >= 0)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
foreach (Security_Type securitytype in securitysection[0].Security)
|
|
|
|
|
{
|
|
|
|
|
if (securitytype.ReferenceList.Items != null && securitytype.ReferenceList.Items.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
foreach (XenOvf.Definitions.XENC.ReferenceType dataref in securitytype.ReferenceList.Items)
|
|
|
|
|
{
|
|
|
|
|
if (dataref is DataReference)
|
|
|
|
|
{
|
|
|
|
|
fileuuids += ":" + ((DataReference)dataref).ValueType;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (securitytype.EncryptionMethod != null && securitytype.EncryptionMethod.Algorithm != null)
|
|
|
|
|
{
|
2018-02-22 13:37:55 +01:00
|
|
|
|
string algoname = (securitytype.EncryptionMethod.Algorithm.Split('#'))[1].ToLower().Replace('-', '_');
|
2013-06-24 13:41:48 +02:00
|
|
|
|
object x = OVF.AlgorithmMap(algoname);
|
|
|
|
|
if (x != null)
|
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
encryptionClass = (string)x;
|
|
|
|
|
encryptionKeySize = Convert.ToInt32(securitytype.EncryptionMethod.KeySize);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(securitytype.version))
|
|
|
|
|
{
|
|
|
|
|
encryptionVersion = securitytype.version;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
2020-08-31 18:32:31 +02:00
|
|
|
|
//Normalise the process
|
|
|
|
|
if (ovfObj.Item is VirtualSystem_Type vstemp)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
ovfObj.Item = new VirtualSystemCollection_Type();
|
|
|
|
|
((VirtualSystemCollection_Type)ovfObj.Item).Content = new Content_Type[] { vstemp };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Create appliance
|
|
|
|
|
|
|
|
|
|
XenRef<VM_appliance> applRef = null;
|
2020-08-31 20:04:55 +02:00
|
|
|
|
if (applianceName != null)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
var vmAppliance = new VM_appliance {name_label = applianceName};
|
2013-06-24 13:41:48 +02:00
|
|
|
|
applRef = VM_appliance.create(xenSession, vmAppliance);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 18:32:31 +02:00
|
|
|
|
StartupSection_TypeItem[] vmStartupSections = null;
|
|
|
|
|
if (ovfObj.Sections != null)
|
|
|
|
|
{
|
|
|
|
|
StartupSection_Type[] startUpArray = OVF.FindSections<StartupSection_Type>(ovfObj.Sections);
|
|
|
|
|
if (startUpArray != null && startUpArray.Length > 0)
|
|
|
|
|
vmStartupSections = startUpArray[0]?.Item;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
#endregion
|
|
|
|
|
|
2020-08-31 18:32:31 +02:00
|
|
|
|
foreach (Content_Type contentType in ((VirtualSystemCollection_Type)ovfObj.Item).Content)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 18:32:31 +02:00
|
|
|
|
if (!(contentType is VirtualSystem_Type vSystem))
|
|
|
|
|
continue;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 18:32:31 +02:00
|
|
|
|
var vmName = OVF.FindSystemName(ovfObj, vSystem.id);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
VirtualHardwareSection_Type vhs = OVF.FindVirtualHardwareSectionByAffinity(ovfObj, vSystem.id, "xen");
|
2020-08-31 18:32:31 +02:00
|
|
|
|
var vmStartupSection = vmStartupSections?.FirstOrDefault(it => it.id == vSystem.id);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 18:32:31 +02:00
|
|
|
|
XenRef<VM> vmRef = CreateVm(xenSession, vmName, vhs, applRef, vmStartupSection);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (vmRef == null)
|
|
|
|
|
{
|
2019-11-08 12:42:18 +01:00
|
|
|
|
log.Error("Failed to create a VM");
|
2019-11-11 04:08:34 +01:00
|
|
|
|
throw new Exception(Messages.ERROR_CREATE_VM_FAILED);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-09-25 11:28:38 +02:00
|
|
|
|
log.DebugFormat("OVF.Import.Process: DefineSystem completed ({0})", VM.get_name_label(xenSession, vmRef));
|
2016-10-27 17:03:16 +02:00
|
|
|
|
|
2013-11-14 13:29:30 +01:00
|
|
|
|
#region Set vgpu
|
|
|
|
|
|
2019-11-04 11:29:09 +01:00
|
|
|
|
List<Tuple<GPU_group, VGPU_type> >vgpus = FindGpuGroupAndVgpuType(xenSession, vhs);
|
|
|
|
|
foreach (var item in vgpus)
|
2013-11-14 13:29:30 +01:00
|
|
|
|
{
|
2019-11-04 11:29:09 +01:00
|
|
|
|
GPU_group gpuGroup = item.Item1;
|
|
|
|
|
VGPU_type vgpuType = item.Item2;
|
|
|
|
|
if (gpuGroup != null)
|
|
|
|
|
{
|
|
|
|
|
var other_config = new Dictionary<string, string>();
|
2013-11-14 13:29:30 +01:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
if (Helpers.FeatureForbidden(connection, Host.RestrictVgpu))
|
2019-11-04 11:29:09 +01:00
|
|
|
|
{
|
2020-08-31 18:32:31 +02:00
|
|
|
|
//If the current licence does not allow vGPU, we create one pass-through vGPU
|
|
|
|
|
//(default vGPU type) for the VM (multiple pass-through vGPUs are not supported yet)
|
|
|
|
|
|
|
|
|
|
log.Debug("The host license does not support vGPU, create one pass-through vGPU for the VM");
|
2019-11-04 11:29:09 +01:00
|
|
|
|
VGPU.create(xenSession, vmRef.opaque_ref, gpuGroup.opaque_ref, "0", other_config);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (vgpuType != null)
|
|
|
|
|
VGPU.create(xenSession, vmRef.opaque_ref, gpuGroup.opaque_ref, "0", other_config, vgpuType.opaque_ref);
|
|
|
|
|
}
|
2013-11-14 13:29:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2017-03-03 19:21:29 +01:00
|
|
|
|
SetDeviceConnections(ovfObj, vhs);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
foreach (RASD_Type rasd in vhs.Item)
|
|
|
|
|
{
|
|
|
|
|
string thisPassCode = null;
|
2020-08-31 20:04:55 +02:00
|
|
|
|
// Check to see if THIS rasd is encrypted, if so, set the passCode.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (OVF.IsThisEncrypted(ovfObj, rasd))
|
2020-08-31 20:04:55 +02:00
|
|
|
|
thisPassCode = passCode;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
if (rasd.ResourceType.Value == 17 || rasd.ResourceType.Value == 19 || rasd.ResourceType.Value == 21)
|
|
|
|
|
{
|
|
|
|
|
bool skip = Tools.ValidateProperty("Caption", rasd) &&
|
|
|
|
|
(
|
|
|
|
|
rasd.Caption.Value.ToUpper().Contains("COM") ||
|
|
|
|
|
rasd.Caption.Value.ToUpper().Contains("FLOPPY") ||
|
|
|
|
|
rasd.Caption.Value.ToUpper().Contains("ISO")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!skip)
|
|
|
|
|
{
|
|
|
|
|
File_Type file = OVF.FindFileReferenceByRASD(ovfObj, rasd);
|
|
|
|
|
if (file == null)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
SetIfDeviceIsBootable(ovfObj, rasd);
|
2020-08-31 20:04:55 +02:00
|
|
|
|
AddResourceSettingData(xenSession, OnUpdate, vmRef, rasd, pathToOvf, OVF.FindRasdFileName(ovfObj, rasd, out string compression), compression, encryptionVersion, thisPassCode, metaDataOnly, encryptionClass, ref vifDeviceIndex);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
AddResourceSettingData(xenSession, OnUpdate, vmRef, rasd, pathToOvf, OVF.FindRasdFileName(ovfObj, rasd, out string compression), compression, encryptionVersion, thisPassCode, metaDataOnly, encryptionClass, ref vifDeviceIndex);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InstallSection_Type[] installSection = OVF.FindSections<InstallSection_Type>(vSystem.Items);
|
|
|
|
|
|
|
|
|
|
if (installSection != null && installSection.Length == 1)
|
|
|
|
|
{
|
2020-08-31 22:46:02 +02:00
|
|
|
|
OnUpdate(Messages.START_POST_INSTALL_INSTRUCTIONS);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
HandleInstallSection(xenSession, vmRef, installSection[0]);
|
|
|
|
|
}
|
|
|
|
|
ShowSystem(xenSession, vmRef);
|
2017-03-03 19:21:29 +01:00
|
|
|
|
|
|
|
|
|
#region PVS Proxy
|
|
|
|
|
var site = FindPvsSite(xenSession, vhs);
|
|
|
|
|
|
|
|
|
|
if (site != null)
|
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
var vm = connection.Resolve(vmRef);
|
2017-03-03 19:21:29 +01:00
|
|
|
|
if (vm != null)
|
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
var vifs = connection.ResolveAll(vm.VIFs);
|
2017-03-03 19:21:29 +01:00
|
|
|
|
var firstVif = vifs.FirstOrDefault(v => v.device.Equals("0"));
|
|
|
|
|
|
|
|
|
|
if (firstVif != null)
|
|
|
|
|
{
|
|
|
|
|
var foundSite = PVS_site.get_by_uuid(xenSession, site.uuid);
|
|
|
|
|
|
|
|
|
|
if (foundSite != null)
|
|
|
|
|
{
|
|
|
|
|
PVS_proxy.create(xenSession, foundSite.opaque_ref, firstVif.opaque_ref);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
if (ex is OperationCanceledException)
|
|
|
|
|
throw;
|
2019-11-08 12:42:18 +01:00
|
|
|
|
log.Error("Import failed", ex);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
throw new Exception(Messages.ERROR_IMPORT_FAILED, ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 22:46:02 +02:00
|
|
|
|
OnUpdate(Messages.COMPLETED_IMPORT);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region PRIVATE
|
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static void HandleInstallSection(Session xenSession, XenRef<VM> vm, InstallSection_Type installsection)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
// Configure for XenServer as requested by OVF.SetRunOnceBootCDROM() with the presence of a post install operation that is specific to XenServer.
|
|
|
|
|
if (installsection.PostInstallOperations != null)
|
|
|
|
|
ConfigureForXenServer(xenSession, vm);
|
|
|
|
|
|
|
|
|
|
// Run the VM for the requested duration if this appliance had its own install section -- one not added to fixup for XenServer.
|
2019-11-11 04:08:34 +01:00
|
|
|
|
if (installsection.Info == null ||
|
|
|
|
|
installsection.Info != null && installsection.Info.Value.CompareTo("ConfigureForXenServer") != 0)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
InstallSectionStartVirtualMachine(xenSession, vm, installsection.initialBootStopDelay);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static void ConfigureForXenServer(Session xenSession, XenRef<VM> vm)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
// Ensure the new VM is down.
|
|
|
|
|
if (VM.get_power_state(xenSession, vm) != vm_power_state.Halted)
|
|
|
|
|
VM.hard_shutdown(xenSession, vm);
|
|
|
|
|
|
|
|
|
|
while (VM.get_power_state(xenSession, vm) != vm_power_state.Halted)
|
|
|
|
|
Thread.Sleep(Properties.Settings.Default.FixupPollTimeAsMs);
|
|
|
|
|
|
|
|
|
|
// Save its original memory configuration.
|
|
|
|
|
long staticMemoryMin = VM.get_memory_static_min(xenSession, vm);
|
|
|
|
|
long staticMemoryMax = VM.get_memory_static_max(xenSession, vm);
|
|
|
|
|
long dynamicMemoryMin = VM.get_memory_dynamic_min(xenSession, vm);
|
|
|
|
|
long dynamicMemoryMax = VM.get_memory_dynamic_max(xenSession, vm);
|
|
|
|
|
|
|
|
|
|
// Minimize the memory capacity for the fixup OS.
|
2020-08-31 18:32:31 +02:00
|
|
|
|
long fixupMemorySize = Properties.Settings.Default.FixupOsMemorySizeAsMB * Util.BINARY_MEGA;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
VM.set_memory_limits(xenSession, vm, fixupMemorySize, fixupMemorySize, fixupMemorySize, fixupMemorySize);
|
|
|
|
|
|
|
|
|
|
// Run the fixup OS on the VM.
|
|
|
|
|
InstallSectionStartVirtualMachine(xenSession, vm, Properties.Settings.Default.FixupDurationAsSeconds);
|
|
|
|
|
|
|
|
|
|
// Restore the original memory configuration.
|
|
|
|
|
VM.set_memory_limits(xenSession, vm, staticMemoryMin, staticMemoryMax, dynamicMemoryMin, dynamicMemoryMax);
|
|
|
|
|
|
|
|
|
|
// Eject the fixupOS CD.
|
|
|
|
|
List<XenRef<VBD>> vbdList = VM.get_VBDs(xenSession, vm);
|
|
|
|
|
foreach (XenRef<VBD> vbd in vbdList)
|
|
|
|
|
{
|
|
|
|
|
if (VBD.get_type(xenSession, vbd) == vbd_type.CD)
|
|
|
|
|
VBD.eject(xenSession, vbd);
|
|
|
|
|
|
|
|
|
|
// Note that the original code did not destroy the VBD representing the CD drive.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Restore the boot order.
|
|
|
|
|
Dictionary<string, string> bootParameters = new Dictionary<string, string>();
|
|
|
|
|
bootParameters.Add("order", "cnd");
|
|
|
|
|
VM.set_HVM_boot_params(xenSession, vm, bootParameters);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static void InstallSectionStartVirtualMachine(Session xenSession, XenRef<VM> vm, int initialBootStopDelayAsSeconds)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2019-11-09 02:26:14 +01:00
|
|
|
|
log.InfoFormat("Running fixup on VM with opaque_ref {0}", vm.opaque_ref);
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
// Start the VM.
|
|
|
|
|
if (VM.get_power_state(xenSession, vm) != vm_power_state.Running)
|
|
|
|
|
VM.start(xenSession, vm, false, true);
|
|
|
|
|
|
|
|
|
|
// Finish early if requested to stop on its own.
|
|
|
|
|
if (initialBootStopDelayAsSeconds == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Wait for it to start.
|
|
|
|
|
while (VM.get_power_state(xenSession, vm) != vm_power_state.Running)
|
|
|
|
|
Thread.Sleep(Properties.Settings.Default.FixupPollTimeAsMs);
|
|
|
|
|
|
|
|
|
|
// Let it run for the requested duration.
|
|
|
|
|
int bootStopDelayAsMs = initialBootStopDelayAsSeconds * 1000;
|
|
|
|
|
int msRunning = 0;
|
|
|
|
|
|
|
|
|
|
while (VM.get_power_state(xenSession, vm) == vm_power_state.Running)
|
|
|
|
|
{
|
|
|
|
|
Thread.Sleep(Properties.Settings.Default.FixupPollTimeAsMs);
|
|
|
|
|
msRunning += Properties.Settings.Default.FixupPollTimeAsMs;
|
|
|
|
|
|
|
|
|
|
if (msRunning > bootStopDelayAsMs)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure it is off.
|
2019-11-09 02:26:14 +01:00
|
|
|
|
if (VM.get_power_state(xenSession, vm) == vm_power_state.Halted)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2013-06-24 13:41:48 +02:00
|
|
|
|
VM.hard_shutdown(xenSession, vm);
|
2019-11-09 02:26:14 +01:00
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
log.InfoFormat("Unable to hard-shutdown VM {0}. Will ignore error: {1}", vm.opaque_ref, e.Message);
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 22:46:02 +02:00
|
|
|
|
private static XenRef<VDI> ImportFile(Session xenSession, Action<string> OnUpdate, string vmname, string pathToOvf, string filename, string compression, string version, string passcode, string sruuid, string description, string vdiuuid, string encryptionClass)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
string sourcefile = filename;
|
|
|
|
|
string encryptfilename = null;
|
|
|
|
|
string uncompressedfilename = null;
|
|
|
|
|
string StartPath = Directory.GetCurrentDirectory();
|
|
|
|
|
Directory.SetCurrentDirectory(pathToOvf);
|
|
|
|
|
Stream dataStream = null;
|
|
|
|
|
long dataCapacity = 0;
|
2020-08-31 20:04:55 +02:00
|
|
|
|
VirtualDisk vhdDisk = null;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
#region SET UP TRANSPORT
|
|
|
|
|
if (filename != null)
|
|
|
|
|
{
|
|
|
|
|
if (File.Exists(filename))
|
|
|
|
|
{
|
|
|
|
|
string ext = Path.GetExtension(filename);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
encryptfilename = "enc_" + filename;
|
|
|
|
|
uncompressedfilename = "unc_" + filename;
|
|
|
|
|
// OK.. lets see is the file encrypted?
|
|
|
|
|
#region ENCRYPTION
|
|
|
|
|
if (passcode != null)
|
|
|
|
|
{
|
|
|
|
|
var statusMessage = string.Format(Messages.START_FILE_DECRYPTION, filename);
|
2020-08-31 22:46:02 +02:00
|
|
|
|
OnUpdate(statusMessage);
|
2019-11-08 12:42:18 +01:00
|
|
|
|
log.Debug($"Decrypting {filename}");
|
2020-08-31 20:04:55 +02:00
|
|
|
|
OVF.DecryptToTempFile(encryptionClass, filename, version, passcode, encryptfilename);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
sourcefile = encryptfilename;
|
|
|
|
|
statusMessage += Messages.COMPLETE;
|
2020-08-31 22:46:02 +02:00
|
|
|
|
OnUpdate(statusMessage);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region COMPRESSION
|
|
|
|
|
// Identity == no compression, it is meant when a URL is used to identify the compression during transport.
|
|
|
|
|
if (compression != null &&
|
|
|
|
|
!compression.ToLower().Equals("none") &&
|
|
|
|
|
!compression.ToLower().Equals("identity"))
|
|
|
|
|
{
|
|
|
|
|
// gz is the only understood 'compressed' format, strip it..
|
|
|
|
|
// the OVF is marked with "compressed=gzip" therefor it will get decompress
|
|
|
|
|
// correctly and use with its disk extension (vmdk/vhd/vdi)...
|
|
|
|
|
if (ext.ToLower().EndsWith(".gz"))
|
|
|
|
|
{
|
|
|
|
|
string newfilename = Path.GetFileNameWithoutExtension(uncompressedfilename);
|
|
|
|
|
uncompressedfilename = newfilename;
|
|
|
|
|
ext = Path.GetExtension(uncompressedfilename);
|
|
|
|
|
}
|
|
|
|
|
var statusMessage = string.Format(Messages.START_FILE_EXPANSION, filename);
|
2020-08-31 22:46:02 +02:00
|
|
|
|
OnUpdate(statusMessage);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
var ovfCompressor = new OvfCompressor();
|
|
|
|
|
ovfCompressor.UncompressFile(sourcefile, uncompressedfilename, compression);
|
|
|
|
|
if (File.Exists(encryptfilename)) { File.Delete(encryptfilename); }
|
|
|
|
|
sourcefile = uncompressedfilename;
|
|
|
|
|
statusMessage += Messages.COMPLETE;
|
2020-08-31 22:46:02 +02:00
|
|
|
|
OnUpdate(statusMessage);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region DISK SELECTION
|
|
|
|
|
bool knownDisk = false;
|
|
|
|
|
foreach (string diskext in VirtualDisk.SupportedDiskFormats)
|
|
|
|
|
{
|
|
|
|
|
if (ext.ToLower().Contains(diskext.ToLower()))
|
|
|
|
|
{
|
|
|
|
|
knownDisk = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (knownDisk)
|
|
|
|
|
{
|
2013-09-25 11:28:38 +02:00
|
|
|
|
log.DebugFormat("Found file {0} using {1} Stream", filename, ext);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
vhdDisk = VirtualDisk.OpenDisk(sourcefile, FileAccess.Read);
|
|
|
|
|
dataStream = vhdDisk.Content;
|
|
|
|
|
dataCapacity = vhdDisk.Capacity;
|
|
|
|
|
}
|
|
|
|
|
else if (ext.ToLower().EndsWith("iso"))
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(sruuid))
|
|
|
|
|
{
|
2020-08-27 18:35:53 +02:00
|
|
|
|
log.Info("ImportFile: Upload Skipped");
|
|
|
|
|
return null;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dataStream = File.OpenRead(filename);
|
2020-08-31 18:32:31 +02:00
|
|
|
|
dataCapacity = dataStream.Length;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-27 12:02:40 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
throw new IOException(string.Format(Messages.UNSUPPORTED_FILE_TYPE, ext));
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2019-11-08 12:42:18 +01:00
|
|
|
|
log.Error("Failed to open virtual disk", ex);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
throw new Exception(Messages.ISCSI_ERROR_CANNOT_OPEN_DISK, ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
throw new FileNotFoundException(string.Format(Messages.FILE_MISSING, filename));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-11-08 12:42:18 +01:00
|
|
|
|
log.Error("The file to import was not provided");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
throw new InvalidDataException(Messages.ERROR_FILE_NAME_NULL);
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
#region SEE IF TARGET SR HAS ENOUGH SPACE
|
2020-08-27 18:35:53 +02:00
|
|
|
|
long freespace;
|
|
|
|
|
string contenttype = string.Empty;
|
|
|
|
|
if(vdiuuid != null)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-27 18:35:53 +02:00
|
|
|
|
XenRef<VDI> vdiLookup = VDI.get_by_uuid(xenSession, vdiuuid);
|
|
|
|
|
freespace = VDI.get_virtual_size(xenSession, vdiLookup);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
XenRef<SR> srRef = SR.get_by_uuid(xenSession, sruuid);
|
|
|
|
|
long size = SR.get_physical_size(xenSession, srRef);
|
|
|
|
|
long usage = SR.get_physical_utilisation(xenSession, srRef);
|
|
|
|
|
contenttype = SR.get_content_type(xenSession, srRef);
|
|
|
|
|
freespace = size - usage;
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-27 18:35:53 +02:00
|
|
|
|
if (freespace <= dataCapacity)
|
|
|
|
|
{
|
|
|
|
|
log.Error($"SR {sruuid} does not have {vhdDisk.Capacity} bytes of free space to import virtual disk {filename}.");
|
|
|
|
|
string message = string.Format(Messages.NOT_ENOUGH_SPACE_IN_SR, sruuid, Convert.ToString(vhdDisk.Capacity), filename);
|
|
|
|
|
throw new IOException(message);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region UPLOAD FILE
|
2020-08-27 18:35:53 +02:00
|
|
|
|
XenRef<VDI> vdiRef = null;
|
|
|
|
|
|
|
|
|
|
//If no VDI uuid is provided create a VDI, otherwise use the one provided as
|
|
|
|
|
//the target for the import. Used for SRs such as Lun per VDI (PR-1544)
|
|
|
|
|
if (string.IsNullOrEmpty(vdiuuid))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-27 18:35:53 +02:00
|
|
|
|
vdiRef = CreateVDI(xenSession, sruuid, vmname, dataCapacity, description);
|
|
|
|
|
vdiuuid = VDI.get_uuid(xenSession, vdiRef);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2020-08-27 18:35:53 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
vdiRef = new XenRef<VDI>(VDI.get_by_uuid(xenSession, vdiuuid));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var taskRef = Task.create(xenSession, "import_raw_vdi_task", "import_raw_vdi_task");
|
|
|
|
|
var uriBuilder = new UriBuilder
|
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
Scheme = xenSession.Connection.UriScheme,
|
|
|
|
|
Host = xenSession.Connection.Hostname,
|
|
|
|
|
Port = xenSession.Connection.Port,
|
2020-08-27 18:35:53 +02:00
|
|
|
|
Path = "/import_raw_vdi",
|
|
|
|
|
Query = string.Format("session_id={0}&task_id={1}&vdi={2}",
|
|
|
|
|
xenSession.opaque_ref, taskRef.opaque_ref, vdiuuid)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using (Stream outStream = HTTPHelper.PUT(uriBuilder.Uri, dataStream.Length, true))
|
|
|
|
|
HTTP.CopyStream(dataStream, outStream,
|
2020-08-31 22:46:02 +02:00
|
|
|
|
b => OnUpdate($"Importing disk {filename} ({Util.DiskSizeString(b)} of {Util.DiskSizeString(dataCapacity)} done)"),
|
2020-08-27 18:35:53 +02:00
|
|
|
|
() => XenAdminConfigManager.Provider.ForcedExiting);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
#endregion
|
2020-08-27 18:35:53 +02:00
|
|
|
|
|
|
|
|
|
return vdiRef;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
if (ex is OperationCanceledException)
|
|
|
|
|
throw;
|
|
|
|
|
throw new Exception(Messages.FILE_TRANSPORT_FAILED, ex);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
if (vhdDisk != null)
|
|
|
|
|
{
|
|
|
|
|
vhdDisk.Dispose();
|
|
|
|
|
vhdDisk = null;
|
|
|
|
|
}
|
2018-09-18 16:01:41 +02:00
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (File.Exists(encryptfilename))
|
|
|
|
|
File.Delete(encryptfilename);
|
|
|
|
|
|
|
|
|
|
if (File.Exists(uncompressedfilename))
|
|
|
|
|
File.Delete(uncompressedfilename);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
//ignore errors
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Directory.SetCurrentDirectory(StartPath);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-11 04:08:34 +01:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static XenRef<VDI> CreateVDI(Session xenSession, string sruuid, string label, long capacity, string description)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-02-16 19:21:17 +01:00
|
|
|
|
VDI vdi = new VDI
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-02-16 19:21:17 +01:00
|
|
|
|
name_label = label,
|
|
|
|
|
name_description = description,
|
|
|
|
|
SR = sruuid.ToLower().StartsWith("opaque") ? new XenRef<SR>(sruuid) : SR.get_by_uuid(xenSession, sruuid),
|
|
|
|
|
virtual_size = capacity,
|
|
|
|
|
physical_utilisation = capacity,
|
|
|
|
|
type = vdi_type.user,
|
|
|
|
|
sharable = false,
|
|
|
|
|
read_only = false,
|
|
|
|
|
storage_lock = false,
|
|
|
|
|
managed = true,
|
|
|
|
|
is_a_snapshot = false
|
|
|
|
|
};
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Note that XenServer will round the capacity up to the nearest multiple of a 2 MB block.
|
2019-11-11 04:08:34 +01:00
|
|
|
|
var vdiRef = VDI.create(xenSession, vdi);
|
|
|
|
|
log.Debug("Import.CeateVDI::VDI Created");
|
|
|
|
|
return vdiRef;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2019-11-08 12:42:18 +01:00
|
|
|
|
log.Error("Failed to create VDI. ", ex);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
throw new Exception(Messages.ERROR_CANNOT_CREATE_VDI, ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static XenRef<VM> CreateVm(Session xenSession, string vmName, VirtualHardwareSection_Type system, XenRef<VM_appliance> applRef, StartupSection_TypeItem vmStartupSection)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 18:32:31 +02:00
|
|
|
|
string description = system.System?.Description?.Value ?? Messages.DEFAULT_IMPORT_DESCRIPTION;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
#region MEMORY
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
|
|
|
|
ulong memorySize = 0;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
RASD_Type[] rasds = OVF.FindRasdByType(system, 4);
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (rasds != null && rasds.Length > 0)
|
|
|
|
|
{
|
2020-08-31 18:32:31 +02:00
|
|
|
|
//The default memory unit is MB (2^20), however, the RASD may contain a different
|
|
|
|
|
//one with format Bytes*memoryBase^memoryPower (Bytes being a literal string)
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
double memoryPower = 20.0;
|
2020-08-31 18:32:31 +02:00
|
|
|
|
double memoryBase = 2.0;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
foreach (RASD_Type rasd in rasds)
|
|
|
|
|
{
|
|
|
|
|
if (rasd.AllocationUnits.Value.ToLower().StartsWith("bytes"))
|
|
|
|
|
{
|
2018-02-22 13:37:55 +01:00
|
|
|
|
string[] a1 = rasd.AllocationUnits.Value.Split('*', '^');
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (a1.Length == 3)
|
|
|
|
|
{
|
2020-08-31 18:32:31 +02:00
|
|
|
|
memoryBase = Convert.ToDouble(a1[1]);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
memoryPower = Convert.ToDouble(a1[2]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
|
|
|
|
double memoryMultiplier = Math.Pow(memoryBase, memoryPower);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
memorySize += rasd.VirtualQuantity.Value * Convert.ToUInt64(memoryMultiplier);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
|
|
|
|
ulong minimumMemory = 512 * Util.BINARY_MEGA; //default minimum
|
|
|
|
|
|
|
|
|
|
if (memorySize < minimumMemory)
|
|
|
|
|
memorySize = minimumMemory;
|
|
|
|
|
else if (memorySize > long.MaxValue)
|
|
|
|
|
memorySize = long.MaxValue;
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region CPU COUNT
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
|
|
|
|
ulong cpuCount = 0;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
rasds = OVF.FindRasdByType(system, 3);
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (rasds != null && rasds.Length > 0)
|
|
|
|
|
{
|
2020-08-31 18:32:31 +02:00
|
|
|
|
//There may be more than one entries corresponding to CPUs
|
|
|
|
|
//The VirtualQuantity in each one is Cores
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
foreach (RASD_Type rasd in rasds)
|
2020-08-31 18:32:31 +02:00
|
|
|
|
cpuCount += rasd.VirtualQuantity.Value;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 18:32:31 +02:00
|
|
|
|
if (cpuCount < 1) //default minimum
|
|
|
|
|
cpuCount = 1;
|
|
|
|
|
else if (cpuCount > long.MaxValue) //unlikely, but better be safe
|
|
|
|
|
cpuCount = long.MaxValue;
|
|
|
|
|
|
|
|
|
|
#endregion
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2018-02-16 19:29:19 +01:00
|
|
|
|
VM newVm = new VM
|
|
|
|
|
{
|
2020-08-31 18:32:31 +02:00
|
|
|
|
name_label = vmName ?? Messages.UNDEFINED_NAME_LABEL,
|
2018-02-16 19:29:19 +01:00
|
|
|
|
name_description = description,
|
|
|
|
|
user_version = 1,
|
|
|
|
|
is_a_template = false,
|
|
|
|
|
is_a_snapshot = false,
|
2020-08-31 18:32:31 +02:00
|
|
|
|
memory_target = (long)memorySize,
|
|
|
|
|
memory_static_max = (long)memorySize,
|
|
|
|
|
memory_dynamic_max = (long)memorySize,
|
|
|
|
|
memory_dynamic_min = (long)memorySize,
|
|
|
|
|
memory_static_min = (long)memorySize,
|
|
|
|
|
VCPUs_max = (long)cpuCount,
|
|
|
|
|
VCPUs_at_startup = (long)cpuCount,
|
2018-02-16 19:29:19 +01:00
|
|
|
|
actions_after_shutdown = on_normal_exit.destroy,
|
|
|
|
|
actions_after_reboot = on_normal_exit.restart,
|
|
|
|
|
actions_after_crash = on_crash_behaviour.restart,
|
|
|
|
|
HVM_shadow_multiplier = 1.0,
|
2020-08-31 18:32:31 +02:00
|
|
|
|
ha_always_run = false,
|
|
|
|
|
other_config = new Dictionary<string, string> {{"HideFromXenCenter", "true"}}
|
2018-02-16 19:29:19 +01:00
|
|
|
|
};
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 18:32:31 +02:00
|
|
|
|
//Note that the VM has to be created hidden.
|
|
|
|
|
//We'll make it visible in the end, after all the setup is done
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
#region XEN SPECIFIC CONFIGURATION INFORMATION
|
2018-02-16 19:29:19 +01:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (system.VirtualSystemOtherConfigurationData == null || system.VirtualSystemOtherConfigurationData.Length <= 0)
|
|
|
|
|
{
|
|
|
|
|
// DEFAULT should work for all of HVM type or 301
|
2018-02-16 19:29:19 +01:00
|
|
|
|
newVm.HVM_boot_policy = Properties.Settings.Default.xenBootOptions;
|
2018-08-21 08:46:42 +02:00
|
|
|
|
newVm.HVM_boot_params = SplitStringIntoDictionary(Properties.Settings.Default.xenBootParams);
|
2018-08-24 09:12:57 +02:00
|
|
|
|
newVm.platform = MakePlatformHash(Properties.Settings.Default.xenPlatformSetting);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-02-16 19:29:19 +01:00
|
|
|
|
var hashtable = new Hashtable();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
foreach (Xen_ConfigurationSettingData_Type xcsd in system.VirtualSystemOtherConfigurationData)
|
|
|
|
|
{
|
|
|
|
|
string key = xcsd.Name.Replace('-', '_');
|
|
|
|
|
switch (key.ToLower())
|
|
|
|
|
{
|
|
|
|
|
case "hvm_boot_params":
|
2020-08-31 18:32:31 +02:00
|
|
|
|
var xcsdValue = xcsd.Value.Value;
|
|
|
|
|
//In new OVFs the xcsd.Value.Value is a dictionary string like "key1=value1;key2=value2"
|
|
|
|
|
//However, we want to be backwards compatible with old OVFs where it was a plain string
|
|
|
|
|
newVm.HVM_boot_params = xcsdValue.IndexOf('=') > -1
|
|
|
|
|
? SplitStringIntoDictionary(xcsdValue)
|
|
|
|
|
: new Dictionary<string, string> {{"order", xcsdValue}};
|
2018-08-24 09:12:57 +02:00
|
|
|
|
break;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
case "platform":
|
2018-02-16 19:29:19 +01:00
|
|
|
|
newVm.platform = MakePlatformHash(xcsd.Value.Value);
|
|
|
|
|
break;
|
2018-08-29 11:26:26 +02:00
|
|
|
|
case "nvram":
|
|
|
|
|
newVm.NVRAM = SplitStringIntoDictionary(xcsd.Value.Value);
|
|
|
|
|
break;
|
2019-11-04 11:29:09 +01:00
|
|
|
|
case "vgpu":
|
2020-08-31 18:32:31 +02:00
|
|
|
|
//Skip vGPUs here; we'll set them up after the VM is created
|
|
|
|
|
//because we need the VM's opaque_ref for them
|
2019-11-04 11:29:09 +01:00
|
|
|
|
break;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
default:
|
2018-02-16 19:29:19 +01:00
|
|
|
|
hashtable.Add(key, xcsd.Value.Value);
|
|
|
|
|
break;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-16 19:29:19 +01:00
|
|
|
|
newVm.UpdateFrom(hashtable);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2018-02-16 19:29:19 +01:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
#endregion
|
|
|
|
|
|
2020-08-31 18:32:31 +02:00
|
|
|
|
#region Set appliance
|
|
|
|
|
|
|
|
|
|
if (applRef != null)
|
|
|
|
|
newVm.appliance = applRef;
|
|
|
|
|
|
|
|
|
|
if (vmStartupSection != null)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 18:32:31 +02:00
|
|
|
|
newVm.start_delay = vmStartupSection.startDelay;
|
|
|
|
|
newVm.shutdown_delay = vmStartupSection.stopDelay;
|
|
|
|
|
newVm.order = vmStartupSection.order;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region set has_vendor_device
|
|
|
|
|
|
|
|
|
|
if (Helpers.DundeeOrGreater(xenSession.Connection))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 18:32:31 +02:00
|
|
|
|
var data = system.VirtualSystemOtherConfigurationData;
|
|
|
|
|
var datum = data?.FirstOrDefault(s => s.Name == "VM_has_vendor_device");
|
|
|
|
|
if (datum != null)
|
|
|
|
|
{
|
|
|
|
|
if (bool.TryParse(datum.Value.Value, out var hasVendorDevice) && hasVendorDevice)
|
|
|
|
|
newVm.has_vendor_device = hasVendorDevice;
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
return VM.create(xenSession, newVm);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2013-11-14 13:29:30 +01:00
|
|
|
|
|
|
|
|
|
public static Regex VGPU_REGEX = new Regex("^GPU_types={(.*)};VGPU_type_vendor_name=(.*);VGPU_type_model_name=(.*);$");
|
2019-11-11 04:08:34 +01:00
|
|
|
|
|
2017-03-03 19:21:29 +01:00
|
|
|
|
public static Regex PVS_SITE_REGEX = new Regex("^PVS_SITE={uuid=(.*)};$");
|
2013-11-14 13:29:30 +01:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static List<Tuple<GPU_group, VGPU_type>> FindGpuGroupAndVgpuType(Session xenSession, VirtualHardwareSection_Type system)
|
2013-11-14 13:29:30 +01:00
|
|
|
|
{
|
2019-11-04 11:29:09 +01:00
|
|
|
|
List<Tuple<GPU_group, VGPU_type>> vgpus = new List<Tuple<GPU_group, VGPU_type>>();
|
2013-11-14 13:29:30 +01:00
|
|
|
|
|
2013-12-02 13:37:35 +01:00
|
|
|
|
var data = system.VirtualSystemOtherConfigurationData;
|
|
|
|
|
if (data == null)
|
2019-11-04 11:29:09 +01:00
|
|
|
|
return vgpus;
|
2013-12-02 13:37:35 +01:00
|
|
|
|
|
2019-11-04 11:29:09 +01:00
|
|
|
|
var datum = data.Where(s => s.Name == "vgpu");
|
2013-11-14 13:29:30 +01:00
|
|
|
|
|
2019-11-04 11:29:09 +01:00
|
|
|
|
foreach (var item in datum)
|
|
|
|
|
{
|
|
|
|
|
Match m = VGPU_REGEX.Match(item.Value.Value);
|
|
|
|
|
if (!m.Success)
|
|
|
|
|
continue;
|
|
|
|
|
var types = m.Groups[1].Value.Split(';');
|
2013-11-14 13:29:30 +01:00
|
|
|
|
|
2019-11-04 11:29:09 +01:00
|
|
|
|
var gpuGroups = GPU_group.get_all_records(xenSession);
|
|
|
|
|
var gpuKvp = gpuGroups.FirstOrDefault(g =>
|
|
|
|
|
g.Value.supported_VGPU_types.Count > 0 &&
|
|
|
|
|
g.Value.GPU_types.Length == types.Length &&
|
|
|
|
|
g.Value.GPU_types.Intersect(types).Count() == types.Length);
|
2013-11-14 13:29:30 +01:00
|
|
|
|
|
2019-11-04 11:29:09 +01:00
|
|
|
|
if (gpuKvp.Equals(default(KeyValuePair<XenRef<GPU_group>, GPU_group>)))
|
|
|
|
|
continue;
|
2013-11-14 13:29:30 +01:00
|
|
|
|
|
2019-11-04 11:29:09 +01:00
|
|
|
|
var gpuGroup = gpuKvp.Value;
|
|
|
|
|
VGPU_type vgpuType = null;
|
2013-11-14 13:29:30 +01:00
|
|
|
|
|
2019-11-04 11:29:09 +01:00
|
|
|
|
gpuGroup.opaque_ref = gpuKvp.Key.opaque_ref;
|
2013-11-14 13:29:30 +01:00
|
|
|
|
|
2019-11-04 11:29:09 +01:00
|
|
|
|
string vendorName = m.Groups[2].Value;
|
|
|
|
|
string modelName = m.Groups[3].Value;
|
2013-11-14 13:29:30 +01:00
|
|
|
|
|
2019-11-04 11:29:09 +01:00
|
|
|
|
var vgpuTypes = VGPU_type.get_all_records(xenSession);
|
|
|
|
|
var vgpuKey = vgpuTypes.FirstOrDefault(v =>
|
|
|
|
|
v.Value.vendor_name == vendorName && v.Value.model_name == modelName);
|
2013-11-14 13:29:30 +01:00
|
|
|
|
|
2019-11-04 11:29:09 +01:00
|
|
|
|
if (!vgpuKey.Equals(default(KeyValuePair<XenRef<VGPU_type>, VGPU_type>)))
|
|
|
|
|
{
|
|
|
|
|
vgpuType = vgpuKey.Value;
|
|
|
|
|
vgpuType.opaque_ref = vgpuKey.Key.opaque_ref;
|
|
|
|
|
}
|
|
|
|
|
vgpus.Add(new Tuple<GPU_group, VGPU_type>(gpuGroup, vgpuType));
|
|
|
|
|
}
|
|
|
|
|
return vgpus;
|
2013-11-14 13:29:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static PVS_site FindPvsSite(Session xenSession, VirtualHardwareSection_Type system)
|
2017-03-03 19:21:29 +01:00
|
|
|
|
{
|
|
|
|
|
var data = system.VirtualSystemOtherConfigurationData;
|
|
|
|
|
if (data == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
var datum = data.FirstOrDefault(s => s.Name == "pvssite");
|
|
|
|
|
if (datum == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
Match m = PVS_SITE_REGEX.Match(datum.Value.Value);
|
|
|
|
|
if (!m.Success)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
var siteUuid = m.Groups[1].Value;
|
|
|
|
|
|
|
|
|
|
var allSites = PVS_site.get_all_records(xenSession);
|
|
|
|
|
|
|
|
|
|
var site = allSites.Select(kvp => kvp.Value).FirstOrDefault(p => p.uuid == siteUuid);
|
|
|
|
|
|
|
|
|
|
return site;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static void RemoveSystem(Session xenSession, XenRef<VM> vm)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
VM.destroy(xenSession, vm);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2019-11-08 12:42:18 +01:00
|
|
|
|
log.Error("Failed to remove a virtual machine (VM). ", ex);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
throw new Exception(Messages.ERROR_REMOVE_VM_FAILED, ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-21 08:46:42 +02:00
|
|
|
|
|
|
|
|
|
private static Dictionary<string, string> SplitStringIntoDictionary(string inputStr)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-08-29 11:26:26 +02:00
|
|
|
|
/* Comment for the usage o.Split(new[] { '=' }, 2)
|
|
|
|
|
* The second parameter "2" is used to handle the case when the delimiter '=' appears in the content of the string
|
|
|
|
|
*
|
|
|
|
|
* For example, inputStr = "EFI-variables-backend=xapidb;EFI-variabbles-on-boot=reset;EFI-variables=dGVzdA=="
|
|
|
|
|
* Notice there are 2 extra '=' in the last section
|
|
|
|
|
* The expected output of that section should be { 'EFI-variables':'dGVzdA==' }
|
|
|
|
|
* But if we do not the second parameter "2" in the Split function, the actual output will be { 'EFI-variables':'' }
|
|
|
|
|
*/
|
|
|
|
|
return inputStr.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries).Select(o => o.Split(new[] { '=' }, 2))
|
|
|
|
|
.ToDictionary(o => o.FirstOrDefault(), o => o.LastOrDefault());
|
2018-08-21 08:46:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static Dictionary<string, string> MakePlatformHash(string platform)
|
2018-08-21 08:46:42 +02:00
|
|
|
|
{
|
|
|
|
|
var hPlatform = SplitStringIntoDictionary(platform);
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
// Handle the case when NX isn't in the platform string.
|
|
|
|
|
if (!hPlatform.ContainsKey("nx"))
|
|
|
|
|
hPlatform.Add("nx", "true");
|
|
|
|
|
|
|
|
|
|
return hPlatform;
|
|
|
|
|
}
|
2019-11-11 04:08:34 +01:00
|
|
|
|
|
2020-08-31 22:46:02 +02:00
|
|
|
|
private static void AddResourceSettingData(Session xenSession, Action<string> OnUpdate, XenRef<VM> vmRef, RASD_Type rasd, string pathToOvf, string filename, string compression, string version, string passcode, bool metaDataOnly, string encryptionClass, ref int vifDeviceIndex)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
switch (rasd.ResourceType.Value)
|
|
|
|
|
{
|
|
|
|
|
case 3: // Processor: Already set in DefineSystem
|
|
|
|
|
case 4: // Memory: Already set in DefineSystem
|
|
|
|
|
case 5: // Internal Disk Controller of one type or another.
|
|
|
|
|
case 6:
|
|
|
|
|
case 7:
|
|
|
|
|
case 8:
|
|
|
|
|
case 9:
|
|
|
|
|
{
|
|
|
|
|
// For Xen really nothing to do here, does not support the different
|
|
|
|
|
// controller types, therefore we must ensure
|
|
|
|
|
// via positional on controllers.
|
|
|
|
|
// IDE - #1
|
|
|
|
|
// SCSI - #2
|
|
|
|
|
// IDE 0 Disk 0 Goes to Xen: userdevice=0
|
|
|
|
|
// IDE 0 Disk 1 Goes to Xen: userdevice=1
|
|
|
|
|
// IDE 1 Disk 0 Goes to Xen: userdevice=2
|
|
|
|
|
// IDE 1 CDDVD 1 Goes to Xen: userdevice=3
|
|
|
|
|
// SCSI 0 Disk 0 Goes to Xen: userdevice=4
|
|
|
|
|
// SCSI 0 Disk 1 Goes to Xen: userdevice=5
|
|
|
|
|
// and so forth.
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 10: // Network
|
|
|
|
|
{
|
|
|
|
|
XenRef<Network> net = null;
|
|
|
|
|
XenRef<Network> netDefault = null;
|
|
|
|
|
string netuuid = null;
|
|
|
|
|
|
|
|
|
|
#region SELECT NETWORK
|
|
|
|
|
Dictionary<XenRef<Network>, Network> networks = Network.get_all_records(xenSession);
|
|
|
|
|
if (rasd.Connection != null && rasd.Connection.Length > 0)
|
|
|
|
|
{
|
2013-09-25 12:10:28 +02:00
|
|
|
|
if (!string.IsNullOrEmpty(rasd.Connection[0].Value))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
// Ignore the NetworkSection/Network
|
|
|
|
|
// During Network Selection the UUID for Network was set in Connection Field
|
|
|
|
|
// Makes data self contained here.
|
|
|
|
|
|
|
|
|
|
if (rasd.Connection[0].Value.Contains(Properties.Settings.Default.xenNetworkKey) ||
|
|
|
|
|
rasd.Connection[0].Value.Contains(Properties.Settings.Default.xenNetworkUuidKey))
|
|
|
|
|
{
|
2018-02-22 13:37:55 +01:00
|
|
|
|
string[] s = rasd.Connection[0].Value.Split(',');
|
2013-06-24 13:41:48 +02:00
|
|
|
|
for (int i = 0; i < s.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
if (s[i].StartsWith(Properties.Settings.Default.xenNetworkKey) ||
|
|
|
|
|
s[i].StartsWith(Properties.Settings.Default.xenNetworkUuidKey))
|
|
|
|
|
{
|
2018-02-22 13:37:55 +01:00
|
|
|
|
string[] s1 = s[i].Split('=');
|
2013-06-24 13:41:48 +02:00
|
|
|
|
netuuid = s1[1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
foreach (XenRef<Network> netRef in networks.Keys)
|
|
|
|
|
{
|
|
|
|
|
// if its a UUID and we find it... use it..
|
|
|
|
|
if (net == null && netuuid != null &&
|
|
|
|
|
netuuid.Equals(networks[netRef].uuid))
|
|
|
|
|
{
|
|
|
|
|
net = netRef;
|
|
|
|
|
}
|
|
|
|
|
// Ok second is to match it as a NAME_LABEL
|
|
|
|
|
else if (net == null && netuuid != null &&
|
|
|
|
|
networks[netRef].name_label.ToLower().Contains(netuuid))
|
|
|
|
|
{
|
|
|
|
|
net = netRef;
|
|
|
|
|
}
|
|
|
|
|
// hhmm neither... is it a BRIDGE name?
|
|
|
|
|
else if (net == null && netuuid != null &&
|
|
|
|
|
networks[netRef].bridge.ToLower().Contains(netuuid))
|
|
|
|
|
{
|
|
|
|
|
net = netRef;
|
|
|
|
|
}
|
|
|
|
|
// ok find the default.
|
|
|
|
|
if (networks[netRef].bridge.ToLower().Contains(Properties.Settings.Default.xenDefaultNetwork))
|
|
|
|
|
{
|
|
|
|
|
netDefault = netRef;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (net == null)
|
|
|
|
|
{
|
|
|
|
|
net = netDefault;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ATTACH NETWORK TO VM
|
2018-02-16 19:19:17 +01:00
|
|
|
|
|
|
|
|
|
VIF vif = new VIF
|
|
|
|
|
{
|
|
|
|
|
uuid = Guid.NewGuid().ToString(),
|
|
|
|
|
allowed_operations = new List<vif_operations> {vif_operations.attach},
|
|
|
|
|
device = Convert.ToString(vifDeviceIndex++),
|
|
|
|
|
network = net,
|
|
|
|
|
VM = vmRef,
|
|
|
|
|
MTU = 1500,
|
|
|
|
|
locking_mode = vif_locking_mode.network_default
|
|
|
|
|
};
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
// This is MAC address if available use it.
|
|
|
|
|
// needs to be in form: 00:00:00:00:00:00
|
|
|
|
|
if (Tools.ValidateProperty("Address", rasd))
|
|
|
|
|
{
|
|
|
|
|
StringBuilder networkAddress = new StringBuilder();
|
|
|
|
|
if (!rasd.Address.Value.Contains(":"))
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < rasd.Address.Value.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
if ((i > 0) && (i % 2) == 0)
|
|
|
|
|
{
|
|
|
|
|
networkAddress.Append(":");
|
|
|
|
|
}
|
|
|
|
|
networkAddress.Append(rasd.Address.Value[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (networkAddress.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
networkAddress.Append(rasd.Address.Value);
|
|
|
|
|
}
|
2018-02-16 19:19:17 +01:00
|
|
|
|
vif.MAC = networkAddress.ToString();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2018-02-16 19:19:17 +01:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
2017-03-03 19:21:29 +01:00
|
|
|
|
xenSession.Connection.WaitForCache(VIF.create(xenSession, vif));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2019-11-08 12:42:18 +01:00
|
|
|
|
log.Error("Failed to create a virtual network interface (VIF). ", ex);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
throw new Exception(Messages.ERROR_CREATE_VIF_FAILED, ex);
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
2013-09-25 11:28:38 +02:00
|
|
|
|
log.Debug("OVF.Import.AddResourceSettingData: Network Added");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 15: // CD Drive
|
|
|
|
|
case 16: // DVD Drive
|
|
|
|
|
{
|
|
|
|
|
// We always attach as "EMPTY".
|
2019-11-11 04:08:34 +01:00
|
|
|
|
// Currently Xen Server can only have ONE CD, so we must
|
2013-06-24 13:41:48 +02:00
|
|
|
|
// Skip the others.
|
|
|
|
|
// If it's not necessary.. skip it.
|
|
|
|
|
|
|
|
|
|
#region Attach DVD to VM
|
|
|
|
|
bool SkipCD = false;
|
|
|
|
|
List<XenRef<VBD>> vbds = VM.get_VBDs(xenSession, vmRef);
|
|
|
|
|
foreach (XenRef<VBD> vbd in vbds)
|
|
|
|
|
{
|
|
|
|
|
vbd_type vbdType = VBD.get_type(xenSession, vbd);
|
|
|
|
|
if (vbdType == vbd_type.CD)
|
|
|
|
|
{
|
|
|
|
|
SkipCD = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!SkipCD)
|
|
|
|
|
{
|
2019-11-11 04:08:34 +01:00
|
|
|
|
List<XenRef<VDI>> vdiRefs = new List<XenRef<VDI>>();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (filename != null)
|
|
|
|
|
{
|
2019-11-11 04:08:34 +01:00
|
|
|
|
#region FIND THE ISO SR MAPPED IN THE OVF
|
2013-06-24 13:41:48 +02:00
|
|
|
|
string isoUuid = null;
|
|
|
|
|
if (rasd.Connection != null && rasd.Connection.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
if (rasd.Connection[0].Value.ToLower().Contains("sr="))
|
|
|
|
|
{
|
2018-02-22 13:37:55 +01:00
|
|
|
|
string[] vpairs = rasd.Connection[0].Value.Split(',');
|
2013-06-24 13:41:48 +02:00
|
|
|
|
foreach (string vset in vpairs)
|
|
|
|
|
{
|
|
|
|
|
if (vset.ToLower().StartsWith("sr="))
|
|
|
|
|
{
|
2019-11-11 04:08:34 +01:00
|
|
|
|
var srToFind = vset.Substring(vset.LastIndexOf('=') + 1);
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
2019-11-11 04:08:34 +01:00
|
|
|
|
XenRef<SR> srRef = SR.get_by_uuid(xenSession, srToFind);
|
|
|
|
|
if (srRef != null && srRef != Helper.NullOpaqueRef)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2019-11-11 04:08:34 +01:00
|
|
|
|
isoUuid = srToFind;
|
|
|
|
|
break;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2019-11-11 04:08:34 +01:00
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
log.Debug("Import.AddResourceSettingData: iso sr uuid not found, trying name_label");
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-11-11 04:08:34 +01:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var srRecords = SR.get_all_records(xenSession);
|
|
|
|
|
|
|
|
|
|
isoUuid = (from SR sr in srRecords.Values
|
|
|
|
|
where sr.name_label == srToFind
|
|
|
|
|
select sr.uuid).FirstOrDefault();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2019-11-11 04:08:34 +01:00
|
|
|
|
catch
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2019-11-11 04:08:34 +01:00
|
|
|
|
log.Debug("Import.AddResourceSettingData: iso sr uuid not found, looking for vdi...");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2019-11-11 04:08:34 +01:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
// VDI trumps SR
|
|
|
|
|
List<XenRef<VDI>> isoVDIlist = VDI.get_by_name_label(xenSession, filename);
|
|
|
|
|
if (isoVDIlist.Count > 0)
|
|
|
|
|
{
|
2019-11-11 04:08:34 +01:00
|
|
|
|
vdiRefs.Add(isoVDIlist[0]);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
#region LAST CHANCE USE XENTOOLS ISO SR
|
|
|
|
|
if (isoUuid == null)
|
|
|
|
|
{
|
|
|
|
|
Dictionary<XenRef<SR>, SR> srDictionary = SR.get_all_records(xenSession);
|
|
|
|
|
foreach (XenRef<SR> key in srDictionary.Keys)
|
|
|
|
|
{
|
|
|
|
|
if (srDictionary[key].content_type.ToLower() == "iso" && srDictionary[key].type.ToLower() == "iso")
|
|
|
|
|
{
|
|
|
|
|
if (srDictionary[key].name_label.ToLower().Equals(Properties.Settings.Default.xenTools.ToLower()))
|
|
|
|
|
{
|
|
|
|
|
isoUuid = srDictionary[key].uuid;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region DO IMPORT ISO FILE
|
2020-08-31 20:04:55 +02:00
|
|
|
|
if (isoUuid != null && !metaDataOnly)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
var vdiRef = ImportFile(xenSession, OnUpdate, filename, pathToOvf, filename, compression, version, passcode, isoUuid, "", null, encryptionClass);
|
2020-08-27 18:35:53 +02:00
|
|
|
|
if (vdiRef != null)
|
|
|
|
|
vdiRefs.Add(vdiRef);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2018-02-16 19:29:19 +01:00
|
|
|
|
if (ex is OperationCanceledException)
|
|
|
|
|
throw;
|
|
|
|
|
var msg = string.Format(Messages.ERROR_ADDRESOURCESETTINGDATA_FAILED, Messages.ISO);
|
2019-11-08 12:42:18 +01:00
|
|
|
|
log.Error("Failed to add resource ISO.", ex);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
throw new Exception(msg, ex);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
2019-11-11 04:08:34 +01:00
|
|
|
|
if (vdiRefs == null || vdiRefs.Count <= 0)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2019-11-08 12:42:18 +01:00
|
|
|
|
log.ErrorFormat("Failed to import virtual disk from file {0} to storage repository {1}.", filename, isoUuid);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
RemoveSystem(xenSession, vmRef);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-11-11 04:08:34 +01:00
|
|
|
|
vdiRefs.Add(XenRef<VDI>.Create(string.Empty));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region CREATE VBD CONNECTION
|
2018-02-16 19:29:19 +01:00
|
|
|
|
|
2019-11-11 04:08:34 +01:00
|
|
|
|
foreach (XenRef<VDI> currentVDI in vdiRefs)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-02-16 19:29:19 +01:00
|
|
|
|
var hashtable = new Hashtable();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
if (rasd.Connection != null && rasd.Connection.Length > 0)
|
|
|
|
|
{
|
2018-02-16 19:29:19 +01:00
|
|
|
|
string[] valuepairs = rasd.Connection[0].Value.Split(',');
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
foreach (string valuepair in valuepairs)
|
|
|
|
|
{
|
2018-02-16 19:29:19 +01:00
|
|
|
|
string[] namevalue = valuepair.Split('=');
|
|
|
|
|
string name = namevalue[0].ToLower();
|
|
|
|
|
|
|
|
|
|
switch (name)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-02-16 19:29:19 +01:00
|
|
|
|
case "sr":
|
|
|
|
|
case "vdi":
|
|
|
|
|
continue;
|
|
|
|
|
case "device":
|
|
|
|
|
hashtable.Add("userdevice", namevalue[1]);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
hashtable.Add(namevalue[0], namevalue[1]);
|
|
|
|
|
break;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-16 19:29:19 +01:00
|
|
|
|
|
|
|
|
|
VBD vbd = new VBD
|
|
|
|
|
{
|
|
|
|
|
VM = vmRef,
|
|
|
|
|
mode = vbd_mode.RO,
|
|
|
|
|
userdevice = "3",
|
|
|
|
|
type = vbd_type.CD,
|
|
|
|
|
storage_lock = false,
|
|
|
|
|
status_code = 0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
vbd.UpdateFrom(hashtable);
|
|
|
|
|
|
|
|
|
|
if (currentVDI != null && !string.IsNullOrEmpty(currentVDI.opaque_ref))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2018-02-16 19:29:19 +01:00
|
|
|
|
vbd.VDI = currentVDI;
|
|
|
|
|
vbd.empty = false;
|
|
|
|
|
vbd.bootable = true;
|
|
|
|
|
vbd.unpluggable = true;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-02-16 19:29:19 +01:00
|
|
|
|
vbd.empty = true;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-16 19:29:19 +01:00
|
|
|
|
vbd.userdevice = VerifyUserDevice(xenSession, vmRef, vbd.userdevice);
|
|
|
|
|
vbd.other_config = new Dictionary<string, string> { { "owner", "true" } };
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2018-02-16 19:29:19 +01:00
|
|
|
|
if (!vbd.userdevice.EndsWith("+"))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
VBD.create(xenSession, vbd);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2019-11-08 13:53:24 +01:00
|
|
|
|
log.Error("Import.AddResourceSettingData: ", ex);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-11-08 13:09:48 +01:00
|
|
|
|
log.WarnFormat(
|
|
|
|
|
"Import: ================== ATTENTION NEEDED =======================" +
|
|
|
|
|
"Import: Could not determine appropriate number of device placement." +
|
|
|
|
|
"Import: Please Start, Logon, Shut down, System ({0})" +
|
|
|
|
|
"Import: Then attach disks with labels ending with \"+\" to the device number defined before the +." +
|
|
|
|
|
"Import: ===========================================================", vmRef);
|
|
|
|
|
|
2020-08-31 22:46:02 +02:00
|
|
|
|
OnUpdate(Messages.WARNING_ADMIN_REQUIRED);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
2020-08-31 22:46:02 +02:00
|
|
|
|
OnUpdate(string.Format(Messages.DEVICE_ATTACHED, Messages.CD_DVD_DEVICE));
|
2013-09-25 11:28:38 +02:00
|
|
|
|
log.Debug("Import.AddResourceSettingData: CD/DVD ROM Added");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 17: // Disk Drive
|
|
|
|
|
case 19: // Storage Extent
|
2020-08-31 20:04:55 +02:00
|
|
|
|
case 21: // Microsoft: Hard drive/Floppy/ISO
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
#region ADD DISK
|
2020-08-31 20:04:55 +02:00
|
|
|
|
if (filename == null)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
log.Warn($"No file available to import, skipping RASD {rasd.ResourceType.Value}: {rasd.InstanceID.Value}");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Tools.ValidateProperty("Caption", rasd) &&
|
|
|
|
|
(rasd.Caption.Value.ToUpper().Contains("COM") ||
|
|
|
|
|
rasd.Caption.Value.ToUpper().Contains("FLOPPY") ||
|
|
|
|
|
rasd.Caption.Value.ToUpper().Contains("ISO")))
|
|
|
|
|
{
|
|
|
|
|
log.Info($"Resource {filename} is {rasd.Caption.Value}. Skipping import.");
|
|
|
|
|
return;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2020-08-31 20:04:55 +02:00
|
|
|
|
|
|
|
|
|
if (metaDataOnly)
|
|
|
|
|
{
|
|
|
|
|
log.Info($"Importing metadata only. Skipping resource {filename}.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
string sruuid = null;
|
|
|
|
|
string vdiuuid = null;
|
|
|
|
|
string userdeviceid = null;
|
2018-02-16 19:22:55 +01:00
|
|
|
|
string vmName = VM.get_name_label(xenSession, vmRef);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
bool isbootable = false;
|
2018-02-16 19:22:55 +01:00
|
|
|
|
vbd_mode mode = vbd_mode.RW;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
#region IMPORT DISKS
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
List<XenRef<VDI>> vdiRefs = new List<XenRef<VDI>>();
|
|
|
|
|
|
|
|
|
|
#region PARSE CONNECTION
|
|
|
|
|
if (Tools.ValidateProperty("Connection", rasd))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
string[] s = rasd.Connection[0].Value.Split('=', ',');
|
|
|
|
|
for (int i = 0; i < s.Length; i++)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
string checkme = s[i].ToLower().Trim();
|
|
|
|
|
switch (checkme)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
case "device":
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
userdeviceid = s[++i];
|
|
|
|
|
break;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2020-08-31 20:04:55 +02:00
|
|
|
|
case "bootable":
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
isbootable = Convert.ToBoolean(s[++i]);
|
|
|
|
|
break;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2020-08-31 20:04:55 +02:00
|
|
|
|
case "mode":
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
if (s[++i].Equals("r"))
|
|
|
|
|
{
|
|
|
|
|
mode = vbd_mode.RO;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2020-08-31 20:04:55 +02:00
|
|
|
|
case "vdi":
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
vdiuuid = s[++i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case "sr":
|
|
|
|
|
{
|
|
|
|
|
sruuid = s[++i];
|
|
|
|
|
break;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-31 20:04:55 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region VERIFY SR UUID
|
|
|
|
|
if (!string.IsNullOrEmpty(sruuid))
|
|
|
|
|
{
|
|
|
|
|
XenRef<SR> srref = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
srref = SR.get_by_uuid(xenSession, sruuid);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
log.Debug("Import.AddResourceSettingData: SR missing... still looking..");
|
|
|
|
|
}
|
|
|
|
|
if (srref == null)
|
|
|
|
|
{
|
|
|
|
|
List<XenRef<SR>> srlist = null;
|
|
|
|
|
try
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
srlist = SR.get_by_name_label(xenSession, sruuid);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2020-08-31 20:04:55 +02:00
|
|
|
|
catch
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
log.Debug("Import.AddResourceSettingData: SR missing... still looking..");
|
|
|
|
|
}
|
|
|
|
|
if (srlist != null && srlist.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
sruuid = SR.get_uuid(xenSession, srlist[0]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sruuid = null;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
#region LAST CHANGE TO FIND SR
|
|
|
|
|
if (sruuid == null)
|
|
|
|
|
{
|
|
|
|
|
XenRef<SR> DefaultSRUUID = null;
|
|
|
|
|
Dictionary<XenRef<Pool>, Pool> pools = Pool.get_all_records(xenSession);
|
|
|
|
|
foreach (XenRef<Pool> pool in pools.Keys)
|
|
|
|
|
{
|
|
|
|
|
DefaultSRUUID = pools[pool].default_SR;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
if (DefaultSRUUID == null)
|
|
|
|
|
{
|
|
|
|
|
log.Error("The SR was not found and a default was not assigned.");
|
|
|
|
|
throw new InvalidDataException(Messages.ERROR_COULD_NOT_FIND_SR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Dictionary<XenRef<SR>, SR> srDict = SR.get_all_records(xenSession);
|
|
|
|
|
if (vdiuuid != null)
|
|
|
|
|
{
|
|
|
|
|
//Try and get the SR that belongs to the VDI attached
|
|
|
|
|
XenRef<VDI> tempVDI = VDI.get_by_uuid(xenSession, vdiuuid);
|
|
|
|
|
if (tempVDI == null)
|
|
|
|
|
{
|
|
|
|
|
log.Error("The SR was not found and a default was not assigned.");
|
|
|
|
|
throw new InvalidDataException(Messages.ERROR_COULD_NOT_FIND_SR);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
XenRef<SR> tempSR = VDI.get_SR(xenSession, tempVDI.opaque_ref);
|
|
|
|
|
sruuid = srDict[tempSR].uuid;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
sruuid = srDict[DefaultSRUUID].uuid;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
2020-08-27 18:35:53 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
XenRef<VDI> vdiRef = null;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string disklabel = string.Format("{0}_{1}", vmName, userdeviceid);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
if ((rasd.ElementName != null) && (!string.IsNullOrEmpty(rasd.ElementName.Value)))
|
|
|
|
|
disklabel = rasd.ElementName.Value;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
string description = "";
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
if ((rasd.Description != null) && (!string.IsNullOrEmpty(rasd.Description.Value)))
|
|
|
|
|
description = rasd.Description.Value;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
vdiRef = ImportFile(xenSession, OnUpdate, disklabel, pathToOvf, filename, compression, version, passcode, sruuid, description, vdiuuid, encryptionClass);
|
|
|
|
|
if (vdiRef != null)
|
|
|
|
|
vdiRefs.Add(vdiRef);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
if (ex is OperationCanceledException)
|
|
|
|
|
throw;
|
|
|
|
|
var msg = string.Format(Messages.ERROR_ADDRESOURCESETTINGDATA_FAILED, Messages.DISK_DEVICE);
|
|
|
|
|
log.Error("Failed to add resource Hard Disk Image.", ex);
|
|
|
|
|
throw new InvalidDataException(msg, ex);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
if (vdiRef == null)
|
|
|
|
|
{
|
|
|
|
|
log.ErrorFormat("Failed to import virtual disk from file {0} to storage repository {1}.", filename, sruuid);
|
|
|
|
|
RemoveSystem(xenSession, vmRef);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
log.DebugFormat("Import.AddResourceSettingData counts {0} VDIs", vdiRefs.Count);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
foreach (XenRef<VDI> currentVDI in vdiRefs)
|
|
|
|
|
{
|
|
|
|
|
VBD vbd = new VBD
|
|
|
|
|
{
|
|
|
|
|
userdevice = VerifyUserDevice(xenSession, vmRef, userdeviceid ?? "99"),
|
|
|
|
|
bootable = isbootable,
|
|
|
|
|
VDI = currentVDI,
|
|
|
|
|
mode = mode,
|
|
|
|
|
uuid = Guid.NewGuid().ToString(),
|
|
|
|
|
VM = vmRef,
|
|
|
|
|
empty = false,
|
|
|
|
|
type = vbd_type.Disk,
|
|
|
|
|
currently_attached = false,
|
|
|
|
|
storage_lock = false,
|
|
|
|
|
status_code = 0,
|
|
|
|
|
// below other_config keys XS to delete the disk along with the VM.
|
|
|
|
|
other_config = new Dictionary<string, string> {{"owner", "true"}}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!vbd.userdevice.EndsWith("+"))
|
|
|
|
|
{
|
|
|
|
|
try
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
VBD.create(xenSession, vbd);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
log.Error("Failed to create a virtual block device (VBD).", ex);
|
|
|
|
|
throw new Exception(Messages.ERROR_CREATE_VBD_FAILED, ex);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-08-31 20:04:55 +02:00
|
|
|
|
log.WarnFormat(
|
|
|
|
|
"Import: ================== ATTENTION NEEDED =======================" +
|
|
|
|
|
"Import: Could not determine appropriate number for device placement." +
|
|
|
|
|
"Import: Please Start, Logon, Shut down, System ({0})" +
|
|
|
|
|
"Import: Then manually attach disks with labels with {0}_# that are not attached to {0}" +
|
|
|
|
|
"Import: ===========================================================",
|
|
|
|
|
vmName);
|
2020-08-31 22:46:02 +02:00
|
|
|
|
OnUpdate(Messages.WARNING_ADMIN_REQUIRED);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-31 20:04:55 +02:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
break;
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-29 14:15:15 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static string VerifyUserDevice(Session xenSession, XenRef<VM> vmRef, string device)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2013-09-25 11:28:38 +02:00
|
|
|
|
log.DebugFormat("Import.VerifyUserDevice, checking device: {0} (99 = autoselect)", device);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
string usethisdevice = null;
|
2018-02-16 19:22:55 +01:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
string[] allowedVBDs = VM.get_allowed_VBD_devices(xenSession, vmRef);
|
|
|
|
|
|
|
|
|
|
if (allowedVBDs == null || allowedVBDs.Length <= 0)
|
|
|
|
|
{
|
2019-11-11 04:08:34 +01:00
|
|
|
|
log.ErrorFormat("OVF.VerifyUserDevice: No more available devices, cannot add device: {0}", device);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return device + "+";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(device) && !device.StartsWith("99"))
|
|
|
|
|
{
|
|
|
|
|
foreach (string allowedvbd in allowedVBDs)
|
|
|
|
|
{
|
|
|
|
|
if (device.ToLower() == allowedvbd.ToLower())
|
|
|
|
|
{
|
|
|
|
|
usethisdevice = device;
|
2013-09-25 11:28:38 +02:00
|
|
|
|
log.DebugFormat("Import.VerifyUserDevice, device: {0} will be used.", device);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
usethisdevice = allowedVBDs[0];
|
2013-09-25 11:28:38 +02:00
|
|
|
|
log.DebugFormat("Import.VerifyUserDevice, device [{0}] is not available, setting to: [{1}]", device, usethisdevice);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (usethisdevice == null)
|
|
|
|
|
{
|
|
|
|
|
if (!device.EndsWith("+"))
|
|
|
|
|
usethisdevice = device + "+";
|
|
|
|
|
}
|
|
|
|
|
return usethisdevice;
|
|
|
|
|
}
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static void SetDeviceConnections(EnvelopeType ovfEnv, VirtualHardwareSection_Type vhs)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
int[] connections = new int[16];
|
|
|
|
|
int deviceoffset = 0;
|
|
|
|
|
List<RASD_Type> rasdList = new List<RASD_Type>();
|
|
|
|
|
|
|
|
|
|
rasdList.AddRange(vhs.Item);
|
|
|
|
|
rasdList.Sort(compareControllerRasd); // sorts based on ResourceType.Value
|
|
|
|
|
|
|
|
|
|
// For Xen really nothing to do here, does not support the different
|
|
|
|
|
// controller types, therefore we must ensure
|
|
|
|
|
// via positional on controllers.
|
|
|
|
|
// IDE - #1
|
|
|
|
|
// SCSI - #2
|
|
|
|
|
// IDE 0 Disk 0 Goes to Xen: userdevice=0
|
|
|
|
|
// IDE 0 Disk 1 Goes to Xen: userdevice=1
|
|
|
|
|
// IDE 1 CD/DVD 0 Goes to Xen: userdevice=2
|
|
|
|
|
// IDE 1 Disk 1 UnUsed
|
|
|
|
|
// SCSI 0 Disk 0 Goes to Xen: userdevice=3
|
|
|
|
|
// SCSI 0 Disk 1 Goes to Xen: userdevice=4
|
|
|
|
|
// and so forth.
|
|
|
|
|
|
|
|
|
|
foreach (RASD_Type rasd in rasdList)
|
|
|
|
|
{
|
|
|
|
|
switch (rasd.ResourceType.Value)
|
|
|
|
|
{
|
|
|
|
|
case 5: // IDE Controller #1
|
|
|
|
|
case 6: // Parallel SCSI HBA #2
|
|
|
|
|
case 7: // FC HBA #3
|
|
|
|
|
case 8: // iSCSI HBA #4
|
|
|
|
|
case 9: // IB HCA #5
|
|
|
|
|
{
|
|
|
|
|
List<RASD_Type> connectedrasds = FindConnectedItems(rasd.InstanceID.Value, vhs.Item, null);
|
|
|
|
|
foreach (RASD_Type _rasd in connectedrasds)
|
|
|
|
|
{
|
|
|
|
|
//if (_rasd.Connection != null &&
|
|
|
|
|
// _rasd.Connection.Length > 0 &&
|
|
|
|
|
// _rasd.Connection[0] != null &&
|
|
|
|
|
// _rasd.Connection[0].Value != null &&
|
|
|
|
|
// _rasd.Connection[0].Value.Length > 0)
|
|
|
|
|
if (_rasd.ResourceType.Value == 15 || _rasd.ResourceType.Value == 16)
|
|
|
|
|
{
|
|
|
|
|
deviceoffset = 2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
deviceoffset = 0;
|
|
|
|
|
}
|
|
|
|
|
if (Tools.ValidateProperty("Connection", _rasd))
|
|
|
|
|
{
|
|
|
|
|
if (!_rasd.Connection[0].Value.ToLower().Contains("device="))
|
|
|
|
|
{
|
|
|
|
|
_rasd.Connection[0].Value = string.Format("{0},device={1}", _rasd.Connection[0].Value, FindNextAvailable(deviceoffset, connections, 0));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_rasd.Connection = new cimString[] { new cimString(string.Format("device={0}", FindNextAvailable(deviceoffset, connections, 0))) };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static int FindNextAvailable(int offset, int[] ids, int unusedkey)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
int available = 0;
|
|
|
|
|
for (int i = offset; i < ids.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
if (ids[i] == unusedkey)
|
|
|
|
|
{
|
|
|
|
|
ids[i] = 1;
|
|
|
|
|
available = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return available;
|
|
|
|
|
}
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static List<RASD_Type> FindConnectedItems(string instanceId, RASD_Type[] rasds, string value22)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
List<RASD_Type> connectedRasds = new List<RASD_Type>();
|
|
|
|
|
foreach (RASD_Type rasd in rasds)
|
|
|
|
|
{
|
2013-09-25 12:10:28 +02:00
|
|
|
|
if (rasd.Parent != null && !string.IsNullOrEmpty(rasd.Parent.Value) )
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
string parent = rasd.Parent.Value.Replace(@"\", "");
|
|
|
|
|
string instance = instanceId.Replace(@"\", "");
|
|
|
|
|
if (parent.Contains(instance))
|
|
|
|
|
{
|
|
|
|
|
switch (rasd.ResourceType.Value)
|
|
|
|
|
{
|
|
|
|
|
case 15:
|
|
|
|
|
case 16:
|
|
|
|
|
{
|
|
|
|
|
connectedRasds.Add(rasd);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 22: // Check to see if it's Microsoft Synthetic Disk Drive
|
|
|
|
|
{
|
|
|
|
|
if (Tools.ValidateProperty("ResourceSubType", rasd) &&
|
|
|
|
|
rasd.ResourceSubType.Value.ToLower().Contains("synthetic")
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
connectedRasds.AddRange(FindConnectedItems(rasd.InstanceID.Value, rasds, rasd.Address.Value));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 17: // VMware Hard Disk
|
|
|
|
|
case 19: // XenServer/XenConvert Storage Extent
|
|
|
|
|
case 21: // Microsoft Hard Disk Image
|
|
|
|
|
{
|
|
|
|
|
if ((Tools.ValidateProperty("ElementName", rasd) && rasd.ElementName.Value.ToLower().Contains("hard disk")) ||
|
|
|
|
|
(Tools.ValidateProperty("Caption", rasd) && rasd.Caption.Value.ToLower().Contains("hard disk")) ||
|
|
|
|
|
(Tools.ValidateProperty("Caption", rasd) && rasd.Caption.Value.ToLower().StartsWith("disk"))
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
if (value22 != null)
|
|
|
|
|
{
|
|
|
|
|
rasd.Address = new cimString(value22);
|
|
|
|
|
}
|
|
|
|
|
if (!connectedRasds.Contains(rasd))
|
|
|
|
|
connectedRasds.Add(rasd);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Comparison<RASD_Type> diskcomparison = new Comparison<RASD_Type>(compareConnectedDisks);
|
|
|
|
|
connectedRasds.Sort(diskcomparison);
|
|
|
|
|
return connectedRasds;
|
|
|
|
|
}
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static void SetIfDeviceIsBootable(EnvelopeType ovfEnv, RASD_Type rasd)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
// This is a best guess algorithm. without opening the VHD itself, there is no guarrenteed method
|
|
|
|
|
// to delineate this, so we guess.
|
|
|
|
|
// IF it's created by Kensho/XenConvert there will be a chance of having a clue.
|
|
|
|
|
// Otherwise it'll be based upon 'order' and device 0 will win the bootable device.
|
|
|
|
|
bool isBootable = true;
|
|
|
|
|
VirtualDiskDesc_Type[] disks = null;
|
|
|
|
|
|
|
|
|
|
foreach (Section_Type sect in ovfEnv.Sections)
|
|
|
|
|
{
|
|
|
|
|
if (sect is DiskSection_Type)
|
|
|
|
|
{
|
|
|
|
|
disks = ((DiskSection_Type)sect).Disk;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (disks == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
bool useHostResource = false;
|
|
|
|
|
if (Tools.ValidateProperty("HostResource", rasd))
|
|
|
|
|
{
|
2013-09-25 11:28:38 +02:00
|
|
|
|
log.Debug("Using HostResource to find Disk");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
useHostResource = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-09-25 11:28:38 +02:00
|
|
|
|
log.Debug("Using InstanceID to find Disk");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach(VirtualDiskDesc_Type disk in disks)
|
|
|
|
|
{
|
|
|
|
|
if (useHostResource)
|
|
|
|
|
{
|
|
|
|
|
if (rasd.HostResource[0].Value.Contains(disk.diskId))
|
|
|
|
|
{
|
|
|
|
|
isBootable = disk.isBootable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (rasd.InstanceID.Value.Contains(disk.diskId))
|
|
|
|
|
{
|
|
|
|
|
isBootable = disk.isBootable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Tools.ValidateProperty("Address", rasd))
|
|
|
|
|
{
|
|
|
|
|
if ((rasd.ResourceType.Value == 21 ||
|
|
|
|
|
rasd.ResourceType.Value == 5) &&
|
|
|
|
|
rasd.Address.Value == "0")
|
|
|
|
|
{
|
|
|
|
|
isBootable = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Tools.ValidateProperty("AddressOnParent", rasd))
|
|
|
|
|
{
|
|
|
|
|
if ((rasd.ResourceType.Value == 17 ||
|
|
|
|
|
rasd.ResourceType.Value == 19) &&
|
|
|
|
|
rasd.AddressOnParent.Value == "0")
|
|
|
|
|
{
|
|
|
|
|
isBootable = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Tools.ValidateProperty("Connection", rasd))
|
|
|
|
|
{
|
|
|
|
|
if (rasd.Connection[0].Value.Contains("device=0"))
|
|
|
|
|
{
|
|
|
|
|
isBootable = true;
|
|
|
|
|
}
|
|
|
|
|
if (!rasd.Connection[0].Value.Contains("bootable"))
|
|
|
|
|
{
|
|
|
|
|
rasd.Connection[0].Value = string.Format("{0},bootable={1}", rasd.Connection[0].Value, isBootable);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
rasd.Connection = new cimString[] { new cimString(string.Format("bootable={0}", isBootable)) };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 20:04:55 +02:00
|
|
|
|
private static void ShowSystem(Session xenSession, XenRef<VM> vmRef)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
VM.remove_from_other_config(xenSession, vmRef, "HideFromXenCenter");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int compareControllerRasd(RASD_Type rasd1, RASD_Type rasd2)
|
|
|
|
|
{
|
|
|
|
|
if (rasd1.ResourceType.Value >= 5 &&
|
|
|
|
|
rasd1.ResourceType.Value <= 9 &&
|
|
|
|
|
rasd2.ResourceType.Value >= 5 &&
|
|
|
|
|
rasd2.ResourceType.Value <= 9 &&
|
|
|
|
|
rasd1.Address != null &&
|
|
|
|
|
rasd1.Address.Value != null &&
|
|
|
|
|
rasd2.Address != null &&
|
|
|
|
|
rasd2.Address.Value != null)
|
|
|
|
|
{
|
|
|
|
|
ushort address1 = Convert.ToUInt16(rasd1.Address.Value);
|
|
|
|
|
ushort address2 = Convert.ToUInt16(rasd2.Address.Value);
|
|
|
|
|
int left = (rasd1.ResourceType.Value * 10) + address1;
|
|
|
|
|
int right = (rasd2.ResourceType.Value * 10) + address2;
|
|
|
|
|
return (left).CompareTo(right);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return rasd1.ResourceType.Value.CompareTo(rasd2.ResourceType.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-31 18:32:31 +02:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
private static int compareConnectedDisks(RASD_Type rasd1, RASD_Type rasd2)
|
|
|
|
|
{
|
|
|
|
|
if (rasd1.AddressOnParent != null &&
|
|
|
|
|
rasd1.AddressOnParent.Value != null &&
|
|
|
|
|
rasd2.AddressOnParent != null &&
|
|
|
|
|
rasd2.AddressOnParent.Value != null)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
return (rasd1.AddressOnParent.Value).CompareTo(rasd2.AddressOnParent.Value);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (rasd1.Address != null &&
|
|
|
|
|
rasd1.Address.Value != null &&
|
|
|
|
|
rasd2.Address != null &&
|
|
|
|
|
rasd2.Address.Value != null)
|
|
|
|
|
{
|
|
|
|
|
ushort address1 = Convert.ToUInt16(rasd1.Address.Value);
|
|
|
|
|
ushort address2 = Convert.ToUInt16(rasd2.Address.Value);
|
|
|
|
|
return (address1).CompareTo(address2);
|
|
|
|
|
}
|
|
|
|
|
throw new ArgumentNullException("Cannot compare null values");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|