sub generate_domain_xml()

in managementnode/lib/VCL/Module/Provisioning/libvirt.pm [1755:2042]


sub generate_domain_xml {
	my $self = shift;
	unless (ref($self) && $self->isa('VCL::Module')) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
		return;
	}
	
	my $request_id = $self->data->get_request_id();
	my $reservation_id = $self->data->get_reservation_id();
	my $image_name = $self->data->get_image_name();
	my $image_display_name = $self->data->get_image_prettyname();
	my $image_os_type = $self->data->get_image_os_type();
	my $image_project = $self->data->get_image_project();
	my $computer_name = $self->data->get_computer_short_name();
	my $management_node_name = $self->data->get_management_node_short_name();
	
	my $timestamp = makedatestring();
	
	my $domain_name = $self->get_domain_name();
	my $domain_type = $self->driver->get_domain_type();
	
	my $copy_on_write_file_path = $self->get_copy_on_write_file_path();
	my $image_type = $self->data->get_vmhost_datastore_imagetype_name();
	my $vmhost_vmpath = $self->data->get_vmhost_profile_vmpath();
	my $add_disk_cache = 0;
	if (! $self->vmhost_os->is_file_on_local_disk($vmhost_vmpath)) {
		# set disk cache to none if vmpath on NFS so live migration will work
		$add_disk_cache = 1;
	}

	my $disk_driver_name = $self->driver->get_disk_driver_name();
	my $disk_bus_type = $self->get_master_xml_disk_bus_type();
	
	my $eth0_source_device = $self->data->get_vmhost_profile_virtualswitch0();
	my $eth1_source_device = $self->data->get_vmhost_profile_virtualswitch1();
	
	my $network_info = $self->get_node_network_info();
	
	my $eth0_interface_type = 'bridge';
	if (defined($network_info->{$eth0_source_device})) {
		$eth0_interface_type = 'network';
	}
	
	my $eth1_interface_type = 'bridge';
	if (defined($network_info->{$eth1_source_device})) {
		$eth1_interface_type = 'network';
	}

	my $eth0_mac_address;
	my $is_eth0_mac_address_random = $self->data->get_vmhost_profile_eth0generated(0);
	if ($is_eth0_mac_address_random) {
		$eth0_mac_address = get_random_mac_address();
		$self->data->set_computer_eth0_mac_address($eth0_mac_address);
	}
	else {
		$eth0_mac_address = $self->data->get_computer_eth0_mac_address();
	}
	
	my $eth1_mac_address;
	my $is_eth1_mac_address_random = $self->data->get_vmhost_profile_eth1generated(0);
	if ($is_eth1_mac_address_random) {
		$eth1_mac_address = get_random_mac_address();
		$self->data->set_computer_eth1_mac_address($eth1_mac_address);
	}
	else {
		$eth1_mac_address = $self->data->get_computer_eth1_mac_address();
	}
	
	my $interface_model_type = $self->get_master_xml_interface_model_type();
	
	my $cpu_count = $self->data->get_image_minprocnumber() || 1;
	
	my $memory_mb = $self->data->get_image_minram();
	if ($memory_mb < 512) {
		$memory_mb = 512;
	}
	my $memory_kb = ($memory_mb * 1024);
	
	my $description = <<EOF;
