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');
}