2013-06-24 13:41:48 +02:00
/ * Copyright ( c ) Citrix Systems Inc .
* 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.Generic ;
using System.Linq ;
using XenAdmin.Network ;
using XenAPI ;
using XenAdmin.Core ;
using System.Xml ;
namespace XenAdmin.Actions.VMActions
{
public enum InstallMethod
{
None ,
CD ,
Network
}
public class CreateVMAction : AsyncAction
{
private static readonly log4net . ILog log = log4net . LogManager . GetLogger ( System . Reflection . MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
private readonly string NameLabel ;
private readonly string NameDescription ;
private readonly InstallMethod InsMethod ;
private readonly string PvArgs ;
private readonly VDI Cd ;
private readonly string Url ;
private readonly Host HomeServer ;
2016-09-29 13:11:37 +02:00
private readonly long VcpusMax ;
private readonly long VcpusAtStartup ;
2013-06-24 13:41:48 +02:00
private readonly long MemoryDynamicMin , MemoryDynamicMax , MemoryStaticMax ;
private readonly List < DiskDescription > Disks ;
private readonly List < VIF > Vifs ;
private readonly bool StartAfter ;
private readonly Host CopyBiosStringsFrom ;
private readonly SR FullCopySR ;
2013-09-20 12:24:46 +02:00
private readonly GPU_group GpuGroup ;
private readonly VGPU_type VgpuType ;
2014-06-05 14:03:06 +02:00
private readonly long CoresPerSocket ;
2014-12-04 19:04:43 +01:00
private readonly string cloudConfigDriveTemplateText ;
private SR firstSR = null ;
2013-06-24 13:41:48 +02:00
private Action < VMStartAbstractAction , Failure > _startDiagnosisForm ;
private Action < VM , bool > _warningDialogHAInvalidConfig ;
private bool PointOfNoReturn ;
2014-06-30 10:44:19 +02:00
private bool assignOrRemoveVgpu ;
2013-06-24 13:41:48 +02:00
/// <summary>
/// These are the RBAC dependencies that you always need to create a VM. Check CreateVMAction constructor for runtime dependent dependencies.
/// </summary>
public static RbacMethodList StaticRBACDependencies = new RbacMethodList (
// provision VM
"vm.provision" ,
"vm.set_other_config" ,
// set VM Params
"vm.set_name_label" ,
"vm.set_name_description" ,
"vm.set_VCPUs_max" ,
"vm.set_VCPUs_at_startup" ,
// set VM Boot Params
"vm.set_HVM_boot_params" ,
"vm.set_PV_args" ,
"vm.set_other_config" ,
// Add CD Drive
"vbd.eject" ,
"vbd.insert" ,
// Create CD Drive
"vbd.create" ,
// Add disks
"vdi.destroy" ,
"vdi.create" ,
"vdi.set_sm_config" ,
"vbd.create" ,
"vbd.destroy" ,
"vdi.copy" ,
// Add networks
2014-06-05 14:03:06 +02:00
"vif.create" ,
"vm.set_platform"
2013-06-24 13:41:48 +02:00
) ;
2013-09-20 12:24:46 +02:00
public CreateVMAction ( IXenConnection connection , VM template , Host copyBiosStringsFrom ,
string name , string description , InstallMethod installMethod ,
2016-09-29 13:11:37 +02:00
string pvArgs , VDI cd , string url , Host homeServer , long vcpusMax , long vcpusAtStartup ,
2013-09-20 12:24:46 +02:00
long memoryDynamicMin , long memoryDynamicMax , long memoryStaticMax ,
List < DiskDescription > disks , SR fullCopySR , List < VIF > vifs , bool startAfter ,
Action < VM , bool > warningDialogHAInvalidConfig ,
Action < VMStartAbstractAction , Failure > startDiagnosisForm ,
2015-08-04 12:25:59 +02:00
GPU_group gpuGroup , VGPU_type vgpuType , bool modifyVgpuSettings , long coresPerSocket , string cloudConfigDriveTemplateText )
2013-09-20 12:24:46 +02:00
: base ( connection , string . Format ( Messages . CREATE_VM , name ) ,
string . Format ( Messages . CREATE_VM_FROM_TEMPLATE , name , Helpers . GetName ( template ) ) )
2013-06-24 13:41:48 +02:00
{
Template = template ;
CopyBiosStringsFrom = copyBiosStringsFrom ;
FullCopySR = fullCopySR ;
NameLabel = name ;
NameDescription = description ;
InsMethod = installMethod ;
PvArgs = pvArgs ;
Cd = cd ;
Url = url ;
HomeServer = homeServer ;
2016-09-29 13:11:37 +02:00
VcpusMax = vcpusMax ;
VcpusAtStartup = vcpusAtStartup ;
2013-06-24 13:41:48 +02:00
MemoryDynamicMin = memoryDynamicMin ;
MemoryDynamicMax = memoryDynamicMax ;
MemoryStaticMax = memoryStaticMax ;
Disks = disks ;
Vifs = vifs ;
StartAfter = startAfter ;
_warningDialogHAInvalidConfig = warningDialogHAInvalidConfig ;
_startDiagnosisForm = startDiagnosisForm ;
2013-09-20 12:24:46 +02:00
GpuGroup = gpuGroup ;
VgpuType = vgpuType ;
2014-06-05 14:03:06 +02:00
CoresPerSocket = coresPerSocket ;
2014-12-04 19:04:43 +01:00
this . cloudConfigDriveTemplateText = cloudConfigDriveTemplateText ;
2013-06-24 13:41:48 +02:00
Pool pool_of_one = Helpers . GetPoolOfOne ( Connection ) ;
if ( HomeServer ! = null | | pool_of_one ! = null ) // otherwise we have no where to put the action
AppliesTo . Add ( HomeServer ! = null ? HomeServer . opaque_ref : pool_of_one . opaque_ref ) ;
2015-08-04 12:25:59 +02:00
assignOrRemoveVgpu = GpuGroup ! = null & & VgpuType ! = null | | modifyVgpuSettings & & Helpers . GpuCapability ( Connection ) ;
2014-06-30 10:44:19 +02:00
2013-06-24 13:41:48 +02:00
#region RBAC Dependencies
if ( StartAfter )
ApiMethodsToRoleCheck . Add ( "vm.start" ) ;
if ( HomeServerChanged ( ) )
ApiMethodsToRoleCheck . Add ( "vm.set_affinity" ) ;
if ( Template . memory_dynamic_min ! = MemoryDynamicMin | | Template . memory_dynamic_max ! = MemoryDynamicMax | | Template . memory_static_max ! = MemoryStaticMax )
ApiMethodsToRoleCheck . Add ( "vm.set_memory_limits" ) ;
2014-06-30 10:44:19 +02:00
if ( assignOrRemoveVgpu )
2013-09-20 12:24:46 +02:00
{
ApiMethodsToRoleCheck . Add ( "VGPU.destroy" ) ;
ApiMethodsToRoleCheck . Add ( "VGPU.create" ) ;
}
2013-06-24 13:41:48 +02:00
ApiMethodsToRoleCheck . AddRange ( StaticRBACDependencies ) ;
ApiMethodsToRoleCheck . AddRange ( Role . CommonTaskApiList ) ;
ApiMethodsToRoleCheck . AddRange ( Role . CommonSessionApiList ) ;
2013-09-20 12:24:46 +02:00
2013-06-24 13:41:48 +02:00
#endregion
}
protected override void Run ( )
{
if ( FullCopySR ! = null )
{
// VM.copy is the best call to make if all target disks are on the same SR.
// however, if the target disks are on the same SR as the source disks, then the user is
// given the choice of a fast-clone (VM.clone) or a full-copy (VM.copy) on the storage page of the wizard. If the
// user chose a VM.clone, then FullCopySR will be null.
RelatedTask = VM . async_copy ( Session , Template . opaque_ref , HiddenVmName , FullCopySR . opaque_ref ) ;
}
else
{
// if the target disks are on mixed storage or the user chose to a do a fast-clone on the storage
// page then we end up here.
RelatedTask = VM . async_clone ( Session , Template . opaque_ref , HiddenVmName ) ;
}
Description = string . Format ( Messages . CLONING_TEMPLATE , Helpers . GetName ( Template ) ) ;
PollToCompletion ( 0 , 10 ) ;
VM = Connection . WaitForCache ( new XenRef < VM > ( Result ) ) ;
2015-12-17 16:31:41 +01:00
ApplyRecommendationsForVendorDevice ( ) ;
2013-06-24 13:41:48 +02:00
CopyBiosStrings ( ) ;
SetXenCenterProperties ( ) ;
ProvisionVM ( ) ;
SetVMParams ( ) ;
SetVMBootParams ( ) ;
AddCdDrive ( ) ;
AddDisks ( ) ;
AddNetworks ( ) ;
XenAdminConfigManager . Provider . ShowObject ( VM . opaque_ref ) ;
PointOfNoReturn = true ;
2014-12-04 19:04:43 +01:00
CloudCreateConfigDrive ( ) ;
2013-09-20 12:24:46 +02:00
AssignVgpu ( ) ;
2013-06-24 13:41:48 +02:00
if ( StartAfter )
{
Description = Messages . STARTING_VM ;
var startAction = new VMStartAction ( VM , _warningDialogHAInvalidConfig , _startDiagnosisForm ) ;
startAction . RunAsync ( ) ;
}
Description = Messages . VM_SUCCESSFULLY_CREATED ;
}
2015-12-17 16:31:41 +01:00
private void ApplyRecommendationsForVendorDevice ( )
{
2016-04-27 10:58:33 +02:00
var pool = Helpers . GetPoolOfOne ( Connection ) ;
bool poolPolicyNoVendorDevice = pool = = null | | pool . policy_no_vendor_device ;
if ( Template . HasVendorDeviceRecommendation & & ! poolPolicyNoVendorDevice & & ! Helpers . FeatureForbidden ( VM , Host . RestrictVendorDevice ) )
2015-12-17 16:31:41 +01:00
{
log . DebugFormat ( "Recommendation (has-vendor-device = true) has been found on the template ({0}) and the host is licensed, so applying it on VM ({1}) being created." , Template . opaque_ref , VM . opaque_ref ) ;
VM . set_has_vendor_device ( Connection . Session , VM . opaque_ref , true ) ;
}
else
{
log . DebugFormat ( "Recommendation (has-vendor-device = true) has not been applied on the VM ({0}) being created." , VM . opaque_ref ) ;
if ( ! Template . HasVendorDeviceRecommendation )
2016-04-27 10:58:33 +02:00
log . DebugFormat ( "Recommendation (has-vendor-device) is not set or false on the template ({0})." , Template . opaque_ref ) ;
if ( poolPolicyNoVendorDevice )
log . DebugFormat ( "pool.policy_no_vendor_device returned {0}" , poolPolicyNoVendorDevice ) ;
2015-12-17 16:31:41 +01:00
if ( Helpers . FeatureForbidden ( VM , Host . RestrictVendorDevice ) )
2016-04-27 10:58:33 +02:00
log . DebugFormat ( "Helpers.FeatureForbidden(VM, Host.RestrictVendorDevice) returned {0}" , Helpers . FeatureForbidden ( VM , Host . RestrictVendorDevice ) ) ;
2015-12-17 16:31:41 +01:00
}
}
2014-12-04 19:04:43 +01:00
private void CloudCreateConfigDrive ( )
{
2015-02-13 15:43:13 +01:00
if ( Template . CanHaveCloudConfigDrive & & ! string . IsNullOrEmpty ( cloudConfigDriveTemplateText ) )
2014-12-04 19:04:43 +01:00
{
2015-02-19 15:48:52 +01:00
Description = Messages . CREATING_CLOUD_CONFIG_DRIVE ;
2014-12-04 19:04:43 +01:00
var parameters = new Dictionary < string , string > ( ) ;
parameters . Add ( "vmuuid" , VM . uuid ) ;
parameters . Add ( "sruuid" , firstSR . uuid ) ;
parameters . Add ( "configuration" , cloudConfigDriveTemplateText . Replace ( "\r\n" , "\n" ) ) ;
2015-02-09 12:16:01 +01:00
var action = new ExecutePluginAction ( Connection , HomeServer ? ? Helpers . GetMaster ( Connection ) ,
2014-12-04 19:04:43 +01:00
"xscontainer" , //plugin
"create_config_drive" , //function
parameters ,
true ) ; //hidefromlogs
action . RunExternal ( Connection . Session ) ;
var result = action . Result . Replace ( "\n" , Environment . NewLine ) ;
}
}
2013-09-20 12:24:46 +02:00
private void AssignVgpu ( )
{
2014-06-30 10:44:19 +02:00
if ( assignOrRemoveVgpu )
2013-09-20 12:24:46 +02:00
{
var action = new GpuAssignAction ( VM , GpuGroup , VgpuType ) ;
action . RunExternal ( Session ) ;
}
}
2013-06-24 13:41:48 +02:00
private void CopyBiosStrings ( )
{
if ( CopyBiosStringsFrom ! = null & & Template . DefaultTemplate )
{
VM . copy_bios_strings ( Session , this . VM . opaque_ref , CopyBiosStringsFrom . opaque_ref ) ;
}
}
private void SetXenCenterProperties ( )
{
XenAdminConfigManager . Provider . HideObject ( VM . opaque_ref ) ;
AppliesTo . Add ( VM . opaque_ref ) ;
}
private void SetVMParams ( )
{
Description = Messages . SETTING_VM_PROPERTIES ;
XenAPI . VM . set_name_label ( Session , VM . opaque_ref , NameLabel ) ;
XenAPI . VM . set_name_description ( Session , VM . opaque_ref , NameDescription ) ;
2016-09-29 13:11:37 +02:00
ChangeVCPUSettingsAction vcpuAction = new ChangeVCPUSettingsAction ( VM , VcpusMax , VcpusAtStartup ) ;
2013-06-24 13:41:48 +02:00
vcpuAction . RunExternal ( Session ) ;
2014-06-05 14:03:06 +02:00
// set cores-per-socket
Dictionary < string , string > platform = VM . platform = = null ?
new Dictionary < string , string > ( ) :
new Dictionary < string , string > ( VM . platform ) ;
platform [ "cores-per-socket" ] = CoresPerSocket . ToString ( ) ;
VM . set_platform ( Session , VM . opaque_ref , platform ) ;
2013-06-24 13:41:48 +02:00
// Check these values have changed before setting them, as they are RBAC protected
if ( HomeServerChanged ( ) )
XenAPI . VM . set_affinity ( Session , VM . opaque_ref , HomeServer ! = null ? HomeServer . opaque_ref : Helper . NullOpaqueRef ) ;
2015-10-26 17:01:55 +01:00
if ( Template . memory_dynamic_min ! = MemoryDynamicMin | | Template . memory_dynamic_max ! = MemoryDynamicMax | | Template . memory_static_max ! = MemoryStaticMax )
XenAPI . VM . set_memory_limits ( Session , VM . opaque_ref , Template . memory_static_min , MemoryStaticMax , MemoryDynamicMin , MemoryDynamicMax ) ;
2013-06-24 13:41:48 +02:00
}
private bool HomeServerChanged ( )
{
if ( HomeServer = = null )
{
return Template . affinity . opaque_ref ! = Helper . NullOpaqueRef ;
}
return HomeServer . opaque_ref ! = Template . affinity . opaque_ref ;
}
private void SetVMBootParams ( )
{
if ( Template . IsHVM & & ( Disks . Count = = 0 | | InsMethod = = InstallMethod . Network ) ) // CA-46213
{
// boot from network
Dictionary < string , string > hvm_params = VM . HVM_boot_params ;
hvm_params [ "order" ] = GetBootOrderNetworkFirst ( ) ;
XenAPI . VM . set_HVM_boot_params ( Session , VM . opaque_ref , hvm_params ) ;
}
else if ( IsEli ( ) & & InsMethod = = InstallMethod . Network )
{
Dictionary < string , string > other_config = VM . other_config ;
string normal_url = IsRhel ( ) ? NormalizeRepoUrlForRHEL ( Url ) : Url ;
other_config [ "install-repository" ] = normal_url ;
XenAPI . VM . set_other_config ( Session , VM . opaque_ref , other_config ) ;
}
else if ( IsEli ( ) & & InsMethod = = InstallMethod . CD )
{
Dictionary < string , string > other_config = VM . other_config ;
other_config [ "install-repository" ] = "cdrom" ;
XenAPI . VM . set_other_config ( Session , VM . opaque_ref , other_config ) ;
}
if ( ! Template . IsHVM )
{
XenAPI . VM . set_PV_args ( Session , VM . opaque_ref , PvArgs ) ;
}
}
private bool IsEli ( )
{
return ! Template . IsHVM & & Template . PV_bootloader = = "eliloader" ;
}
private bool IsRhel ( )
{
string distro = VM . InstallDistro ;
return distro = = "rhel41" | | distro = = "rhel44" | | distro = = "rhlike" ;
}
private string NormalizeRepoUrlForRHEL ( string url )
{
Uri uri = new Uri ( url ) ;
return uri . Scheme = = "nfs" ? string . Format ( "nfs:{0}:{1}" , uri . Host , uri . PathAndQuery ) : url ;
}
private void ProvisionVM ( )
{
Description = Messages . PROVISIONING_VM ;
RewriteProvisionXML ( ) ;
RelatedTask = XenAPI . VM . async_provision ( Session , VM . opaque_ref ) ;
PollToCompletion ( 10 , 60 ) ;
}
private void RewriteProvisionXML ( )
{
XmlNode xml = VM . ProvisionXml ;
if ( xml = = null )
return ;
2015-09-18 16:51:17 +02:00
// set the new vm's provision xml: remove "disks" entry, as we are going to explicitly create all the disks
2013-06-24 13:41:48 +02:00
Dictionary < string , string > other_config = VM . other_config ;
2015-09-18 16:51:17 +02:00
other_config . Remove ( "disks" ) ;
2013-06-24 13:41:48 +02:00
XenAPI . VM . set_other_config ( Session , VM . opaque_ref , other_config ) ;
}
private void AddCdDrive ( )
{
if ( Helpers . CustomWithNoDVD ( Template ) )
return ; // we have skipped the install media page because we are a cutom template with no cd drive - the user doesnt want a cd drive
Description = Messages . CREATE_CD_DRIVE ;
VBD cd_drive = null ;
foreach ( VBD vbd in Connection . ResolveAll ( VM . VBDs ) )
{
if ( vbd . type ! = vbd_type . CD )
continue ;
if ( "0123" . IndexOf ( vbd . userdevice ) < 0 ) // userdevice is not 0, 1, 2 or 3: these are the valid positions for CD drives
continue ;
cd_drive = vbd ;
break ;
}
if ( cd_drive = = null )
{
cd_drive = CreateCdDrive ( ) ;
}
if ( ! cd_drive . empty )
{
RelatedTask = VBD . async_eject ( Session , cd_drive . opaque_ref ) ;
PollToCompletion ( 65 , 67 ) ;
}
if ( InsMethod = = InstallMethod . CD & & Cd ! = null ) // obviously dont insert the empty cd
{
RelatedTask = VBD . async_insert ( Session , cd_drive . opaque_ref , Cd . opaque_ref ) ;
PollToCompletion ( 67 , 70 ) ;
}
}
private VBD CreateCdDrive ( )
{
List < string > devices = AllowedVBDs ;
if ( devices . Count = = 0 )
throw new Exception ( Messages . NO_MORE_USERDEVICES ) ;
VBD vbd = new VBD ( ) ;
vbd . bootable = InsMethod = = InstallMethod . CD ;
vbd . empty = true ;
vbd . unpluggable = true ;
vbd . mode = vbd_mode . RO ;
vbd . type = vbd_type . CD ;
vbd . userdevice = devices . Contains ( "3" ) ? "3" : devices [ 0 ] ;
vbd . device = "" ;
vbd . VM = new XenRef < VM > ( VM . opaque_ref ) ;
vbd . VDI = null ;
RelatedTask = VBD . async_create ( Session , vbd ) ;
PollToCompletion ( 60 , 65 ) ;
return Connection . WaitForCache ( new XenRef < VBD > ( Result ) ) ;
}
private void AddDisks ( )
{
Description = Messages . CREATING_DISKS ;
List < VBD > vbds = Connection . ResolveAll ( VM . VBDs ) ;
bool firstDisk = true ;
string suspendSr = null ;
double progress = 70 ;
double step = 20.0 / ( double ) Disks . Count ;
foreach ( DiskDescription disk in Disks )
{
VBD vbd = GetDiskVBD ( disk , vbds ) ;
VDI vdi = null ;
if ( vbd ! = null )
{
vdi = Connection . Resolve < VDI > ( vbd . VDI ) ;
}
if ( ! DiskOk ( disk , vbd ) )
{
if ( vbd ! = null )
vdi = MoveDisk ( disk , vbd , progress , step ) ;
else
vdi = CreateDisk ( disk , progress , step ) ;
}
if ( vdi = = null )
continue ;
if ( vdi . name_description ! = disk . Disk . name_description )
VDI . set_name_description ( Session , vdi . opaque_ref , disk . Disk . name_description ) ;
if ( vdi . name_label ! = disk . Disk . name_label )
VDI . set_name_label ( Session , vdi . opaque_ref , disk . Disk . name_label ) ;
2015-10-26 17:01:55 +01:00
if ( firstDisk )
2013-06-24 13:41:48 +02:00
{
//use the first disk to set the VM.suspend_SR
SR vdiSR = Connection . Resolve ( vdi . SR ) ;
2014-12-04 19:04:43 +01:00
this . firstSR = vdiSR ;
2013-06-24 13:41:48 +02:00
if ( vdiSR ! = null & & ! vdiSR . HBALunPerVDI )
suspendSr = vdi . SR ;
firstDisk = false ;
}
progress + = step ;
}
2015-10-26 17:01:55 +01:00
VM . set_suspend_SR ( Session , VM . opaque_ref , suspendSr ) ;
2013-06-24 13:41:48 +02:00
}
private VBD GetDiskVBD ( DiskDescription disk , List < VBD > vbds )
{
foreach ( VBD vbd in vbds )
{
if ( disk . Device . userdevice = = vbd . userdevice )
return vbd ;
}
return null ;
}
private bool DiskOk ( DiskDescription disk , VBD vbd )
{
if ( vbd = = null )
return false ;
VDI vdi = Connection . Resolve ( vbd . VDI ) ;
return vdi ! = null & & disk . Disk . SR . opaque_ref = = vdi . SR . opaque_ref ;
}
private VDI MoveDisk ( DiskDescription disk , VBD vbd , double progress , double step )
{
string old_vdi_ref = vbd . VDI . opaque_ref ;
RelatedTask = XenAPI . VDI . async_copy ( Session , vbd . VDI . opaque_ref , disk . Disk . SR . opaque_ref ) ;
PollToCompletion ( progress , progress + 0.25 * step ) ;
AddVMHint ( Connection . WaitForCache ( new XenRef < VDI > ( Result ) ) ) ;
VDI new_vdi = Connection . Resolve ( new XenRef < VDI > ( Result ) ) ;
RelatedTask = XenAPI . VBD . async_destroy ( Session , vbd . opaque_ref ) ;
PollToCompletion ( progress + 0.25 * step , progress + 0.5 * step ) ;
RelatedTask = XenAPI . VDI . async_destroy ( Session , old_vdi_ref ) ;
PollToCompletion ( progress + 0.5 * step , progress + 0.75 * step ) ;
CreateVbd ( disk , new_vdi , progress + 0.75 * step , progress + step , IsDeviceAtPositionZero ( disk ) ) ;
return new_vdi ;
}
/// <summary>
/// Helper: Check if the disk is at the zeroth position in the VBD list
/// </summary>
/// <param name="disk"></param>
/// <returns></returns>
private bool IsDeviceAtPositionZero ( DiskDescription disk )
{
return disk . Device . userdevice = = "0" ;
}
/// <summary>
/// Create a VDI/disk.
/// If disk type is existing use the VDI in disk description
/// Otherwise create a new disk (provision it from the SR)
/// </summary>
/// <param name="disk"></param>
/// <param name="progress"></param>
/// <param name="step"></param>
/// <returns></returns>
private VDI CreateDisk ( DiskDescription disk , double progress , double step )
{
VDI vdi ;
bool bootable = false ;
if ( disk . Type = = DiskDescription . DiskType . Existing )
vdi = disk . Disk ;
else
{
vdi = CreateVdi ( disk , progress , progress + 0.75 * step ) ;
2015-09-18 16:51:17 +02:00
bootable = IsDeviceAtPositionZero ( disk ) & & InsMethod ! = InstallMethod . CD ;
2013-06-24 13:41:48 +02:00
}
AddVMHint ( vdi ) ;
CreateVbd ( disk , vdi , progress + 0.75 * step , progress + step , bootable ) ;
return vdi ;
}
private void AddVMHint ( VDI vdi )
{
Dictionary < string , string > sm_config = VDI . get_sm_config ( Session , vdi . opaque_ref ) ;
sm_config [ "vmhint" ] = VM . opaque_ref ;
VDI . set_sm_config ( Session , vdi . opaque_ref , sm_config ) ;
}
private VDI CreateVdi ( DiskDescription disk , double progress1 , double progress2 )
{
VDI vdi = new VDI ( ) ;
vdi . name_label = disk . Disk . name_label ;
vdi . name_description = disk . Disk . name_description ;
vdi . read_only = false ;
vdi . sharable = false ;
vdi . SR = disk . Disk . SR ;
vdi . type = disk . Disk . type ;
vdi . virtual_size = disk . Disk . virtual_size ;
2015-09-18 16:51:17 +02:00
vdi . sm_config = disk . Disk . sm_config ;
2013-06-24 13:41:48 +02:00
RelatedTask = XenAPI . VDI . async_create ( Session , vdi ) ;
PollToCompletion ( progress1 , progress2 ) ;
return Connection . WaitForCache ( new XenRef < VDI > ( Result ) ) ;
}
/// <summary>
/// Create a VBD
///
/// ** vbd.bootable **
/// 1. Windows ignores bootable flag
/// 2. Eliloader changes the device "0" to bootable when booting linux
/// </summary>
/// <param name="disk"></param>
/// <param name="vdi"></param>
/// <param name="progress1"></param>
/// <param name="progress2"></param>
/// <param name="bootable">Set VBD.bootable to this value - see comments above</param>
private void CreateVbd ( DiskDescription disk , VDI vdi , double progress1 , double progress2 , bool bootable )
{
List < string > devices = AllowedVBDs ;
if ( devices . Count = = 0 )
throw new Exception ( Messages . NO_MORE_USERDEVICES ) ;
VBD vbd = new VBD ( ) ;
vbd . IsOwner = true ;
vbd . bootable = bootable ;
vbd . empty = false ;
vbd . unpluggable = true ;
vbd . mode = vbd_mode . RW ;
vbd . type = vbd_type . Disk ;
vbd . userdevice = devices . Contains ( disk . Device . userdevice ) ? disk . Device . userdevice : devices [ 0 ] ;
vbd . device = "" ;
vbd . VM = new XenRef < VM > ( VM . opaque_ref ) ;
vbd . VDI = new XenRef < VDI > ( vdi . opaque_ref ) ;
RelatedTask = VBD . async_create ( Session , vbd ) ;
PollToCompletion ( progress1 , progress2 ) ;
Connection . WaitForCache ( new XenRef < VBD > ( Result ) ) ;
}
private void AddNetworks ( )
{
// first of all we need to clear any vifs that we have cloned from the template
double progress = 90 ;
VIF vif ;
List < VIF > existingTemplateVifs = Connection . ResolveAll ( VM . VIFs ) ;
double step = 5.0 / ( double ) existingTemplateVifs . Count ;
for ( int i = 0 ; i < existingTemplateVifs . Count ; i + + )
{
vif = existingTemplateVifs [ i ] ;
RelatedTask = XenAPI . VIF . async_destroy ( Session , vif . opaque_ref ) ;
PollToCompletion ( progress , progress + step ) ;
progress + = step ;
}
// then we add the ones the user has specified
step = 5.0 / ( double ) Vifs . Count ;
for ( int i = 0 ; i < Vifs . Count ; i + + )
{
vif = Vifs [ i ] ;
List < string > devices = AllowedVIFs ;
VIF new_vif = new VIF ( ) ;
if ( devices . Count < 1 )
{
// If we have assigned more VIFs than we have space for then don't try to create them
log . Warn ( "Tried to create more VIFs than the server allows. Ignoring remaining vifs" ) ;
return ;
}
new_vif . device = devices . Contains ( vif . device ) ? vif . device : devices [ 0 ] ;
new_vif . MAC = vif . MAC ;
new_vif . network = vif . network ;
new_vif . VM = new XenRef < VM > ( VM . opaque_ref ) ;
new_vif . qos_algorithm_type = vif . qos_algorithm_type ;
new_vif . qos_algorithm_params = vif . qos_algorithm_params ;
RelatedTask = XenAPI . VIF . async_create ( Session , new_vif ) ;
PollToCompletion ( progress , progress + step ) ;
progress + = step ;
Connection . WaitForCache ( new XenRef < VIF > ( Result ) ) ;
}
}
private string HiddenVmName
{
get
{
return string . Format ( "{0}{1}" , Helpers . GuiTempObjectPrefix , NameLabel ) ;
}
}
private List < string > AllowedVBDs
{
get
{
return new List < String > ( XenAPI . VM . get_allowed_VBD_devices ( Session , VM . opaque_ref ) ) ;
}
}
private List < string > AllowedVIFs
{
get
{
return new List < String > ( XenAPI . VM . get_allowed_VIF_devices ( Session , VM . opaque_ref ) ) ;
}
}
protected override void CleanOnError ( )
{
if ( VM ! = null & & ! PointOfNoReturn & & Connection . IsConnected )
{
try
{
VMDestroyAction . DestroyVM ( Session , VM , true ) ;
}
catch ( Exception e )
{
// if the clean up has failed for whatever reason we just log it and give up.
log . Error ( e ) ;
}
}
}
private string GetBootOrderNetworkFirst ( )
{
// add "n" at the beginning of the order string
if ( VM . HVM_boot_params . ContainsKey ( "order" ) )
{
string order = VM . HVM_boot_params [ "order" ] . ToLower ( ) ;
int i = order . IndexOf ( "n" ) ;
switch ( i )
{
case - 1 : return order . Insert ( 0 , "n" ) ;
case 0 : return order ;
default : return order . Remove ( i , 1 ) . Insert ( 0 , "n" ) ;
}
}
else
{
return "ncd" ;
}
}
}
public class DiskDescription
{
public VDI Disk ;
public VBD Device ;
public DiskType Type ;
public enum DiskType { New , Existing }
public DiskDescription ( ) { }
public DiskDescription ( VDI disk , VBD device )
{
Disk = disk ;
Device = device ;
Type = DiskType . New ;
}
}
}