image: $image_display_name
revision: $image_name
load time: $timestamp
management node: $management_node_name
request ID: $request_id
reservation ID: $reservation_id
EOF

	# Per libvirt documentation:
	#   "The guest clock is typically initialized from the host clock.
	#    Most operating systems expect the hardware clock to be kept in UTC, and this is the default.
	#    Windows, however, expects it to be in so called 'localtime'."
	my $clock_offset = ($image_os_type =~ /windows/) ? 'localtime' : 'utc';
	
	my $cpusockets = $cpu_count;
	my $cpucores = 1;
	if($cpu_count > 2) {
		$cpusockets = 2;
		$cpucores = ($cpu_count - ($cpu_count % 2)) / 2;
	}

	my $xml_hashref = {
		'type' => $domain_type,
		'description' => [$description],
		'name' => [$domain_name],
		'on_poweroff' => ['preserve'],
		'on_reboot' => ['restart'],
		'on_crash' => ['preserve'],
		'os' => [
			{
				'type' => {
					'content' => 'hvm'
				}
			}
		],
		'features' => [
			{
				'acpi' => [{}],
				'apic' => [{}],
			}
		],
		'memory' => [$memory_kb],
		'vcpu'   => [$cpu_count],
		'cpu' => [
			
			{
				mode => 'host-model',		# Required, some images won't boot on different hosts without
				model => {
					'fallback' => 'allow',
				},
				topology => {
					'sockets' => $cpusockets,
					'cores' => $cpucores,
					'threads' => 1,
				},
			}
		],
		'clock' => [
			{
				'offset' => $clock_offset,
			}
		],
		'devices' => [
			{
				'disk' => [
					{
						'device' => 'disk',
						'type' => 'file',
						'driver' => {
							'name' => $disk_driver_name,
							'type' => $image_type,
							#'cache' => 'none',
						},
						'source' => {
							'file' => $copy_on_write_file_path,
						},
						'target' => {
							'bus' => $disk_bus_type,
							'dev' => 'vda',	# Required
						},
					}
				],
				'interface' => [
					{
						'type' => $eth0_interface_type,
						'mac' => {
							'address' => $eth0_mac_address,
						},
						'source' => {
							$eth0_interface_type => $eth0_source_device,
						},
						#'target' => {
						#	'dev' => 'vnet0',
						#},
						'model' => {
							'type' => $interface_model_type,
						},
					},
					#{
					#	'type' => $eth1_interface_type,	
					#	'mac' => {
					#		'address' => $eth1_mac_address,
					#	},
					#	'source' => {
					#		$eth1_interface_type => $eth1_source_device,
					#	},
					#	#'target' => {
					#	#	'dev' => 'vnet1',
					#	#},
					#	'model' => {
					#		'type' => $interface_model_type,
					#	},
					#}
				],
				'graphics' => [
					{
						'type' => 'vnc',
						#'type' => 'spice',
						#'autoport' => 'yes',
						#'port' => '-1',
						#'tlsPort' => '-1',
						
					}
				],
			}
		]
	};

	if ($add_disk_cache) {
		notify($ERRORS{'DEBUG'}, 0, "vmpath ($vmhost_vmpath) is on NFS; setting disk cache to none");
		$xml_hashref->{'devices'}[0]{'disk'}[0]{'driver'}{'cache'} = 'none';
	}

	# add nic for public, first check for project not including 'vcl', skipping the public network assigned to the vmhost profile
	my $skipadditionalnicnetwork = '';
	if ($image_project !~ /vcl/i) {
		foreach my $network_name (keys %{$network_info}) {
			next if ($network_name =~ /^$eth0_source_device$/i || $network_name =~ /^$eth1_source_device$/i);
			if ($network_name =~ /$image_project/i || $image_project =~ /$network_name/i) {
				$skipadditionalnicnetwork = $network_name;
				notify($ERRORS{'DEBUG'}, 0, "network name ($network_name) and image project name ($image_project) intersect, setting public network interface for VM to network $network_name");
				my %interface = (
					'type' => 'network',
					'mac' => {
						'address' => $eth1_mac_address,
					},
					'source' => {
						'network' => $network_name,
					},
					'model' => {
						'type' => $interface_model_type,
					}
				);
				push @{$xml_hashref->{'devices'}[0]{'interface'}}, \%interface;
			}
		}
	}

	my $nic_cnt = scalar @{$xml_hashref->{'devices'}[0]{interface}};

	# if no public nic added above, add public nic on network assigned in the vmhost profile
	if($nic_cnt == 1) {
		notify($ERRORS{'DEBUG'}, 0, "image project is: $image_project, adding $eth1_source_device for public network");
		my %interface = (
			'type' => $eth1_interface_type,
			'mac' => {
				'address' => $eth1_mac_address,
			},
			'source' => {
				$eth1_interface_type => $eth1_source_device,
			},
			'model' => {
				'type' => $interface_model_type,
			}
		);
		push @{$xml_hashref->{'devices'}[0]{'interface'}}, \%interface;
	}

	# add additional nics if project not strictly 'vcl'
	if ($image_project !~ /^vcl$/i) {
		notify($ERRORS{'DEBUG'}, 0, "image project is: $image_project, checking if additional network adapters should be configured");
		foreach my $network_name (keys %{$network_info}) {
			next if ($network_name =~ /^$eth0_source_device$/i || $network_name =~ /^$eth1_source_device$/i);
			next if ($network_name eq $skipadditionalnicnetwork);
			if ($network_name =~ /$image_project/i || $image_project =~ /$network_name/i) {
				notify($ERRORS{'DEBUG'}, 0, "network name ($network_name) and image project name ($image_project) intersect, adding network interface to VM for network $network_name");
				my %interface = (
					'type' => 'network',
					'mac' => {
						'address' => get_random_mac_address('00:0c:29:'),
					},
					'source' => {
						'network' => $network_name,
					},
					'model' => {
						'type' => $interface_model_type,
					}
				);
				push @{$xml_hashref->{'devices'}[0]{'interface'}}, \%interface;
			}
			else {
				notify($ERRORS{'DEBUG'}, 0, "network name ($network_name) and image project name ($image_project) do not intersect, network interface will not be added to VM for network $network_name");
			}
		}
	}

	notify($ERRORS{'DEBUG'}, 0, "generated domain XML:\n" . format_data($xml_hashref));
	return hash_to_xml_string($xml_hashref, 'domain');
}