mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-22 16:36:03 +01:00
7c0bc50b4a
Inc. Signed-off-by: Gabor Apati-Nagy<gabor.apati-nagy@citrix.com>
427 lines
15 KiB
C#
427 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.IO;
|
|
using System.Net;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Xml;
|
|
|
|
using Citrix.XenCenter;
|
|
|
|
using XenAdmin.Core;
|
|
|
|
namespace XenAdmin.XenSearch
|
|
{
|
|
public class SearchMarshalling
|
|
{
|
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
private const int CURRENT_SEARCH_MAJOR_VERSION = 2;
|
|
private const int CURRENT_SEARCH_MINOR_VERSION = 0;
|
|
|
|
private const String xmlns = "http://www.citrix.com/XenCenter/XenSearch/schema";
|
|
|
|
private static Dictionary<String, Type> subclasses = new Dictionary<String, Type>();
|
|
|
|
static SearchMarshalling()
|
|
{
|
|
subclasses["Query"] = typeof(Query);
|
|
|
|
foreach (Type t in Assembly.GetAssembly(typeof(QueryFilter)).GetTypes())
|
|
{
|
|
if (t.IsSubclassOf(typeof(QueryFilter)) && !t.IsAbstract)
|
|
subclasses[GetClassName(t)] = t;
|
|
}
|
|
|
|
foreach (Type t in Assembly.GetAssembly(typeof(Grouping)).GetTypes())
|
|
{
|
|
if (t.IsSubclassOf(typeof(Grouping)) && !t.IsAbstract)
|
|
subclasses[GetClassName(t)] = t;
|
|
}
|
|
}
|
|
|
|
internal static String GetClassName(object o)
|
|
{
|
|
return GetClassName(o.GetType());
|
|
}
|
|
|
|
private static String GetClassName(Type type)
|
|
{
|
|
return type.Name.Split(new char[] { '`' })[0];
|
|
}
|
|
|
|
private static Type GetType(XmlNode node)
|
|
{
|
|
Type type;
|
|
|
|
if (subclasses.ContainsKey(node.Name))
|
|
type = subclasses[node.Name];
|
|
else
|
|
type = Type.GetType(node.Name, true);
|
|
|
|
if(!type.IsGenericType)
|
|
return type;
|
|
|
|
Type genericType = PropertyAccessors.GetType(GetPropertyNames(node));
|
|
|
|
return type.MakeGenericType(genericType);
|
|
}
|
|
|
|
private static PropertyNames GetPropertyNames(XmlNode node)
|
|
{
|
|
return (PropertyNames)Enum.Parse(typeof(PropertyNames), node.Attributes["property"].Value);
|
|
}
|
|
|
|
public static Object FromXmlNode(XmlNode node)
|
|
{
|
|
try
|
|
{
|
|
Type type = GetType(node);
|
|
|
|
if (type == null)
|
|
return null;
|
|
|
|
ConstructorInfo ci = type.GetConstructor(new Type[] { typeof(XmlNode) });
|
|
|
|
return ci.Invoke(new Object[] { node });
|
|
}
|
|
catch (TargetInvocationException e)
|
|
{
|
|
log.ErrorFormat("Error unmarshalling object. Xml was: {0}", node.OuterXml);
|
|
log.Error(e, e);
|
|
throw e.InnerException;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
log.ErrorFormat("Error unmarshalling object. Xml was: {0}", node.OuterXml);
|
|
log.Error(e, e);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
internal static List<Search> LoadSearches(String xml)
|
|
{
|
|
try
|
|
{
|
|
List<Search> result = new List<Search>();
|
|
|
|
XmlDocument doc = new XmlDocument();
|
|
doc.XmlResolver = new BasicXMLResolver();
|
|
doc.LoadXml(xml);
|
|
|
|
foreach (XmlNode search in doc.GetElementsByTagName("Search"))
|
|
{
|
|
result.Add(LoadSearch(search));
|
|
}
|
|
|
|
// Just for backwards compat.
|
|
foreach (XmlNode search in doc.GetElementsByTagName("search"))
|
|
{
|
|
result.Add(LoadSearch(search));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
log.DebugFormat("Exception parsing xml '{0}'", xml.Substring(0, 10000));
|
|
log.Debug(e, e);
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static Search LoadSearch(String xml)
|
|
{
|
|
// The string "null" is the default in Orlando (inadvertently, I think).
|
|
if (xml == null || xml == "null")
|
|
return null;
|
|
|
|
try
|
|
{
|
|
XmlDocument doc = new XmlDocument();
|
|
doc.XmlResolver = new BasicXMLResolver();
|
|
doc.LoadXml(xml);
|
|
|
|
return LoadSearch(doc.FirstChild);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
log.DebugFormat("Exception parsing xml '{0}'", xml);
|
|
log.Debug(e, e);
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public class SearchVersionException : Exception
|
|
{
|
|
int majorVersion, currentMajor;
|
|
|
|
public SearchVersionException(int majorVersion, int currentMajor)
|
|
{
|
|
this.majorVersion = majorVersion;
|
|
this.currentMajor = currentMajor;
|
|
}
|
|
|
|
public override string Message
|
|
{
|
|
get
|
|
{
|
|
return String.Format("SearchVersionException: a search version {0} was loaded. Max allowed is {1}",
|
|
majorVersion, currentMajor);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static Search LoadSearch(XmlNode node)
|
|
{
|
|
String uuid = Helpers.GetXmlAttribute(node, "uuid");
|
|
String name = Helpers.GetXmlAttribute(node, "name");
|
|
int major_version = int.Parse(Helpers.GetXmlAttribute(node, "major_version"));
|
|
//int minor_version = int.Parse(Helpers.GetXmlAttribute(node, "minor_version"));
|
|
|
|
XmlAttribute expanded = node.Attributes["show_expanded"];
|
|
if (expanded == null)
|
|
{
|
|
// Backwards compat.
|
|
expanded = node.Attributes["showsearch"];
|
|
}
|
|
bool showQuery = expanded == null ? false : ParseBool(expanded.Value);
|
|
|
|
if (major_version > CURRENT_SEARCH_MAJOR_VERSION)
|
|
throw new SearchVersionException(major_version, CURRENT_SEARCH_MAJOR_VERSION);
|
|
|
|
Query query;
|
|
Object q = FromXmlNode(node.ChildNodes[0]);
|
|
switch (major_version)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
QueryFilter filter = q as QueryFilter;
|
|
if (filter == null)
|
|
throw new I18NException(I18NExceptionType.XmlInvalid, node.OuterXml);
|
|
query = new Query(null, filter);
|
|
break;
|
|
default:
|
|
query = q as Query;
|
|
if (query == null || query.QueryScope == null) // query.QueryFilter == null is allowed
|
|
throw new I18NException(I18NExceptionType.XmlInvalid, node.OuterXml);
|
|
break;
|
|
}
|
|
|
|
|
|
Grouping grouping = null;
|
|
Sort[] sorting = new Sort[] { };
|
|
List<KeyValuePair<String, int>> columns = null;
|
|
int i = 0;
|
|
foreach (XmlNode child in node.ChildNodes)
|
|
{
|
|
if (i != 0)
|
|
{
|
|
if (child.Name == "columns")
|
|
{
|
|
columns = LoadColumns(child);
|
|
}
|
|
else if (child.Name == "sorting")
|
|
{
|
|
sorting = LoadSorting(child);
|
|
}
|
|
else
|
|
{
|
|
grouping = (Grouping)FromXmlNode(child);
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
return new Search(query, grouping, showQuery, name, uuid, columns, sorting);
|
|
}
|
|
|
|
private static List<KeyValuePair<string, int>> LoadColumns(XmlNode columnsNode)
|
|
{
|
|
List<KeyValuePair<string, int>> columns = new List<KeyValuePair<string, int>>();
|
|
|
|
foreach (XmlNode node in columnsNode.ChildNodes)
|
|
{
|
|
String name = node.Attributes["name"].Value;
|
|
if (name == "state") // the "state" column no longer exists (CA-25116)
|
|
continue;
|
|
|
|
int width;
|
|
if (!Int32.TryParse(node.Attributes["width"].Value, out width))
|
|
continue;
|
|
|
|
columns.Add(new KeyValuePair<String, int>(name, Math.Abs(width)));
|
|
}
|
|
|
|
return columns;
|
|
}
|
|
|
|
private static Sort[] LoadSorting(XmlNode sortingNode)
|
|
{
|
|
Sort[] sorting = new Sort[sortingNode.ChildNodes.Count];
|
|
|
|
int i = 0;
|
|
foreach (XmlNode childNode in sortingNode.ChildNodes)
|
|
sorting[i++] = new Sort(childNode);
|
|
|
|
return sorting;
|
|
}
|
|
|
|
internal static string SearchesToXML(List<Search> searches)
|
|
{
|
|
XmlDocument document = new XmlDocument();
|
|
document.XmlResolver = new BasicXMLResolver();
|
|
|
|
document.AppendChild(document.CreateXmlDeclaration("1.0", Encoding.UTF8.WebName, null));
|
|
document.AppendChild(document.CreateDocumentType(Search.BrandedSearchKey, "-//" + Search.BrandedSearchKey.ToUpper() + "//DTD " + Search.BrandedSearchKey.ToUpper() + " 1//EN", Search.BrandedSearchKey + "-1.dtd", null));
|
|
|
|
XmlNode node = document.CreateElement("Searches");
|
|
AddAttribute(document, node, "xmlns", xmlns);
|
|
document.AppendChild(node);
|
|
|
|
foreach (Search s in searches)
|
|
{
|
|
node.AppendChild(SearchToXMLNode(document, s));
|
|
}
|
|
|
|
StringWriter sw = new StringWriter();
|
|
XmlTextWriter w = new XmlTextWriter(sw);
|
|
try
|
|
{
|
|
w.Formatting = Formatting.Indented;
|
|
w.Indentation = 4;
|
|
w.IndentChar = ' ';
|
|
|
|
document.WriteTo(w);
|
|
w.Flush();
|
|
|
|
return sw.ToString();
|
|
}
|
|
finally
|
|
{
|
|
w.Close();
|
|
sw.Close();
|
|
}
|
|
}
|
|
|
|
internal static string SearchToXML(Search search)
|
|
{
|
|
XmlDocument document = new XmlDocument();
|
|
XmlNode node = SearchToXMLNode(document, search);
|
|
return node.OuterXml;
|
|
}
|
|
|
|
private static XmlNode SearchToXMLNode(XmlDocument document, Search search)
|
|
{
|
|
XmlNode node = document.CreateElement("Search");
|
|
AddAttribute(document, node, "uuid", search.UUID);
|
|
AddAttribute(document, node, "name", search.Name);
|
|
AddAttribute(document, node, "major_version", CURRENT_SEARCH_MAJOR_VERSION);
|
|
AddAttribute(document, node, "minor_version", CURRENT_SEARCH_MINOR_VERSION);
|
|
if (search.ShowSearch)
|
|
AddAttribute(document, node, "show_expanded", true);
|
|
|
|
node.AppendChild(search.Query.ToXmlNode(document));
|
|
if (search.Grouping != null)
|
|
node.AppendChild(search.Grouping.ToXmlNode(document));
|
|
|
|
XmlNode columnsNode = GetColumnXML(document, search);
|
|
if (columnsNode != null)
|
|
node.AppendChild(columnsNode);
|
|
|
|
XmlNode sortingNode = GetSortingXML(document, search);
|
|
if (sortingNode != null)
|
|
node.AppendChild(sortingNode);
|
|
|
|
return node;
|
|
}
|
|
|
|
private static XmlNode GetColumnXML(XmlDocument doc, Search search)
|
|
{
|
|
if (search.Columns == null)
|
|
return null;
|
|
|
|
XmlNode columnsNode = doc.CreateElement("columns");
|
|
|
|
foreach (KeyValuePair<String, int> kvp in search.Columns)
|
|
{
|
|
XmlNode columnNode = doc.CreateElement("column");
|
|
|
|
AddAttribute(doc, columnNode, "name", kvp.Key);
|
|
AddAttribute(doc, columnNode, "width", kvp.Value.ToString());
|
|
|
|
columnsNode.AppendChild(columnNode);
|
|
}
|
|
|
|
return columnsNode;
|
|
}
|
|
|
|
private static XmlNode GetSortingXML(XmlDocument doc, Search search)
|
|
{
|
|
XmlNode columnsNode = doc.CreateElement("sorting");
|
|
|
|
foreach (Sort sort in search.Sorting)
|
|
columnsNode.AppendChild(sort.ToXmlNode(doc));
|
|
|
|
return columnsNode;
|
|
}
|
|
|
|
internal static void AddAttribute(XmlDocument document, XmlNode node, string n, string v)
|
|
{
|
|
XmlAttribute attr = document.CreateAttribute(n);
|
|
attr.Value = v;
|
|
node.Attributes.Append(attr);
|
|
}
|
|
|
|
internal static void AddAttribute(XmlDocument document, XmlNode node, string n, int v)
|
|
{
|
|
AddAttribute(document, node, n, v.ToString());
|
|
}
|
|
|
|
internal static void AddAttribute(XmlDocument document, XmlNode node, string n, bool v)
|
|
{
|
|
AddAttribute(document, node, n, v ? "yes" : "no");
|
|
}
|
|
|
|
internal static bool ParseBool(string v)
|
|
{
|
|
// "True" is just for backwards compat.
|
|
return v == "yes" || v == "True" ? true : false;
|
|
}
|
|
}
|
|
}
|