/* 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; using System.Linq; using System.Diagnostics; namespace XenAdmin.Network { public class Cache : ICache { #pragma warning disable 0414 // keep sorted please private readonly ChangeableDictionary, Bond> _bond = new ChangeableDictionary, Bond>(); private readonly ChangeableDictionary, Blob> _blob = new ChangeableDictionary, Blob>(); private readonly ChangeableDictionary, XenAPI.Console> _console = new ChangeableDictionary, XenAPI.Console>(); private readonly ChangeableDictionary, Folder> _folders = new ChangeableDictionary, Folder>(); private readonly ChangeableDictionary, DockerContainer> _dockerContainers = new ChangeableDictionary, DockerContainer>(); private readonly ChangeableDictionary, GPU_group> _gpu_groups = new ChangeableDictionary, GPU_group>(); private readonly ChangeableDictionary, Host> _host = new ChangeableDictionary, Host>(); private readonly ChangeableDictionary, Host_cpu> _host_cpu = new ChangeableDictionary, Host_cpu>(); private readonly ChangeableDictionary, Host_crashdump> _host_crashdump = new ChangeableDictionary, Host_crashdump>(); private readonly ChangeableDictionary, Host_metrics> _host_metrics = new ChangeableDictionary, Host_metrics>(); private readonly ChangeableDictionary, Host_patch> _host_patch = new ChangeableDictionary, Host_patch>(); private readonly ChangeableDictionary, Message> _message = new ChangeableDictionary, Message>(); private readonly ChangeableDictionary, XenAPI.Network> _network = new ChangeableDictionary, XenAPI.Network>(); private readonly ChangeableDictionary, PBD> _pbd = new ChangeableDictionary, PBD>(); private readonly ChangeableDictionary, PCI> _pcis = new ChangeableDictionary, PCI>(); private readonly ChangeableDictionary, PGPU> _pgpu = new ChangeableDictionary, PGPU>(); private readonly ChangeableDictionary, PIF> _pif = new ChangeableDictionary, PIF>(); private readonly ChangeableDictionary, PIF_metrics> _pif_metrics = new ChangeableDictionary, PIF_metrics>(); private readonly ChangeableDictionary, Pool> _pool = new ChangeableDictionary, Pool>(); private readonly ChangeableDictionary, Pool_patch> _pool_patch = new ChangeableDictionary, Pool_patch>(); private readonly ChangeableDictionary, Pool_update> _pool_update = new ChangeableDictionary, Pool_update>(); private readonly ChangeableDictionary, PVS_cache_storage> _pvs_cache_storage = new ChangeableDictionary, PVS_cache_storage>(); private readonly ChangeableDictionary, PVS_proxy> _pvs_proxy = new ChangeableDictionary, PVS_proxy>(); private readonly ChangeableDictionary, PVS_server> _pvs_server = new ChangeableDictionary, PVS_server>(); private readonly ChangeableDictionary, PVS_site> _pvs_site = new ChangeableDictionary, PVS_site>(); private readonly ChangeableDictionary, Role> _role = new ChangeableDictionary, Role>(); private readonly ChangeableDictionary, SM> _sm = new ChangeableDictionary, SM>(); private readonly ChangeableDictionary, SR> _sr = new ChangeableDictionary, SR>(); private readonly ChangeableDictionary, Subject> _subject = new ChangeableDictionary, Subject>(); private readonly ChangeableDictionary, Task> _task = new ChangeableDictionary, Task>(); private readonly ChangeableDictionary, Tunnel> _tunnel = new ChangeableDictionary, Tunnel>(); private readonly ChangeableDictionary, VBD> _vbd = new ChangeableDictionary, VBD>(); private readonly ChangeableDictionary, VBD_metrics> _vbd_metrics = new ChangeableDictionary, VBD_metrics>(); private readonly ChangeableDictionary, VDI> _vdi = new ChangeableDictionary, VDI>(); private readonly ChangeableDictionary, VGPU> _vgpu = new ChangeableDictionary, VGPU>(); private readonly ChangeableDictionary, VGPU_type> _vgpu_types = new ChangeableDictionary, VGPU_type>(); private readonly ChangeableDictionary, VIF> _vif = new ChangeableDictionary, VIF>(); private readonly ChangeableDictionary, VIF_metrics> _vif_metrics = new ChangeableDictionary, VIF_metrics>(); private readonly ChangeableDictionary, VLAN> _vlan = new ChangeableDictionary, VLAN>(); private readonly ChangeableDictionary, VM> _vm = new ChangeableDictionary, VM>(); private readonly ChangeableDictionary, VM_metrics> _vm_metrics = new ChangeableDictionary, VM_metrics>(); private readonly ChangeableDictionary, VM_guest_metrics> _vm_guest_metrics = new ChangeableDictionary, VM_guest_metrics>(); private readonly ChangeableDictionary, VMSS> _vmss = new ChangeableDictionary, VMSS>(); private readonly ChangeableDictionary, VM_appliance> _vm_appliance = new ChangeableDictionary, VM_appliance>(); private readonly ChangeableDictionary, Crashdump> _crashdump = new ChangeableDictionary, Crashdump>(); #pragma warning restore 0414 private readonly Dictionary dictionaries = new Dictionary(); 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 VMSS[] VMSSs { get { return contents(_vmss); } } public VM_appliance[] VM_appliances { get { return contents(_vm_appliance); } } public Folder[] Folders { get { return contents(_folders); } } public DockerContainer[] DockerContainers { get { return contents(_dockerContainers); } } 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 Pool_update[] Pool_updates { get { return contents(_pool_update); } } public PVS_site[] PVS_sites { get { return contents(_pvs_site); } } public PVS_server[] PVS_servers { get { return contents(_pvs_server); } } public PVS_proxy[] PVS_proxies { get { return contents(_pvs_proxy); } } public PVS_cache_storage[] PVS_cache_storages { get { return contents(_pvs_cache_storage); } } 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(ChangeableDictionary, T> d) where T : XenObject { lock (d) { T[] result = new T[d.Values.Count]; int i = 0; foreach (T o in d.Values) { result[i] = o; i++; } return result; } } /// /// Returns the collection for the given type, or null if no such dictionary is present. /// private IDictionary GetCollectionForType(Type t) { return dictionaries.ContainsKey(t) ? dictionaries[t] : null; } public void DeregisterCollectionChanged(CollectionChangeEventHandler h) where T : XenObject { ChangeableDictionary, T> d = GetCollectionForType(typeof(T)) as ChangeableDictionary, T>; if (d == null) return; d.CollectionChanged -= h; } public void RegisterCollectionChanged(CollectionChangeEventHandler h) where T : XenObject { ChangeableDictionary, T> d = GetCollectionForType(typeof(T)) as ChangeableDictionary, T>; if (d == null) return; d.CollectionChanged -= h; d.CollectionChanged += h; } public void DeregisterBatchCollectionChanged(EventHandler h) where T : XenObject { ChangeableDictionary, T> d = GetCollectionForType(typeof(T)) as ChangeableDictionary, T>; if (d == null) return; d.BatchCollectionChanged -= h; } public void RegisterBatchCollectionChanged(EventHandler h) where T : XenObject { ChangeableDictionary, T> d = GetCollectionForType(typeof(T)) as ChangeableDictionary, T>; if (d == null) return; d.BatchCollectionChanged -= h; d.BatchCollectionChanged += h; } public void AddAll(List l, Predicate p) where T : XenObject { 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); /// true if some changes have been made. This is used to indicate that XenObjectsUpdated should be fired by IXenConnection. public bool UpdateFrom(IXenConnection connection, IList changes) { Dictionary tofire = new Dictionary(); 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; } /// /// 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. /// 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(obj.xenref)); return oldPool != null && newPool.DeepEquals(oldPool, false); //We do want to see changes in the Pool.current_operations. } else if (obj.type == typeof(SR)) { SR newSR = (SR)obj.value; SR oldSR = Resolve(new XenRef(obj.xenref)); return oldSR != null && newSR.DeepEquals(oldSR, true); } else { return false; } } public T Find_By_Uuid(string uuid) where T : XenObject { Type t = typeof(T); PropertyInfo p = t.GetProperty("uuid", BindingFlags.Public | BindingFlags.Instance); if (p == null) return null; ChangeableDictionary, T> d = GetCollectionForType(t) as ChangeableDictionary, T>; if (d == null) return null; lock (d) { foreach (T m in d.Values) { if (string.Equals((string)p.GetValue(m, null), uuid, StringComparison.OrdinalIgnoreCase)) return m; } } return null; } /// /// Find a XenRef corresponding to the given XenObject, using its UUID. /// Returns null if no such object is found. /// public XenRef FindRef(T needle) where T : XenObject { 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, T> d = GetCollectionForType(t) as ChangeableDictionary, T>; if (d == null) return null; lock (d) { foreach (KeyValuePair, T> kvp in d) { if (string.Equals((string)p.GetValue(kvp.Value, null), uuid, StringComparison.OrdinalIgnoreCase)) return kvp.Key; } } return null; } public bool TryResolve(XenRef xenRef, out T result) where T : XenObject { result = Resolve(xenRef); return result != null; } /// /// /// /// /// May be null, in which case null is returned. May not be a null ref. /// public T Resolve(XenRef xenRef) where T : XenObject { if (xenRef == null) return null; ChangeableDictionary, T> d = GetCollectionForType(typeof(T)) as ChangeableDictionary, T>; if (d == null) return null; T result; return d.TryGetValue(xenRef, out result) ? result : null; } private void Clear_(ChangeableDictionary, T> o) where T : XenObject { // explicitly remove each element so change events are fired XenRef[] keys = new XenRef[o.Keys.Count]; o.Keys.CopyTo(keys, 0); foreach (XenRef key in keys) { lock (o) { o.Remove(key); } } } private void UpdateFrom_(Network.IXenConnection connection, ChangeableDictionary, T> target, ObjectChange source) where T : XenObject, new() { XenRef xenref = source.xenref as XenRef; if (xenref == null) { xenref = new XenRef((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 path, Folder folder) { _folders[path] = folder; foldersChanged = true; } public void RemoveFolder(XenRef path) { lock (_folders) { _folders.Remove(path); } foldersChanged = true; } public void CheckFoldersBatchChange() { if (foldersChanged) { foldersChanged = false; _folders.OnBatchCollectionChanged(); } } public void CheckDockerContainersBatchChange() { if (dockerContainersChanged) { dockerContainersChanged = false; _dockerContainers.OnBatchCollectionChanged(); } } private bool dockerContainersChanged = false; public void UpdateDockerContainersForVM(IList containers, VM vm) { Trace.Assert(vm != null); //updating existing, adding new containers dockerContainersChanged = dockerContainersChanged || containers.Count > 0; foreach (var c in containers) { _dockerContainers[new XenRef(c)] = c; } List, DockerContainer>> containersGoneFromThisVM = null; //removing the ones that are not there anymore on this VM lock (_dockerContainers) { containersGoneFromThisVM = _dockerContainers.Where(c => c.Value != null && c.Value.Parent.uuid == vm.uuid && !containers.Any(cont => cont.uuid == c.Value.uuid)).ToList(); dockerContainersChanged = dockerContainersChanged || containersGoneFromThisVM.Count > 0; foreach (var c in containersGoneFromThisVM) { _dockerContainers.Remove(new XenRef(c.Value)); } } } public IEnumerable 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 (IXenObject o in DockerContainers) yield return o; foreach (Pool pool in Pools) { if (pool != null && pool.IsVisible()) { yield return pool; break; } } } } } }