/* Copyright (c) Cloud Software Group, Inc.
*
* 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 XenAdmin.Network;
using XenAPI;
using XenAdmin.Core;
using System.Xml;
namespace XenAdmin.CustomFields
{
///
/// Provide custom fields management support for VMs. The coordinator list of custom fields will be
/// maintained in the pool class using the same conventions as the tags implementation (see
/// XenAdmin.XenSearch.Tags). When persisting the label-value pairs in the VMs, the
/// following key/value convention will be used:
/// "XenCenter.CustomFields.foo1" value
/// "XenCenter.CustomFields.foo2" value
///
public static class CustomFieldsManager
{
#region These functions deal with caching the list of custom fields
private static readonly CustomFieldsCache customFieldsCache = new CustomFieldsCache();
private const String CUSTOM_FIELD_DELIM = ".";
public const String CUSTOM_FIELD_BASE_KEY = "XenCenter.CustomFields";
public const String CUSTOM_FIELD = "CustomField:";
public static event Action CustomFieldsChanged;
static CustomFieldsManager()
{
OtherConfigAndTagsWatcher.GuiConfigChanged += OtherConfigAndTagsWatcher_GuiConfigChanged;
}
private static void OtherConfigAndTagsWatcher_GuiConfigChanged()
{
InvokeHelper.AssertOnEventThread();
customFieldsCache.RecalculateCustomFields();
OnCustomFieldsChanged();
}
private static void OnCustomFieldsChanged()
{
Action handler = CustomFieldsChanged;
if (handler != null)
{
handler();
}
}
#endregion
#region These functions deal with custom field definitions on the pool object
public static List GetCustomFields()
{
return customFieldsCache.GetCustomFields();
}
public static List GetCustomFields(IXenConnection connection)
{
return customFieldsCache.GetCustomFields(connection);
}
/// The CustomFieldDefinition with the given name, or null if none is found.
public static CustomFieldDefinition GetCustomFieldDefinition(string name)
{
foreach (CustomFieldDefinition d in GetCustomFields())
{
if (d.Name == name)
return d;
}
return null;
}
public static void RemoveCustomField(Session session, IXenConnection connection, CustomFieldDefinition definition)
{
List customFields = customFieldsCache.GetCustomFields(connection);
if (customFields.Remove(definition))
{
SaveCustomFields(session, connection, customFields);
// theoretically any object type with other_config may have a custom field set,
// but the object types more likely to have one are those shown on the treeview,
// i.e. pools, hosts, VMs (including snapshots and templates), SRs, VDIs, and networks
string customFieldKey = GetCustomFieldKey(definition);
foreach (var pool in connection.Cache.Pools)
{
if (pool.other_config.ContainsKey(customFieldKey))
Pool.remove_from_other_config(session, pool.opaque_ref, customFieldKey);
}
foreach (var host in connection.Cache.Hosts)
{
if (host.other_config.ContainsKey(customFieldKey))
Host.remove_from_other_config(session, host.opaque_ref, customFieldKey);
}
foreach (var vm in connection.Cache.VMs)
{
if (vm.other_config.ContainsKey(customFieldKey))
VM.remove_from_other_config(session, vm.opaque_ref, customFieldKey);
}
foreach (var sr in connection.Cache.SRs)
{
if (sr.other_config.ContainsKey(customFieldKey))
SR.remove_from_other_config(session, sr.opaque_ref, customFieldKey);
}
foreach (var vdi in connection.Cache.VDIs)
{
if (vdi.other_config.ContainsKey(customFieldKey))
VDI.remove_from_other_config(session, vdi.opaque_ref, customFieldKey);
}
foreach (var network in connection.Cache.Networks)
{
if (network.other_config.ContainsKey(customFieldKey))
XenAPI.Network.remove_from_other_config(session, network.opaque_ref, customFieldKey);
}
}
}
public static void AddCustomField(Session session, IXenConnection connection, CustomFieldDefinition customField)
{
List customFields = customFieldsCache.GetCustomFields(connection);
if (!customFields.Contains(customField))
{
customFields.Add(customField);
SaveCustomFields(session, connection, customFields);
}
}
private static String GetCustomFieldDefinitionXML(List customFieldDefinitions)
{
XmlDocument doc = new XmlDocument();
XmlNode parentNode = doc.CreateElement("CustomFieldDefinitions");
doc.AppendChild(parentNode);
foreach (CustomFieldDefinition customFieldDefinition in customFieldDefinitions)
{
parentNode.AppendChild(customFieldDefinition.ToXmlNode(doc));
}
return doc.OuterXml;
}
#endregion
#region These functions deal with the custom fields themselves
public static string GetCustomFieldKey(CustomFieldDefinition customFieldDefinition)
{
return CUSTOM_FIELD_BASE_KEY + CUSTOM_FIELD_DELIM + customFieldDefinition.Name;
}
private static void SaveCustomFields(Session session, IXenConnection connection, List customFields)
{
Pool pool = Helpers.GetPoolOfOne(connection);
if (pool != null)
{
string customFieldXML = GetCustomFieldDefinitionXML(customFields);
Pool.remove_from_gui_config(session, pool.opaque_ref, CUSTOM_FIELD_BASE_KEY);
Pool.add_to_gui_config(session, pool.opaque_ref, CUSTOM_FIELD_BASE_KEY, customFieldXML);
}
}
public static List CustomFieldValues(IXenObject o)
{
//Program.AssertOnEventThread();
List customFields = new List();
Dictionary otherConfig = GetOtherConfigCopy(o);
if (otherConfig != null)
{
foreach (CustomFieldDefinition customFieldDefinition in customFieldsCache.GetCustomFields(o.Connection))
{
string customFieldKey = GetCustomFieldKey(customFieldDefinition);
if (!otherConfig.ContainsKey(customFieldKey) || otherConfig[customFieldKey] == String.Empty)
{
continue;
}
object value = ParseValue(customFieldDefinition.Type, otherConfig[customFieldKey]);
if (value != null)
{
customFields.Add(new CustomField(customFieldDefinition, value));
}
}
}
return customFields;
}
// The same as CustomFieldValues(), but with each custom field unwound into an array
public static List