diff --git a/Anvil/Tools.pm b/Anvil/Tools.pm
index 317224e3..31f8045d 100644
--- a/Anvil/Tools.pm
+++ b/Anvil/Tools.pm
@@ -1202,6 +1202,7 @@ sub _set_paths
usermod => "/usr/sbin/usermod",
uuidgen => "/usr/bin/uuidgen",
virsh => "/usr/bin/virsh",
+ 'virt-install' => "/usr/bin/virt-install",
vgs => "/usr/sbin/vgs",
vgscan => "/usr/sbin/vgscan",
wc => "/usr/bin/wc",
diff --git a/share/words.xml b/share/words.xml
index c82eea31..7e14d23a 100644
--- a/share/words.xml
+++ b/share/words.xml
@@ -287,6 +287,12 @@ Output (if any):
It appears that the initial forced primary role to initialize the new DRBD resource failed. Expected the return code '0' but got: [#!variable!return_code!#]. The command returned: [#!variable!output!#].
The logical volume behind the resource: [#!variable!resource!#] existed, and started the resource has the disk state 'diskless'. This is likely because the LV doesn't have DRBD meta-data. We can't (safely) create it. Please either remove the LV backing this resource or create the meta data manually.
Failed to make the resource: [#!variable!resource!#] disk state to 'UpToDate'. After attempt, the disk state is: [#!variable!disk_state!#].
+ No operating system type was found for the server: [#!variable!server_name!#] in the job: [#!variable!job_uuid!#].
+ The call to create the server appears to have failed. The attempt to parse the server's definition failed. The command was run as a background process so exact error details are not available here. Please check the logs for more details. The call used to create the server was:
+====
+#!variable!shell_call!#
+====
+ The call to create the new server appears to have failed. It hasn't shown up as running after 10 seconds. The status, if any, was last seen as: [#!variable!status!#].
@@ -600,6 +606,15 @@ It should be provisioned in the next minute or two.
-=] OS Short List
* Please enter an OS key that is closest to your target OS. Run 'osinfo-query os' for a full list.
Optimize for: .. [#!variable!os!#]
+ Ready to provision the server! Please be patient, this could take a moment. The call to create the server will be:
+====
+#!variable!shell_call!#
+====
+ Provision call made, waiting for the server to start...
+ Started! Verifying that it looks good and adding it to the database.
+ Done! The server should now be booting. Connect now and finish the OS install.
+ The resource: [#!variable!resource!#] is now up.
+ We're the peer for this new server, and so we're now done. The other node will complete the server's install momentarily.
Starting: [#!variable!program!#].
diff --git a/tools/anvil-provision-server b/tools/anvil-provision-server
index 81e9b251..7ab4a87e 100755
--- a/tools/anvil-provision-server
+++ b/tools/anvil-provision-server
@@ -255,7 +255,19 @@ sub run_jobs
startup_resource($anvil);
# If we're here, we can finally craft the 'virt-install' call!.
- provision_server($anvil);
+ if ($anvil->data->{job}{peer_mode})
+ {
+ # The peer is done, it'll pick up the XML definition when ScanCore runs
+ $anvil->Job->update_progress({
+ progress => 100,
+ message => "job_0204",
+ });
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "job_0204"});
+ }
+ else
+ {
+ provision_server($anvil);
+ }
return(0);
}
@@ -265,8 +277,183 @@ sub provision_server
{
my ($anvil) = @_;
- my $shell_call = "";
+ # The logic behind this is explained here: https://libvirt.org/formatdomain.html#elementsTime
+ my $server = $anvil->data->{job}{server_name};
+ my $clock_offset = $anvil->data->{job}{os} =~ /win/i ? "localtime" : "utc";
+ my $say_memory = sprintf("%.0f", ($anvil->data->{job}{ram} /= (2 ** 20)));
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ clock_offset => $clock_offset,
+ say_memory => $say_memory,
+ }});
+
+ ### TODO: Support user-selected IFN. For now, we hard-code it to 'ifn_bridge1'
+ ### Support disk images (ie: sysprep) via '--import'. The device used for booting is the first device specified via "--disk"
+ ### Consider support for TPM, RNG and watchdog devices
+ my $shell_call = $anvil->data->{path}{exe}{'virt-install'}." --connect qemu:///system \\\n";
+ $shell_call .= "--name ".$server." \\\n";
+ $shell_call .= " --os-variant ".$anvil->data->{job}{os}." \\\n";
+ $shell_call .= " --memory ".$say_memory." \\\n";
+ $shell_call .= " --events on_poweroff=destroy,on_reboot=restart \\\n";
+ $shell_call .= " --vcpus ".$anvil->data->{job}{cpu_cores}.",sockets=1,cores=".$anvil->data->{job}{cpu_cores}." \\\n";
+ $shell_call .= " --cpu host \\\n";
+ $shell_call .= " --network bridge=ifn1_bridge1,model=virtio \\\n";
+ $shell_call .= " --graphics spice \\\n";
+ $shell_call .= " --sound ich9 \\\n";
+ $shell_call .= " --clock offset=".$clock_offset." \\\n"; # We may want to support ',rtc_tickpolicy=catchup'
+ $shell_call .= " --boot menu=on \\\n";
+ $shell_call .= " --disk path=/dev/drbd/by-res/".$server."/0,target.bus=virtio,driver.io=threads,cache=writeback,driver.discard=unmap,boot.order=1 \\\n";
+ $shell_call .= " --disk path=".$anvil->data->{job}{install_iso_path}.",device=cdrom,shareable=on,boot.order=2 \\\n";
+ if ($anvil->data->{job}{driver_iso_path})
+ {
+ $shell_call .= " --disk path=".$anvil->data->{job}{driver_iso_path}.",device=cdrom,shareable=on,boot.order=3 --force \\\n";
+ }
+ $shell_call .= " --noautoconsole --wait -1 > /var/log/anvil-server_".$server.".log\n";
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
+
+ $anvil->Job->update_progress({
+ progress => 70,
+ message => "job_0199,!!shell_call!".$shell_call."!!",
+ });
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "job_0199", variables => { shell_call => $shell_call }});
+
+ # Call as a background process.
+ my ($handle, $return_code) = $anvil->System->call({
+ background => 1,
+ shell_call => $shell_call,
+ stdout_file => "/var/log/anvil_server_".$server.".stdout",
+ stderr_file => "/var/log/anvil_server_".$server.".stderr",
+ });
+ my $pid = $handle->pid();
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ pid => $pid,
+ return_code => $return_code,
+ }});
+
+ $anvil->Job->update_progress({
+ progress => 80,
+ message => "job_0200",
+ });
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "job_0200"});
+
+ # Loop for up to 10 seconds waiting to see the server start running.
+ my $wait_until = time + 10;
+ my $waiting = 1;
+ my $status = "";
+ while($waiting)
+ {
+ my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." list --all"});
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ output => $output,
+ return_code => $return_code,
+ }});
+
+ foreach my $line (split/\n/, $output)
+ {
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
+ if ($line =~ /.*?$server\s+(.*)$/)
+ {
+ $status = $1;
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { status => $status }});
+ if ($status eq "running")
+ {
+ $waiting = 0;
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
+ }
+ }
+ if (($waiting) && (time > $wait_until))
+ {
+ # Didn't boot, give up
+ $waiting = 0;
+ $anvil->Job->update_progress({
+ progress => 100,
+ message => "error_0210,!!status!".$status."!!",
+ job_status => "failed",
+ });
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0210", variables => { status => $status }});
+ $anvil->nice_exit({exit_code => 1});
+ }
+ sleep 1;
+ }
+ }
+ $anvil->Job->update_progress({
+ progress => 90,
+ message => "job_0201",
+ });
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "job_0201"});
+
+ # Dump the server's XML.
+ (my $server_definition_xml, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." dumpxml ".$server});
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ server_definition_xml => $server_definition_xml,
+ return_code => $return_code,
+ }});
+
+ my $problem = $anvil->Server->parse_definition({
+ server => $server,
+ source => "from_virsh",
+ definition => $server_definition_xml,
+ });
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
+ if ($problem)
+ {
+ # Something went wrong with the provision
+ $anvil->Job->update_progress({
+ progress => 100,
+ message => "error_0209,!!shell_call!".$shell_call."!!",
+ job_status => "failed",
+ });
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0209", variables => { shell_call => $shell_call }});
+ $anvil->nice_exit({exit_code => 1});
+ }
+
+ # Write out the definition file.
+ my $definition_file = $anvil->data->{path}{directories}{shared}{definitions}."/".$server.".xml";
+ $problem = $anvil->Storage->write_file({
+ overwrite => 1,
+ backup => 1,
+ file => $definition_file,
+ body => $server_definition_xml,
+ mode => "0644",
+ });
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
+
+ # Add the server to the 'servers' table
+ my $anvil_uuid = $anvil->data->{job}{anvil_uuid};
+ my $short_host_name = $anvil->Get->short_host_name;
+ my $server_uuid = $anvil->data->{server}{$short_host_name}{$server}{"from_virsh"}{info}{uuid};
+ my $got_server_uuid = $anvil->Database->insert_or_update_servers({
+ server_uuid => $server_uuid,
+ server_name => $server,
+ server_state => "running",
+ server_anvil_uuid => $anvil_uuid,
+ server_host_uuid => $anvil->Get->host_uuid,
+ server_ram_in_use => $anvil->data->{job}{ram},
+ server_configured_ram => $anvil->data->{job}{ram},
+ server_boot_time => time,
+ });
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { got_server_uuid => $got_server_uuid }});
+
+ # Save the definition.
+ my $server_definition_uuid = $anvil->Database->insert_or_update_server_definitions({
+ server_definition_server_uuid => $got_server_uuid,
+ server_definition_xml => $server_definition_xml,
+ });
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_definition_uuid => $server_definition_uuid }});
+
+ # Undefine the server
+ (my $output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{virsh}." undefine ".$server});
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ output => $output,
+ return_code => $return_code,
+ }});
+
+ # Done!
+ $anvil->Job->update_progress({
+ progress => 100,
+ message => "job_0202",
+ });
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "job_0202"});
return(0);
}
@@ -486,9 +673,9 @@ sub startup_resource
$anvil->Job->update_progress({
progress => 60,
- message => "job_0192,!!resource!".$anvil->data->{job}{server_name}."!!",
+ message => "job_0203,!!resource!".$anvil->data->{job}{server_name}."!!",
});
- $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "job_0192", variables => { resource => $anvil->data->{job}{server_name} }});
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "job_0203", variables => { resource => $anvil->data->{job}{server_name} }});
return(0);
}
@@ -755,7 +942,7 @@ sub check_drbd_minor_and_port
message => "error_0199,!!storage_group_uuid!".$anvil->data->{job}{storage_group_uuid}."!!",
job_status => "failed",
});
- $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0197", variables => { storage_group_uuid => $anvil->data->{job}{storage_group_uuid} }});
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0199", variables => { storage_group_uuid => $anvil->data->{job}{storage_group_uuid} }});
$anvil->nice_exit({exit_code => 1});
}
@@ -863,6 +1050,11 @@ sub parse_job_data
$anvil->data->{job}{drbd_tcp_port} = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'job::drbd_tcp_port' => $anvil->data->{job}{drbd_tcp_port} }});
}
+ if ($line =~ /os=(.*)$/)
+ {
+ $anvil->data->{job}{os} = $1;
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'job::os' => $anvil->data->{job}{os} }});
+ }
}
# We need a server name and storage group UUID regardless of which mode we're in.
@@ -1009,7 +1201,14 @@ sub parse_job_data
install_iso_uuid => $install_iso_uuid,
install_iso => $install_iso,
}});
- if (not -e $install_iso)
+ if (-e $install_iso)
+ {
+ $anvil->data->{job}{install_iso_path} = $install_iso;
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ 'job::install_iso_path' => $anvil->data->{job}{install_iso_path},
+ }});
+ }
+ else
{
$anvil->Job->update_progress({
progress => 100,
@@ -1023,11 +1222,19 @@ sub parse_job_data
$anvil->nice_exit({exit_code => 1});
}
# Driver disc is optional.
+ $anvil->data->{new_server}{driver_iso_path} = "";
if ($anvil->data->{job}{driver_iso_uuid})
{
my $driver_iso_uuid = $anvil->data->{job}{driver_iso_uuid};
my $driver_iso = $anvil->data->{files}{file_uuid}{$driver_iso_uuid}{file_directory}."/".$anvil->data->{files}{file_uuid}{$driver_iso_uuid}{file_name};
- if (not -e $driver_iso)
+ if (-e $driver_iso)
+ {
+ $anvil->data->{job}{driver_iso_path} = $driver_iso;
+ $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
+ 'job::driver_iso_path' => $anvil->data->{job}{driver_iso_path},
+ }});
+ }
+ else
{
$anvil->Job->update_progress({
progress => 100,
@@ -1041,6 +1248,20 @@ sub parse_job_data
$anvil->nice_exit({exit_code => 1});
}
}
+ if (not $anvil->data->{job}{os})
+ {
+ # No requested RAM
+ $anvil->Job->update_progress({
+ progress => 100,
+ message => "error_0208,!!server_name!".$anvil->data->{job}{server_name}."!!,!!job_uuid!".$anvil->data->{switches}{'job-uuid'}."!!",
+ job_status => "failed",
+ });
+ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0208", variables => {
+ server_name => $anvil->data->{job}{server_name},
+ job_uuid => $anvil->data->{switches}{'job-uuid'},
+ }});
+ $anvil->nice_exit({exit_code => 1});
+ }
}
return(0);
@@ -1847,7 +2068,6 @@ sub interactive_ask_server_os
{
my ($anvil, $terminal) = @_;
- ### TODO: Left off here, use the short list 'sys::servers::os_short_list'
my $words_file = $anvil->data->{path}{words}{'words.xml'};
my $language = $anvil->Words->language;
my $os_list = "";