mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-19 15:06:02 +01:00
fb1f44e67b
Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
580 lines
22 KiB
C#
580 lines
22 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.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<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 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|