/* 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.ComponentModel; using System.Reflection; using XenAPI; using XenAdmin.Core; using XenAdmin.Model; namespace XenAdmin.Network { public class Cache : ICache { #pragma warning disable 0414 // keep sorted please private readonly ChangeableDictionary<XenRef<Bond>, Bond> _bond = new ChangeableDictionary<XenRef<Bond>, Bond>(); private readonly ChangeableDictionary<XenRef<Blob>, Blob> _blob = new ChangeableDictionary<XenRef<Blob>, Blob>(); private readonly ChangeableDictionary<XenRef<XenAPI.Console>, XenAPI.Console> _console = new ChangeableDictionary<XenRef<XenAPI.Console>, XenAPI.Console>(); private readonly ChangeableDictionary<XenRef<Folder>, Folder> _folders = new ChangeableDictionary<XenRef<Folder>, Folder>(); private readonly ChangeableDictionary<XenRef<GPU_group>, GPU_group> _gpu_groups = new ChangeableDictionary<XenRef<GPU_group>, GPU_group>(); private readonly ChangeableDictionary<XenRef<Host>, Host> _host = new ChangeableDictionary<XenRef<Host>, Host>(); private readonly ChangeableDictionary<XenRef<Host_cpu>, Host_cpu> _host_cpu = new ChangeableDictionary<XenRef<Host_cpu>, Host_cpu>(); private readonly ChangeableDictionary<XenRef<Host_crashdump>, Host_crashdump> _host_crashdump = new ChangeableDictionary<XenRef<Host_crashdump>, Host_crashdump>(); private readonly ChangeableDictionary<XenRef<Host_metrics>, Host_metrics> _host_metrics = new ChangeableDictionary<XenRef<Host_metrics>, Host_metrics>(); private readonly ChangeableDictionary<XenRef<Host_patch>, Host_patch> _host_patch = new ChangeableDictionary<XenRef<Host_patch>, Host_patch>(); private readonly ChangeableDictionary<XenRef<Message>, Message> _message = new ChangeableDictionary<XenRef<Message>, Message>(); private readonly ChangeableDictionary<XenRef<XenAPI.Network>, XenAPI.Network> _network = new ChangeableDictionary<XenRef<XenAPI.Network>, XenAPI.Network>(); private readonly ChangeableDictionary<XenRef<PBD>, PBD> _pbd = new ChangeableDictionary<XenRef<PBD>, PBD>(); private readonly ChangeableDictionary<XenRef<PCI>, PCI> _pcis = new ChangeableDictionary<XenRef<PCI>, PCI>(); private readonly ChangeableDictionary<XenRef<PGPU>, PGPU> _pgpu = new ChangeableDictionary<XenRef<PGPU>, PGPU>(); private readonly ChangeableDictionary<XenRef<PIF>, PIF> _pif = new ChangeableDictionary<XenRef<PIF>, PIF>(); private readonly ChangeableDictionary<XenRef<PIF_metrics>, PIF_metrics> _pif_metrics = new ChangeableDictionary<XenRef<PIF_metrics>, PIF_metrics>(); private readonly ChangeableDictionary<XenRef<Pool>, Pool> _pool = new ChangeableDictionary<XenRef<Pool>, Pool>(); private readonly ChangeableDictionary<XenRef<Pool_patch>, Pool_patch> _pool_patch = new ChangeableDictionary<XenRef<Pool_patch>, Pool_patch>(); private readonly ChangeableDictionary<XenRef<Role>, Role> _role = new ChangeableDictionary<XenRef<Role>, Role>(); private readonly ChangeableDictionary<XenRef<SM>, SM> _sm = new ChangeableDictionary<XenRef<SM>, SM>(); private readonly ChangeableDictionary<XenRef<SR>, SR> _sr = new ChangeableDictionary<XenRef<SR>, SR>(); private readonly ChangeableDictionary<XenRef<Subject>, Subject> _subject = new ChangeableDictionary<XenRef<Subject>, Subject>(); private readonly ChangeableDictionary<XenRef<Task>, Task> _task = new ChangeableDictionary<XenRef<Task>, Task>(); private readonly ChangeableDictionary<XenRef<Tunnel>, Tunnel> _tunnel = new ChangeableDictionary<XenRef<Tunnel>, Tunnel>(); private readonly ChangeableDictionary<XenRef<VBD>, VBD> _vbd = new ChangeableDictionary<XenRef<VBD>, VBD>(); private readonly ChangeableDictionary<XenRef<VBD_metrics>, VBD_metrics> _vbd_metrics = new ChangeableDictionary<XenRef<VBD_metrics>, VBD_metrics>(); private readonly ChangeableDictionary<XenRef<VDI>, VDI> _vdi = new ChangeableDictionary<XenRef<VDI>, VDI>(); private readonly ChangeableDictionary<XenRef<VGPU>, VGPU> _vgpu = new ChangeableDictionary<XenRef<VGPU>, VGPU>(); private readonly ChangeableDictionary<XenRef<VGPU_type>, VGPU_type> _vgpu_types = new ChangeableDictionary<XenRef<VGPU_type>, VGPU_type>(); private readonly ChangeableDictionary<XenRef<VIF>, VIF> _vif = new ChangeableDictionary<XenRef<VIF>, VIF>(); private readonly ChangeableDictionary<XenRef<VIF_metrics>, VIF_metrics> _vif_metrics = new ChangeableDictionary<XenRef<VIF_metrics>, VIF_metrics>(); private readonly ChangeableDictionary<XenRef<VLAN>, VLAN> _vlan = new ChangeableDictionary<XenRef<VLAN>, VLAN>(); private readonly ChangeableDictionary<XenRef<VM>, VM> _vm = new ChangeableDictionary<XenRef<VM>, VM>(); private readonly ChangeableDictionary<XenRef<VM_metrics>, VM_metrics> _vm_metrics = new ChangeableDictionary<XenRef<VM_metrics>, VM_metrics>(); private readonly ChangeableDictionary<XenRef<VM_guest_metrics>, VM_guest_metrics> _vm_guest_metrics = new ChangeableDictionary<XenRef<VM_guest_metrics>, VM_guest_metrics>(); private readonly ChangeableDictionary<XenRef<VMPP>, VMPP> _vmmp = new ChangeableDictionary<XenRef<VMPP>, VMPP>(); private readonly ChangeableDictionary<XenRef<VM_appliance>, VM_appliance> _vm_appliance = new ChangeableDictionary<XenRef<VM_appliance>, VM_appliance>(); private readonly ChangeableDictionary<XenRef<Crashdump>, Crashdump> _crashdump = new ChangeableDictionary<XenRef<Crashdump>, Crashdump>(); #pragma warning restore 0414 private readonly Dictionary<Type, IDictionary> dictionaries = new Dictionary<Type, IDictionary>(); public Cache() { foreach (FieldInfo f in GetType().GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic)) { if (f.FieldType.Name.StartsWith("ChangeableDictionary")) { dictionaries.Add(f.FieldType.GetGenericArguments()[0].GetGenericArguments()[0], (IDictionary)f.GetValue(this)); } } } public Bond[] Bonds { get { return contents(_bond); } } public VMPP[] VMPPs { get { return contents(_vmmp); } } public VM_appliance[] VM_appliances { get { return contents(_vm_appliance); } } public Folder[] Folders { get { return contents(_folders); } } public GPU_group[] GPU_groups { get { return contents(_gpu_groups); } } public Host[] Hosts { get { return contents(_host); } } public int HostCount { get { return _host.Count; } } public Host_cpu[] Host_cpus { get { return contents(_host_cpu); } } public XenAPI.Message[] Messages { get { return contents(_message); } } public XenAPI.Network[] Networks { get { return contents(_network); } } public PBD[] PBDs { get { return contents(_pbd); } } public PCI[] PCIs { get { return contents(_pcis); } } public PGPU[] PGPUs { get { return contents(_pgpu); } } public PIF[] PIFs { get { return contents(_pif); } } public Pool[] Pools { get { return contents(_pool); } } public Pool_patch[] Pool_patches { get { return contents(_pool_patch); } } public Role[] Roles { get { return contents(_role); } } public SM[] SMs { get { return contents(_sm); } } public SR[] SRs { get { return contents(_sr); } } public Subject[] Subjects { get { return contents(_subject); } } public Tunnel[] Tunnels { get { return contents(_tunnel); } } public VBD[] VBDs { get { return contents(_vbd); } } public VDI[] VDIs { get { return contents(_vdi); } } public VGPU[] VGPUs { get { return contents(_vgpu); } } public VGPU_type[] VGPU_types { get { return contents(_vgpu_types); } } public VIF[] VIFs { get { return contents(_vif); } } public VM[] VMs { get { return contents(_vm); } } private static T[] contents<T>(ChangeableDictionary<XenRef<T>, T> d) where T : XenObject<T> { lock (d) { T[] result = new T[d.Values.Count]; int i = 0; foreach (T o in d.Values) { result[i] = o; i++; } return result; } } /// <summary> /// Returns the collection for the given type, or null if no such dictionary is present. /// </summary> private IDictionary GetCollectionForType(Type t) { return dictionaries.ContainsKey(t) ? dictionaries[t] : null; } public void DeregisterCollectionChanged<T>(CollectionChangeEventHandler h) where T : XenObject<T> { ChangeableDictionary<XenRef<T>, T> d = GetCollectionForType(typeof(T)) as ChangeableDictionary<XenRef<T>, T>; d.CollectionChanged -= h; } public void RegisterCollectionChanged<T>(CollectionChangeEventHandler h) where T : XenObject<T> { ChangeableDictionary<XenRef<T>, T> d = GetCollectionForType(typeof(T)) as ChangeableDictionary<XenRef<T>, T>; d.CollectionChanged -= h; d.CollectionChanged += h; } public void DeregisterBatchCollectionChanged<T>(EventHandler h) where T : XenObject<T> { ChangeableDictionary<XenRef<T>, T> d = GetCollectionForType(typeof(T)) as ChangeableDictionary<XenRef<T>, T>; if (d == null) return; d.BatchCollectionChanged -= h; } public void RegisterBatchCollectionChanged<T>(EventHandler h) where T : XenObject<T> { ChangeableDictionary<XenRef<T>, T> d = GetCollectionForType(typeof(T)) as ChangeableDictionary<XenRef<T>, T>; if (d == null) return; d.BatchCollectionChanged -= h; d.BatchCollectionChanged += h; } public void AddAll<T>(List<T> l, Predicate<T> p) where T : XenObject<T> { IDictionary d = GetCollectionForType(typeof(T)); if (d == null) return; lock (d) { if (p == null) foreach (T value in d.Values) l.Add(value); else foreach (T value in d.Values) if (p(value)) l.Add(value); } } private static MethodInfo ClearMethod = typeof(Cache).GetMethod("Clear_", BindingFlags.NonPublic | BindingFlags.Instance); public void Clear() { foreach (IDictionary d in dictionaries.Values) { lock (d) { object[] args = { d }; ClearMethod.MakeGenericMethod(APIType(d)).Invoke(this, args); } INotifyCollectionChanged d1 = d as INotifyCollectionChanged; if (d1 != null) d1.OnBatchCollectionChanged(); } } private static MethodInfo UpdateFromMethod = typeof(Cache).GetMethod("UpdateFrom_", BindingFlags.NonPublic | BindingFlags.Instance); /// <returns>true if some changes have been made. This is used to indicate that XenObjectsUpdated should be fired by IXenConnection.</returns> public bool UpdateFrom(IXenConnection connection, IList<ObjectChange> changes) { Dictionary<IDictionary, object> tofire = new Dictionary<IDictionary, object>(); foreach (ObjectChange o in changes) { if (IgnoreObjectChange(o)) continue; IDictionary d = GetCollectionForType(o.type); if (d == null) continue; object[] args = { connection, d, o }; UpdateFromMethod.MakeGenericMethod(o.type).Invoke(this, args); tofire[d] = null; } bool result = false; foreach (IDictionary d in tofire.Keys) { INotifyCollectionChanged n = d as INotifyCollectionChanged; if (n != null) n.OnBatchCollectionChanged(); result = true; } return result; } /// <summary> /// For performance reasons, we ignore some events. These are: /// 1. The heartbeat event from xapi, which is a "change" on the pool object, but where the object doesn't actually change. /// 2. Tasks that are frequent and which we don't care about, like SR.scan. /// 3. Changes on SR that only toggle current_operations. These happen at the same time as the /// periodic SR.scan. /// </summary> private bool IgnoreObjectChange(ObjectChange obj) { if (obj.value == null) { // Object is being deleted. Never ignore these! return false; } else if (obj.type == typeof(Task)) { Task task = (Task)obj.value; return task.IgnoreInCacheUpdate(); } else if (obj.type == typeof(Pool)) { Pool newPool = (Pool)obj.value; Pool oldPool = Resolve(new XenRef<Pool>(obj.xenref)); return oldPool != null && newPool.DeepEquals(oldPool); } else if (obj.type == typeof(SR)) { SR newSR = (SR)obj.value; SR oldSR = Resolve(new XenRef<SR>(obj.xenref)); return oldSR != null && newSR.DeepEquals(oldSR, true); } else { return false; } } public T Find_By_Uuid<T>(string uuid) where T : XenObject<T> { Type t = typeof(T); PropertyInfo p = t.GetProperty("uuid", BindingFlags.Public | BindingFlags.Instance); if (p == null) return null; ChangeableDictionary<XenRef<T>, T> d = (ChangeableDictionary<XenRef<T>, T>)GetCollectionForType(t); lock (d) { foreach (T m in d.Values) { if (((string)p.GetValue(m, null)) == uuid) return m; } } return null; } /// <summary> /// Find a XenRef corresponding to the given XenObject, using its UUID. /// Returns null if no such object is found. /// </summary> public XenRef<T> FindRef<T>(T needle) where T : XenObject<T> { Type t = typeof(T); PropertyInfo p = t.GetProperty("uuid", BindingFlags.Public | BindingFlags.Instance); if (p == null) return null; string uuid = (string)p.GetValue(needle, null); ChangeableDictionary<XenRef<T>, T> d = (ChangeableDictionary<XenRef<T>, T>)GetCollectionForType(t); lock (d) { foreach (KeyValuePair<XenRef<T>, T> kvp in d) { if (((string)p.GetValue(kvp.Value, null)) == uuid) return kvp.Key; } } return null; } public bool TryResolve<T>(XenRef<T> xenRef, out T result) where T : XenObject<T> { result = Resolve(xenRef); return result != null; } /// <summary> /// /// </summary> /// <typeparam name="T"></typeparam> /// <param name="xenRef">May be null, in which case null is returned. May not be a null ref.</param> /// <returns></returns> public T Resolve<T>(XenRef<T> xenRef) where T : XenObject<T> { if (xenRef == null) return null; ChangeableDictionary<XenRef<T>, T> d = (ChangeableDictionary<XenRef<T>, T>)GetCollectionForType(typeof(T)); T result; return d.TryGetValue(xenRef, out result) ? result : null; } private void Clear_<T>(ChangeableDictionary<XenRef<T>, T> o) where T : XenObject<T> { // explicitly remove each element so change events are fired XenRef<T>[] keys = new XenRef<T>[o.Keys.Count]; o.Keys.CopyTo(keys, 0); foreach (XenRef<T> key in keys) { lock (o) { o.Remove(key); } } } private void UpdateFrom_<T>(Network.IXenConnection connection, ChangeableDictionary<XenRef<T>, T> target, ObjectChange source) where T : XenObject<T>, new() { XenRef<T> xenref = source.xenref as XenRef<T>; if (xenref == null) { xenref = new XenRef<T>((string)source.xenref); } if (source.value != null) { T to_update = null; lock (target) { if (!target.TryGetValue(xenref, out to_update)) { // add T obj = new T(); obj.Connection = connection; obj.UpdateFrom((T)source.value); obj.opaque_ref = xenref.opaque_ref; target.Add(xenref, obj); } } // Update the object that we found above. Note that this needs to be done out of the // scope of the lock(target), as UpdateFrom is going to fire events. if (to_update != null) to_update.UpdateFrom((T)source.value); } else { // delete the source object from our model lock (target) { target.Remove(xenref); } } } private Type APIType(IDictionary d) { return d.GetType().GetGenericArguments()[1]; } private bool foldersChanged = false; public void AddFolder(XenRef<Folder> path, Folder folder) { _folders[path] = folder; foldersChanged = true; } public void RemoveFolder(XenRef<Folder> path) { lock (_folders) { _folders.Remove(path); } foldersChanged = true; } public void CheckFoldersBatchChange() { if (foldersChanged) { foldersChanged = false; _folders.OnBatchCollectionChanged(); } } public IEnumerable<IXenObject> XenSearchableObjects { get { foreach (IXenObject o in VMs) yield return o; foreach (IXenObject o in VM_appliances) yield return o; foreach (IXenObject o in Hosts) yield return o; foreach (IXenObject o in SRs) yield return o; foreach (IXenObject o in Networks) yield return o; foreach (IXenObject o in VDIs) yield return o; foreach (IXenObject o in Folders) yield return o; foreach (Pool pool in Pools) { if (pool!=null&&pool.IsVisible) { yield return pool; break; } } } } } }