mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-20 23:46:03 +01:00
669 lines
25 KiB
C#
669 lines
25 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;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Reflection;
|
|||
|
using System.Xml;
|
|||
|
|
|||
|
using XenAPI;
|
|||
|
using XenAdmin.Network;
|
|||
|
using XenAdmin.ServerDBs.FakeAPI;
|
|||
|
using XenAdmin.Core;
|
|||
|
|
|||
|
namespace XenAdmin.ServerDBs
|
|||
|
{
|
|||
|
public class DbProxy : IInvocationHandler
|
|||
|
{
|
|||
|
private static readonly ClassGenerator generator = new ClassGenerator(typeof(Proxy));
|
|||
|
private static readonly Type proxyType = generator.Generate();
|
|||
|
public static readonly Proxy_Event dummyEvent = new Proxy_Event();
|
|||
|
public static readonly Dictionary<IXenConnection, DbProxy> proxys = new Dictionary<IXenConnection, DbProxy>();
|
|||
|
private static readonly object dicLock = new object();
|
|||
|
|
|||
|
public readonly Db db;
|
|||
|
public readonly List<Proxy_Event> eventsList = new List<Proxy_Event>();
|
|||
|
private readonly string url;
|
|||
|
public readonly IXenConnection connection;
|
|||
|
public volatile bool MarkToDisconnect;
|
|||
|
|
|||
|
/// Set this delegate to run inside the event loop.
|
|||
|
/// Throwing an specific exception type or Failure will simulate the termination
|
|||
|
/// of the event loop mid-loop
|
|||
|
public Action RunInEventLoop { get; private set; }
|
|||
|
|
|||
|
private static readonly Dictionary<string, List<string>> callVersions = new Dictionary<string, List<string>>();
|
|||
|
public bool IsSuperUser { get; private set; }
|
|||
|
|
|||
|
public void SetIsSuperUser(bool isSuperUser)
|
|||
|
{
|
|||
|
IsSuperUser = isSuperUser;
|
|||
|
}
|
|||
|
|
|||
|
public static void RemoveAll()
|
|||
|
{
|
|||
|
lock (dicLock)
|
|||
|
{
|
|||
|
foreach (KeyValuePair<IXenConnection, DbProxy> kvp in proxys)
|
|||
|
{
|
|||
|
kvp.Value.db.Dispose();
|
|||
|
kvp.Value.RunInEventLoop = delegate()
|
|||
|
{
|
|||
|
throw new Failure(Failure.SESSION_INVALID);
|
|||
|
};
|
|||
|
}
|
|||
|
proxys.Clear();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Initializes the <see cref="DbProxy"/> class.
|
|||
|
/// </summary>
|
|||
|
static DbProxy()
|
|||
|
{
|
|||
|
dummyEvent.class_ = "vtpm"; // This will cause XenCenter to ignore the event;
|
|||
|
|
|||
|
// load the api-version.xml file to detect whether a call is made for the wrong version of the server being simulated.
|
|||
|
|
|||
|
/*XmlDocument doc = new XmlDocument();
|
|||
|
doc.Load(Assembly.GetExecutingAssembly().GetManifestResourceStream("XenAdmin.TestResources.api-version.xml"));
|
|||
|
|
|||
|
foreach (XmlNode node in doc.SelectNodes("ApiVersionTool/version"))
|
|||
|
{
|
|||
|
string ver = node.Attributes["name"].Value;
|
|||
|
callVersions.Add(ver, new List<string>());
|
|||
|
|
|||
|
foreach (XmlNode n in node.SelectNodes("call"))
|
|||
|
{
|
|||
|
callVersions[ver].Add(n.Attributes["name"].Value);
|
|||
|
}
|
|||
|
}
|
|||
|
*/
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Generates a Proxy instance which implements <see cref="Proxy"/> for the specified <see cref="IXenConnection"/> and XML document.
|
|||
|
/// </summary>
|
|||
|
/// <param name="connection">The connection.</param>
|
|||
|
/// <param name="fileName">Name of the file.</param>
|
|||
|
/// <returns></returns>
|
|||
|
public static Proxy GetProxy(IXenConnection connection, string fileName)
|
|||
|
{
|
|||
|
return (Proxy)generator.CreateProxyInstance(proxyType, ProxyInstance(connection, fileName));
|
|||
|
}
|
|||
|
|
|||
|
private static IInvocationHandler ProxyInstance(IXenConnection connection, string fileName)
|
|||
|
{
|
|||
|
lock (dicLock)
|
|||
|
{
|
|||
|
if (!proxys.ContainsKey(connection))
|
|||
|
{
|
|||
|
proxys[connection] = new DbProxy(connection, fileName);
|
|||
|
}
|
|||
|
|
|||
|
return proxys[connection];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Initializes a new instance of the <see cref="DbProxy"/> class.
|
|||
|
/// </summary>
|
|||
|
/// <param name="connection">The connection.</param>
|
|||
|
/// <param name="url">The http url of filename to the XenServer status report XML document to be used.</param>
|
|||
|
public DbProxy(IXenConnection connection, string url)
|
|||
|
{
|
|||
|
Util.ThrowIfParameterNull(connection, "connection");
|
|||
|
Util.ThrowIfStringParameterNullOrEmpty(url, "url");
|
|||
|
|
|||
|
this.url = url;
|
|||
|
this.connection = connection;
|
|||
|
db = new Db(connection, url);
|
|||
|
}
|
|||
|
|
|||
|
#region IInvocationHandler Members
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public object ExecuteMethod(ProxyMethodInfo pmi, object[] args)
|
|||
|
{
|
|||
|
object obj;
|
|||
|
|
|||
|
if (pmi.FakeType == typeof(fakeUnknown))
|
|||
|
{
|
|||
|
obj = new fakeUnknown(pmi.TypeName, this);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
obj = Activator.CreateInstance(pmi.FakeType, this);
|
|||
|
}
|
|||
|
|
|||
|
object[] methodArgs = new object[pmi.FakeMethod.GetParameters().Length];
|
|||
|
for (int i = 0; i < methodArgs.Length; i++)
|
|||
|
{
|
|||
|
methodArgs[i] = args[i];
|
|||
|
}
|
|||
|
|
|||
|
switch (pmi.FakeMethodType)
|
|||
|
{
|
|||
|
case MethodType.Sync:
|
|||
|
return pmi.FakeMethod.Invoke(obj, methodArgs);
|
|||
|
|
|||
|
case MethodType.Async:
|
|||
|
Response<string> response = (Response<string>)pmi.FakeMethod.Invoke(obj, methodArgs);
|
|||
|
if (response.Status == ResponseTypes.SUCCESS)
|
|||
|
{
|
|||
|
return new Response<string>(CreateTask(DbProxy.TaskStatus.success, response.Value));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return new Response<string>(CreateTask(DbProxy.TaskStatus.failure, response.ErrorDescription));
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return new Response<String>(true, new String[] { Failure.INTERNAL_ERROR });
|
|||
|
}
|
|||
|
|
|||
|
public event EventHandler<DbProxyInvokingEventArgs> Invoking;
|
|||
|
|
|||
|
protected virtual void OnInvoking(DbProxyInvokingEventArgs e)
|
|||
|
{
|
|||
|
var handler = Invoking;
|
|||
|
|
|||
|
if (handler != null)
|
|||
|
{
|
|||
|
handler(this, e);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public object Invoke(string proxyMethodName, string returnType, params object[] args)
|
|||
|
{
|
|||
|
VerifyVersion(proxyMethodName);
|
|||
|
|
|||
|
ProxyMethodInfo pmi = ProxyMethodNameParser.Parse(proxyMethodName);
|
|||
|
|
|||
|
OnInvoking(new DbProxyInvokingEventArgs(pmi));
|
|||
|
|
|||
|
if (pmi.FakeType != null)
|
|||
|
{
|
|||
|
return ExecuteMethod(pmi, args);
|
|||
|
}
|
|||
|
|
|||
|
if (pmi.MethodName.StartsWith("set_"))
|
|||
|
{
|
|||
|
string fieldname = pmi.MethodName.Substring(4);
|
|||
|
|
|||
|
// Special case: set_memory_limits is not just editing a "memory_limits" field
|
|||
|
if (fieldname == "memory_limits")
|
|||
|
{
|
|||
|
EditObject(pmi.TypeName, (string)args[1], "memory_static_min", args[2]);
|
|||
|
EditObject(pmi.TypeName, (string)args[1], "memory_static_max", args[3]);
|
|||
|
EditObject(pmi.TypeName, (string)args[1], "memory_dynamic_min", args[4]);
|
|||
|
EditObject(pmi.TypeName, (string)args[1], "memory_dynamic_max", args[5]);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
EditObject(pmi.TypeName, (string)args[1], fieldname, args[2]);
|
|||
|
}
|
|||
|
|
|||
|
return new Response<string>("");
|
|||
|
}
|
|||
|
|
|||
|
switch (pmi.MethodName)
|
|||
|
{
|
|||
|
case "Url":
|
|||
|
return url;// "http://XenCenter.Simulator/";
|
|||
|
|
|||
|
case "get_record":
|
|||
|
{
|
|||
|
string uuid = (string)args[1];
|
|||
|
if (uuid.StartsWith("task")) // tasks are kept track of separately
|
|||
|
{
|
|||
|
return new Response<Proxy_Task>(task_get_record(uuid));
|
|||
|
}
|
|||
|
return get_record(pmi.TypeName, uuid, true);
|
|||
|
}
|
|||
|
|
|||
|
case "destroy":
|
|||
|
{
|
|||
|
string uuid = (string)args[1];
|
|||
|
if (uuid.StartsWith("task"))
|
|||
|
{
|
|||
|
task_destroy(uuid);
|
|||
|
return new Response<string>("");
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case "add_to_other_config":
|
|||
|
{
|
|||
|
string uuid = (string)args[1];
|
|||
|
if (!uuid.StartsWith("task")) // ignore other_config for tasks (used to keep track of meddling actions)
|
|||
|
{
|
|||
|
AddToDictionary(pmi.TypeName, (string)args[1], "other_config", args[2], args[3]);
|
|||
|
}
|
|||
|
return new Response<string>("");
|
|||
|
}
|
|||
|
|
|||
|
case "remove_from_other_config":
|
|||
|
RemoveFromDictionary(pmi.TypeName, (string)args[1], "other_config", args[2]);
|
|||
|
return new Response<string>("");
|
|||
|
|
|||
|
case "add_to_gui_config":
|
|||
|
AddToDictionary(pmi.TypeName, (string)args[1], "gui_config", args[2], args[3]);
|
|||
|
return new Response<string>("");
|
|||
|
|
|||
|
case "remove_from_gui_config":
|
|||
|
RemoveFromDictionary(pmi.TypeName, (string)args[1], "gui_config", args[2]);
|
|||
|
return new Response<string>("");
|
|||
|
|
|||
|
case "add_tags":
|
|||
|
AddToArray(pmi.TypeName, (string)args[1], "tags", args[2]);
|
|||
|
return new Response<string>("");
|
|||
|
|
|||
|
case "remove_tags":
|
|||
|
RemoveFromArray(pmi.TypeName, (string)args[1], "tags", args[2]);
|
|||
|
return new Response<string>("");
|
|||
|
|
|||
|
case "get_subject_information_from_identifier":
|
|||
|
Hashtable subjectInfo = new Hashtable();
|
|||
|
if ((string)args[1] == "SID1")
|
|||
|
{
|
|||
|
subjectInfo["subject-name"] = @"citrix\tu_one";
|
|||
|
subjectInfo["subject-displayname"] = "Test User 1";
|
|||
|
subjectInfo["subject-is-group"] = "false";
|
|||
|
}
|
|||
|
else if ((string)args[1] == "SID2")
|
|||
|
{
|
|||
|
subjectInfo["subject-name"] = @"citrix\tu_two";
|
|||
|
subjectInfo["subject-displayname"] = "Test User 2";
|
|||
|
subjectInfo["subject-is-group"] = "false";
|
|||
|
}
|
|||
|
else if ((string)args[1] == "SID3")
|
|||
|
{
|
|||
|
subjectInfo["subject-name"] = @"citrix\tg";
|
|||
|
subjectInfo["subject-displayname"] = "Test Group";
|
|||
|
subjectInfo["subject-is-group"] = "true";
|
|||
|
}
|
|||
|
return new Response<Object>(subjectInfo);
|
|||
|
|
|||
|
// For VBDEditPage.CalculateDevicePositions()
|
|||
|
case "get_allowed_vbd_devices":
|
|||
|
return new Response<String[]>(new String[] { "0", "1", "2", "3", "4", "5", "6", "7" });
|
|||
|
|
|||
|
case "retrieve_wlb_recommendations":
|
|||
|
case "retrieve_wlb_evacuate_recommendations":
|
|||
|
case "retrieve_wlb_configuration":
|
|||
|
Hashtable tbl = new Hashtable();
|
|||
|
return new Response<object>(tbl);
|
|||
|
|
|||
|
case "get_uncooperative_resident_vms":
|
|||
|
return new Response<string[]>(new string[0]);
|
|||
|
|
|||
|
// For HAWizard_Pages.AssignPriorities
|
|||
|
case "ha_compute_hypothetical_max_host_failures_to_tolerate":
|
|||
|
return new Response<string>("1");
|
|||
|
|
|||
|
case "get_ha_host_failures_to_tolerate":
|
|||
|
return new Response<string>("1");
|
|||
|
|
|||
|
// For EvacuateHostDialog and RollingUpgradeWizard
|
|||
|
case "get_vms_which_prevent_evacuation":
|
|||
|
return new Response<object>(new Hashtable());
|
|||
|
|
|||
|
// For RollingUpgradeWizard
|
|||
|
case "get_live":
|
|||
|
return new Response<bool>(true);
|
|||
|
|
|||
|
case "get_allowed_operations":
|
|||
|
return new Response<string[]>(new string[0]);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return new Response<String>(true, new String[] { Failure.INTERNAL_ERROR });
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
// Code to create fake asynchronous tasks that just return a known value
|
|||
|
|
|||
|
private static Dictionary<string, Proxy_Task> tasks = new Dictionary<string, Proxy_Task>();
|
|||
|
private readonly Random rand = new Random();
|
|||
|
|
|||
|
public enum TaskStatus { success, failure, pending }
|
|||
|
|
|||
|
// If success, use the next argument as the result;
|
|||
|
// if !success, use all the subsequent arguments as the error_info.
|
|||
|
public string CreateTask(TaskStatus status, params string[] result)
|
|||
|
{
|
|||
|
Proxy_Task task = new Proxy_Task();
|
|||
|
task.uuid = String.Format("task{0}", rand.Next());
|
|||
|
task.name_label = task.uuid;
|
|||
|
task.name_description = "";
|
|||
|
task.created = DateTime.Now;
|
|||
|
task.status = status.ToString();
|
|||
|
|
|||
|
if (status != TaskStatus.pending)
|
|||
|
{
|
|||
|
task.finished = DateTime.Now;
|
|||
|
task.progress = 100.0;
|
|||
|
|
|||
|
if (status == TaskStatus.success)
|
|||
|
{
|
|||
|
task.result = result[0];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
task.error_info = result;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
tasks.Add(task.uuid, task);
|
|||
|
return task.uuid;
|
|||
|
}
|
|||
|
|
|||
|
public Proxy_Task GetTask(string opaque_ref)
|
|||
|
{
|
|||
|
return tasks[opaque_ref];
|
|||
|
}
|
|||
|
|
|||
|
private static Proxy_Task task_get_record(string uuid)
|
|||
|
{
|
|||
|
Proxy_Task task;
|
|||
|
if (tasks.TryGetValue(uuid, out task))
|
|||
|
{
|
|||
|
return task;
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
private static bool task_destroy(string uuid)
|
|||
|
{
|
|||
|
return tasks.Remove(uuid);
|
|||
|
}
|
|||
|
|
|||
|
public enum EditTypes { Replace, AddToDict, RemoveFromDict, AddToArray, RemoveFromArray }
|
|||
|
|
|||
|
public void EditObject_(EditTypes editType, string typeName, string opaqueRef, string field, params object[] args)
|
|||
|
{
|
|||
|
// Edit the XML to have the new value
|
|||
|
edit_record(editType, typeName, opaqueRef, field, args);
|
|||
|
|
|||
|
// Make a Proxy object from the new XML
|
|||
|
Type proxyT = TypeCache.GetProxyType(typeName);
|
|||
|
object proxy = get_record(typeName, opaqueRef, false);
|
|||
|
|
|||
|
// Make a Proxy_Event representing this edit, and add it to the events queue
|
|||
|
eventsList.Add(MakeProxyEvent(typeName, opaqueRef, "mod", proxyT, proxy));
|
|||
|
}
|
|||
|
|
|||
|
private void EditObject(string typeName, string opaqueRef, string field, object new_value)
|
|||
|
{
|
|||
|
EditObject_(EditTypes.Replace, typeName, opaqueRef, field, new_value);
|
|||
|
}
|
|||
|
|
|||
|
private void AddToDictionary(string typeName, string opaqueRef, string field, object key, object new_value)
|
|||
|
{
|
|||
|
EditObject_(EditTypes.AddToDict, typeName, opaqueRef, field, key, new_value);
|
|||
|
}
|
|||
|
|
|||
|
private void RemoveFromDictionary(string typeName, string opaqueRef, string field, object key)
|
|||
|
{
|
|||
|
EditObject_(EditTypes.RemoveFromDict, typeName, opaqueRef, field, key);
|
|||
|
}
|
|||
|
|
|||
|
private void AddToArray(string typeName, string opaqueRef, string field, object value)
|
|||
|
{
|
|||
|
EditObject_(EditTypes.AddToArray, typeName, opaqueRef, field, value);
|
|||
|
}
|
|||
|
|
|||
|
private void RemoveFromArray(string typeName, string opaqueRef, string field, object value)
|
|||
|
{
|
|||
|
EditObject_(EditTypes.RemoveFromArray, typeName, opaqueRef, field, value);
|
|||
|
}
|
|||
|
|
|||
|
public static Proxy_Event MakeProxyEvent(string typeName, string opaqueRef, string operation, Type ProxyT, object proxy)
|
|||
|
{
|
|||
|
Proxy_Event ev = new Proxy_Event();
|
|||
|
ev.class_ = typeName;
|
|||
|
ev.opaqueRef = opaqueRef;
|
|||
|
ev.operation = operation;
|
|||
|
ev.snapshot = ProxyToHashtable(ProxyT, proxy);
|
|||
|
return ev;
|
|||
|
}
|
|||
|
|
|||
|
public static Hashtable ProxyToHashtable(Type proxyT, object proxy)
|
|||
|
{
|
|||
|
Hashtable table = new Hashtable();
|
|||
|
FieldInfo[] fields = proxyT.GetFields();
|
|||
|
foreach (FieldInfo field in fields)
|
|||
|
{
|
|||
|
table[field.Name] = field.GetValue(proxy);
|
|||
|
}
|
|||
|
return table;
|
|||
|
}
|
|||
|
|
|||
|
public Object get_record(String clazz, String opaqueRef, bool makeResponse)
|
|||
|
{
|
|||
|
Db.Table table = db.Tables[clazz];
|
|||
|
|
|||
|
Type type = table.XapiType;
|
|||
|
|
|||
|
if (!table.Rows.ContainsKey(opaqueRef))
|
|||
|
{
|
|||
|
if (makeResponse)
|
|||
|
{
|
|||
|
return typeof(Response<>).MakeGenericType(type).GetConstructor(new Type[] { typeof(bool), typeof(String[]) })
|
|||
|
.Invoke(new Object[] { true, new String[] { Failure.OBJECT_NO_LONGER_EXISTS, opaqueRef } });
|
|||
|
}
|
|||
|
|
|||
|
throw new Exception(Failure.OBJECT_NO_LONGER_EXISTS);
|
|||
|
}
|
|||
|
|
|||
|
Db.Row row = table.Rows[opaqueRef];
|
|||
|
Object result = Activator.CreateInstance(type);
|
|||
|
|
|||
|
foreach (string propName in row.Props.Keys)
|
|||
|
{
|
|||
|
FieldInfo info = type.GetField(propName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
|||
|
info.SetValue(result, row.Props[propName].XapiObjectValue);
|
|||
|
}
|
|||
|
|
|||
|
if (makeResponse)
|
|||
|
{
|
|||
|
return typeof(Response<>).MakeGenericType(type).GetConstructor(new Type[] { type }).Invoke(new Object[] { result });
|
|||
|
}
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
private void edit_record(EditTypes editType, string clazz, string opaqueRef, string field, params object[] args)
|
|||
|
{
|
|||
|
Db.Row row = db.Tables[clazz].Rows[opaqueRef];
|
|||
|
Db.Prop prop;
|
|||
|
if (row.Props.TryGetValue(field, out prop))
|
|||
|
{
|
|||
|
prop.XapiObjectValue = NewValue(editType, prop.XapiObjectValue, args);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
prop = new Db.Prop(row, field, NewValue(editType, null, args));
|
|||
|
row.Props[field] = prop;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
private object NewValue(EditTypes editType, object fieldBefore, object[] args)
|
|||
|
{
|
|||
|
switch (editType)
|
|||
|
{
|
|||
|
case EditTypes.Replace:
|
|||
|
return args[0];
|
|||
|
|
|||
|
case EditTypes.AddToDict:
|
|||
|
Hashtable ht = fieldBefore == null ? new Hashtable() : (Hashtable)fieldBefore;
|
|||
|
ht[args[0]] = args[1];
|
|||
|
return ht;
|
|||
|
|
|||
|
case EditTypes.RemoveFromDict:
|
|||
|
if (fieldBefore == null)
|
|||
|
{
|
|||
|
return new Hashtable();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ht = (Hashtable)fieldBefore;
|
|||
|
ht.Remove(args[0]);
|
|||
|
return ht;
|
|||
|
}
|
|||
|
|
|||
|
case EditTypes.AddToArray:
|
|||
|
// We only support string[] at the moment
|
|||
|
string[] arr = fieldBefore == null ? new string[0] : (string[])fieldBefore;
|
|||
|
List<string> list = new List<string>(arr);
|
|||
|
if (!list.Contains((string)args[0]))
|
|||
|
{
|
|||
|
list.Add((string)args[0]);
|
|||
|
}
|
|||
|
return list.ToArray();
|
|||
|
|
|||
|
case EditTypes.RemoveFromArray:
|
|||
|
if (fieldBefore == null)
|
|||
|
{
|
|||
|
return new string[0];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
arr = (string[])fieldBefore;
|
|||
|
list = new List<string>(arr);
|
|||
|
while (list.Contains((string)args[0]))
|
|||
|
{
|
|||
|
list.Remove((string)args[0]);
|
|||
|
}
|
|||
|
return list.ToArray();
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
System.Diagnostics.Trace.Assert(false);
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string GetRandomRef(string clazz)
|
|||
|
{
|
|||
|
string[] refs = new string[db.Tables[clazz].Rows.Keys.Count];
|
|||
|
db.Tables[clazz].Rows.Keys.CopyTo(refs, 0);
|
|||
|
|
|||
|
return refs[rand.Next(0, refs.Length)];
|
|||
|
}
|
|||
|
|
|||
|
public string CopyObject(string clazz, string opaque_ref)
|
|||
|
{
|
|||
|
if (!db.Tables[clazz].Rows.ContainsKey(opaque_ref))
|
|||
|
return Helper.NullOpaqueRef;
|
|||
|
Db.Row r = db.Tables[clazz].Rows[opaque_ref].CopyOf();
|
|||
|
string new_opaque_ref = CreateOpaqueRef();
|
|||
|
r.Props["uuid"].XapiObjectValue = Guid.NewGuid().ToString(); // this object needs a new uuid & ref
|
|||
|
db.Tables[clazz].Rows.Add(new_opaque_ref, r);
|
|||
|
return new_opaque_ref;
|
|||
|
}
|
|||
|
|
|||
|
public void SendCreateObject(string typeName, string opaqueRef)
|
|||
|
{
|
|||
|
// Make a Proxy object from the new XML
|
|||
|
Type proxyT = TypeCache.GetProxyType(typeName);
|
|||
|
object proxy = get_record(typeName, opaqueRef, false);
|
|||
|
|
|||
|
// Make a Proxy_Event representing this edit, and add it to the events queue
|
|||
|
eventsList.Add(MakeProxyEvent(typeName, opaqueRef, "add", proxyT, proxy));
|
|||
|
}
|
|||
|
|
|||
|
public void SendDestroyObject(string typeName, string opaqueRef)
|
|||
|
{
|
|||
|
// Make a Proxy object from the new XML
|
|||
|
Type proxyT = TypeCache.GetProxyType(typeName);
|
|||
|
|
|||
|
// Make a Proxy_Event representing this edit, and add it to the events queue
|
|||
|
eventsList.Add(MakeProxyEvent(typeName, opaqueRef, "del", proxyT, Activator.CreateInstance(proxyT)));
|
|||
|
}
|
|||
|
|
|||
|
public void SendModObject(string typeName, string opaqueRef)
|
|||
|
{
|
|||
|
// Make a Proxy object from the new XML
|
|||
|
Type proxyT = TypeCache.GetProxyType(typeName);
|
|||
|
object proxy = get_record(typeName, opaqueRef, false);
|
|||
|
|
|||
|
// Make a Proxy_Event representing this edit, and add it to the events queue
|
|||
|
eventsList.Add(MakeProxyEvent(typeName, opaqueRef, "mod", proxyT, proxy));
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Verifies that the specified call can be made with this version of server.
|
|||
|
/// </summary>
|
|||
|
private void VerifyVersion(string call)
|
|||
|
{
|
|||
|
string version = null;
|
|||
|
|
|||
|
foreach (string key in callVersions.Keys)
|
|||
|
{
|
|||
|
if (callVersions[key].Contains(call))
|
|||
|
{
|
|||
|
version = key;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (version == "george")
|
|||
|
{
|
|||
|
if (!Helpers.GeorgeOrGreater(connection))
|
|||
|
{
|
|||
|
throw new NotSupportedException("Server needs to be George or greater for this call.");
|
|||
|
}
|
|||
|
}
|
|||
|
else if (version == "mnr")
|
|||
|
{
|
|||
|
if (!Helpers.MidnightRideOrGreater(connection))
|
|||
|
{
|
|||
|
throw new NotSupportedException("Server needs to be Midnight Ride or greater for this call.");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string CreateOpaqueRef()
|
|||
|
{
|
|||
|
return string.Format("OpaqueRef:{0}", Guid.NewGuid());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|