mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-30 07:40:13 +01:00
431 lines
15 KiB
C#
431 lines
15 KiB
C#
|
/* 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.Net;
|
|||
|
using System.Reflection;
|
|||
|
using System.Threading;
|
|||
|
using XenAdmin;
|
|||
|
using XenAdmin.Core;
|
|||
|
using XenAdmin.Network;
|
|||
|
|
|||
|
|
|||
|
namespace XenAPI
|
|||
|
{
|
|||
|
partial class Task
|
|||
|
{
|
|||
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
|||
|
|
|||
|
public delegate task_status_type TaskStatusOp(Session session, string task);
|
|||
|
public delegate double TaskProgressOp(Session session, string task);
|
|||
|
public delegate Task TaskGetRecordOp(Session session, string task);
|
|||
|
|
|||
|
private static readonly string XapiExportPrefix = "Export of VM: ";
|
|||
|
|
|||
|
private static readonly Dictionary<string, string> Names = new Dictionary<string, string>();
|
|||
|
private static readonly Dictionary<string, string> Titles = new Dictionary<string, string>();
|
|||
|
static Task()
|
|||
|
{
|
|||
|
Names["VM.clean_reboot"] = Messages.ACTION_VM_REBOOTING;
|
|||
|
Names["VM.clean_shutdown"] = Messages.ACTION_VM_SHUTTING_DOWN;
|
|||
|
Names["VM.clone"] = Messages.ACTION_VM_COPYING;
|
|||
|
// Export is dealt with specially.
|
|||
|
// Names["VM.export"] = ...;
|
|||
|
Names["VM.hard_reboot"] = Messages.ACTION_VM_REBOOTING;
|
|||
|
Names["VM.hard_shutdown"] = Messages.ACTION_VM_SHUTTING_DOWN;
|
|||
|
Names["VM.migrate_send"] = Messages.ACTION_VM_MIGRATING;
|
|||
|
Names["VM.resume"] = Messages.ACTION_VM_RESUMING;
|
|||
|
Names["VM.resume_on"] = Messages.ACTION_VM_RESUMING;
|
|||
|
Names["VM.start"] = Messages.ACTION_VM_STARTING;
|
|||
|
Names["VM.start_on"] = Messages.ACTION_VM_STARTING;
|
|||
|
Names["VM.suspend"] = Messages.ACTION_VM_SUSPENDING;
|
|||
|
Names["VM.checkpoint"] = Messages.SNAPSHOTTING;
|
|||
|
Names["VM.snapshot"] = Messages.SNAPSHOTTING;
|
|||
|
|
|||
|
Names["VM import"] = Messages.IMPORTING;
|
|||
|
Names["Import of Zurich/Geneva style XVA"] = Messages.IMPORTING;
|
|||
|
|
|||
|
Titles["VM.clean_reboot"] = Messages.ACTION_VM_REBOOTING_TITLE;
|
|||
|
Titles["VM.clean_shutdown"] = Messages.ACTION_VM_SHUTTING_DOWN_TITLE;
|
|||
|
Titles["VM.clone"] = Messages.ACTION_VM_COPYING_TITLE_MEDDLING;
|
|||
|
// Export is dealt with specially.
|
|||
|
// Titles["VM.export"] = ...;
|
|||
|
Titles["VM.hard_reboot"] = Messages.ACTION_VM_REBOOTING_TITLE;
|
|||
|
Titles["VM.hard_shutdown"] = Messages.ACTION_VM_SHUTTING_DOWN_TITLE;
|
|||
|
Titles["VM.migrate_send"] = Messages.ACTION_VM_MIGRATING_TITLE;
|
|||
|
Titles["VM.resume"] = Messages.ACTION_VM_RESUMING_ON_TITLE;
|
|||
|
Titles["VM.resume_on"] = Messages.ACTION_VM_RESUMING_ON_TITLE;
|
|||
|
Titles["VM.start"] = Messages.ACTION_VM_STARTING_ON_TITLE;
|
|||
|
Titles["VM.start_on"] = Messages.ACTION_VM_STARTING_ON_TITLE;
|
|||
|
Titles["VM.suspend"] = Messages.ACTION_VM_SUSPENDING_TITLE;
|
|||
|
Titles["VM.checkpoint"] = Messages.ACTION_VM_SNAPSHOT_TITLE;
|
|||
|
Titles["VM.snapshot"] = Messages.ACTION_VM_SNAPSHOT_TITLE;
|
|||
|
|
|||
|
Titles["VM import"] = Messages.IMPORTVM_TITLE;
|
|||
|
Titles["Import of Zurich/Geneva style XVA"] = Messages.IMPORTVM_TITLE;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Try and run the delegate.
|
|||
|
/// If it fails with a web exception or invalid session, try again.
|
|||
|
/// Only retry 60 times.
|
|||
|
/// </summary>
|
|||
|
/// <param name="connection"></param>
|
|||
|
/// <param name="session"></param>
|
|||
|
/// <param name="f"></param>
|
|||
|
/// <param name="p"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static Object DoWithSessionRetry(IXenConnection connection, ref Session session, Delegate f, params object[] p)
|
|||
|
{
|
|||
|
int retries = 60;
|
|||
|
|
|||
|
while (true)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
object[] ps = new object[p.Length + 1];
|
|||
|
|
|||
|
ps[0] = session;
|
|||
|
|
|||
|
for (int i = 0; i < p.Length; i++)
|
|||
|
{
|
|||
|
ps[i + 1] = p[i];
|
|||
|
}
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
return f.DynamicInvoke(ps);
|
|||
|
}
|
|||
|
catch (TargetInvocationException exn)
|
|||
|
{
|
|||
|
throw exn.InnerException;
|
|||
|
}
|
|||
|
}
|
|||
|
catch (WebException we)
|
|||
|
{
|
|||
|
log.ErrorFormat("WebException in DoWithSessionRetry, retry {0}", retries);
|
|||
|
log.Error(we, we);
|
|||
|
|
|||
|
if (retries <= 0)
|
|||
|
throw;
|
|||
|
}
|
|||
|
catch (Failure failure)
|
|||
|
{
|
|||
|
log.ErrorFormat("Failure in DoWithSessionRetry, retry {0}", retries);
|
|||
|
log.Error(failure, failure);
|
|||
|
|
|||
|
if (retries <= 0)
|
|||
|
throw;
|
|||
|
|
|||
|
if (failure.ErrorDescription.Count < 1 || failure.ErrorDescription[0] != XenAPI.Failure.SESSION_INVALID)
|
|||
|
throw;
|
|||
|
}
|
|||
|
|
|||
|
Session newSession = connection.DuplicateSession();
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
// Try and logout the old session using the new session
|
|||
|
newSession.proxy.session_logout(session.uuid);
|
|||
|
}
|
|||
|
catch
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
session = newSession;
|
|||
|
|
|||
|
retries--;
|
|||
|
|
|||
|
Thread.Sleep(connection.ExpectDisruption ? 500 : 100);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// A list of OpaqueRefs of objects to which the current task applies. This is set
|
|||
|
/// by one XenCenter instance, and picked up by other ones.
|
|||
|
/// </summary>
|
|||
|
public List<string> AppliesTo
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
string s = Get(other_config, "applies_to");
|
|||
|
return s == null ? ExportAppliesTo() : new List<string>(s.Split(','));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private List<string> ExportAppliesTo()
|
|||
|
{
|
|||
|
VM vm = ExportAppliesToVM();
|
|||
|
if (vm == null)
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
List<string> result = new List<string>(1);
|
|||
|
result.Add(vm.opaque_ref);
|
|||
|
return result;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <returns>The VM that this Task applies to, or null if we don't know, or it's not an export.</returns>
|
|||
|
private VM ExportAppliesToVM()
|
|||
|
{
|
|||
|
if (name_label.StartsWith(XapiExportPrefix))
|
|||
|
{
|
|||
|
if (Connection == null)
|
|||
|
return null;
|
|||
|
string uuid = name_label.Substring(XapiExportPrefix.Length);
|
|||
|
return Connection.Cache.Find_By_Uuid<VM>(uuid);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void SetAppliesTo(Session session, string _task, List<string> applies_to)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
add_to_other_config(session, _task, "applies_to", string.Join(",", applies_to.ToArray()));
|
|||
|
}
|
|||
|
catch (XenAPI.Failure f)
|
|||
|
{
|
|||
|
if (f.ErrorDescription[0] == Failure.RBAC_PERMISSION_DENIED)
|
|||
|
{
|
|||
|
// Read only user without task.other_config rights - just ignore this request
|
|||
|
return;
|
|||
|
}
|
|||
|
throw f;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private const string XenCenterMeddlingActionTitleKey = "XenCenterMeddlingActionTitle";
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the meddling action title which is stored on the task
|
|||
|
/// </summary>
|
|||
|
public string MeddlingActionTitle
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return Get(other_config, XenCenterMeddlingActionTitleKey);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Set a title that can be looked up if this task is re-opened as a
|
|||
|
/// meddling action. This is stored on the server
|
|||
|
/// </summary>
|
|||
|
/// <param name="session"></param>
|
|||
|
/// <param name="task">task opaque ref</param>
|
|||
|
/// <param name="title">title to set</param>
|
|||
|
public static void SetMeddlingActionTitle(Session session, string task, string title)
|
|||
|
{
|
|||
|
if (!Helpers.TampaOrGreater(session.Connection))
|
|||
|
return;
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
add_to_other_config(session, task, XenCenterMeddlingActionTitleKey, title);
|
|||
|
}
|
|||
|
catch (Failure f)
|
|||
|
{
|
|||
|
if (f.ErrorDescription[0] == Failure.RBAC_PERMISSION_DENIED)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
throw;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public string XenCenterUUID
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return Get(other_config, "XenCenterUUID");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void SetXenCenterUUID(Session session, string _task, string uuid)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
add_to_other_config(session, _task, "XenCenterUUID", uuid);
|
|||
|
}
|
|||
|
catch (XenAPI.Failure f)
|
|||
|
{
|
|||
|
if (f.ErrorDescription[0] == Failure.RBAC_PERMISSION_DENIED)
|
|||
|
{
|
|||
|
// Read only user without task.other_config rights - just ignore this request
|
|||
|
return;
|
|||
|
}
|
|||
|
throw f;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void RemoveXenCenterUUID(Session session, string task)
|
|||
|
{
|
|||
|
if(session == null || String.IsNullOrEmpty(task))
|
|||
|
return;
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
remove_from_other_config(session, task, "XenCenterUUID");
|
|||
|
}
|
|||
|
catch (XenAPI.Failure f)
|
|||
|
{
|
|||
|
if (f.ErrorDescription[0] == Failure.RBAC_PERMISSION_DENIED)
|
|||
|
{
|
|||
|
// Read only user without task.other_config rights - just ignore this request
|
|||
|
return;
|
|||
|
}
|
|||
|
throw;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// True if the task should be completely hidden (i.e. Name == null).
|
|||
|
/// </summary>
|
|||
|
public bool Hidden
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (!String.IsNullOrEmpty(MeddlingActionTitle))
|
|||
|
return false;
|
|||
|
return Name == null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The friendly name to use for this task, or null
|
|||
|
/// if this task is to be completely hidden (e.g. SR.scan).
|
|||
|
/// </summary>
|
|||
|
public override string Name
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
string nl = name_label.Replace("Async.", "");
|
|||
|
return
|
|||
|
nl.StartsWith(XapiExportPrefix) ? ExportName():
|
|||
|
Names.ContainsKey(nl) ? Names[nl] :
|
|||
|
other_config.ContainsKey("ShowInXenCenter") ? name_label :
|
|||
|
null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public override string Description
|
|||
|
{
|
|||
|
get { return name_description; }
|
|||
|
}
|
|||
|
|
|||
|
private string ExportName()
|
|||
|
{
|
|||
|
VM vm = ExportAppliesToVM();
|
|||
|
return
|
|||
|
vm == null ?
|
|||
|
Messages.EXPORTING :
|
|||
|
string.Format(Messages.ACTION_EXPORT_TASK_NAME, vm.name_label);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The title to use for this task, or null
|
|||
|
/// if this task is to be completely hidden (e.g. SR.scan).
|
|||
|
/// </summary>
|
|||
|
public string Title
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
string nl = name_label.Replace("Async.", "");
|
|||
|
if (nl.StartsWith(XapiExportPrefix))
|
|||
|
return ExportName();
|
|||
|
if (AppliesTo != null && Titles.ContainsKey(nl))
|
|||
|
{
|
|||
|
try { return string.Format(Titles[nl], (object[])GetAppliesToNames().ToArray()); }
|
|||
|
catch { } // can crash if GetAppliesToNames() can't find enough names: CA-29493
|
|||
|
}
|
|||
|
if (Names.ContainsKey(nl))
|
|||
|
return Names[nl];
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private List<string> GetAppliesToNames()
|
|||
|
{
|
|||
|
|
|||
|
VM vm = null;
|
|||
|
Host host1 = null;
|
|||
|
Host host2 = null;
|
|||
|
foreach (string r in AppliesTo)
|
|||
|
{
|
|||
|
VM _vm = Connection.Resolve(new XenRef<VM>(r));
|
|||
|
if (_vm != null)
|
|||
|
{
|
|||
|
vm = _vm;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
Host _host = Connection.Resolve(new XenRef<Host>(r));
|
|||
|
if (_host != null)
|
|||
|
{
|
|||
|
if (host1 == null)
|
|||
|
host1 = _host;
|
|||
|
else
|
|||
|
host2 = _host;
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
List<string> result = new List<string>();
|
|||
|
|
|||
|
if (vm != null)
|
|||
|
result.Add(vm.name_label);
|
|||
|
if (host1 != null)
|
|||
|
result.Add(host1.name_label);
|
|||
|
if (host2 != null)
|
|||
|
result.Add(host2.name_label);
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
private static List<string> tasksToIgnore = new List<string>(new string[] { "SR.scan" });
|
|||
|
public bool IgnoreInCacheUpdate()
|
|||
|
{
|
|||
|
return tasksToIgnore.Contains(name_label);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|