/*
* 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:
*
* 1) Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2) 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.IO;
using System.Net;
using XenAdmin.Core;
namespace XenAPI
{
public class EventNextBlockedException : Exception { }
public static class XenObjectDownloader
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private const double EVENT_FROM_TIMEOUT = 30; // 30 seconds
///
/// Whether to use the legacy event system (event.next); the new system is event.from.
///
///
public static bool LegacyEventSystem(Session session)
{
return session.APIVersion <= API_Version.API_1_9;
}
///
/// Gets all objects from the server. Used in order to fill the cache.
/// This function implements the new event system, available from in API version 1.9.
/// In the new event system, (GetAllRecords + GetEvents) sequence will replace (RegisterForEvents + DownloadObjects + GetNextEvents).
///
/// The session over which to download the objects. Must not be null.
/// The queue that the ObjectChanges will be put into. Must not be null.
/// Used by GetEvents().
/// True if legacy event system (event.next) should to be used.
/// Used by GetEvents().
public static void GetAllObjects(Session session, LockFreeQueue changes, HTTP.FuncBool cancelled, bool legacyEventSystem, ref string token)
{
if (legacyEventSystem)
{
DownloadObjects(session, changes);
return;
}
// download objects that are not covered by event.from(), e.g. Roles
List list = new List();
Download_Role(session, list);
foreach (ObjectChange o in list)
changes.Enqueue(o);
// get all objects with event.from()
token = "";
GetEvents(session, changes, cancelled, false, ref token);
}
///
/// Blocks until events are sent on the session, or timeout is reached, then processes any received events and adds them
/// to eventQueue. This function implements the new event system, available in API version 1.9.
/// In the new event system, (GetAllRecords + GetEvents) sequence will replace (RegisterForEvents + DownloadObjects + GetNextEvents).
///
///
///
///
/// True if legacy event system (event.next) should be used.
/// A token used by event.from().
/// It should be the empty string when event.from is first called, which is the replacement of get_all_records.
///
public static void GetEvents(Session session, LockFreeQueue eventQueue, HTTP.FuncBool cancelled, bool legacyEventSystem, ref string token)
{
if (legacyEventSystem)
{
GetNextEvents(session, eventQueue, cancelled);
return;
}
Proxy_Event[] proxyEvents;
try
{
var classes = new [] { "*" }; // classes that we are interested in receiving events from
var eventResult = Event.from(session, classes, token, EVENT_FROM_TIMEOUT);
token = eventResult.token;
proxyEvents = eventResult.events;
}
catch (WebException e)
{
// Catch timeout, and turn it into an EventNextBlockedException so we can recognise it later (CA-33145)
if (e.Status == WebExceptionStatus.Timeout)
throw new EventNextBlockedException();
else
throw;
}
if (cancelled())
return;
//We want to do the marshalling on this bg thread so as not to block the gui thread
foreach (Proxy_Event proxyEvent in proxyEvents)
{
ObjectChange objectChange = ProcessEvent(proxyEvent);
if (objectChange != null)
eventQueue.Enqueue(objectChange);
}
}
public static void RegisterForEvents(Session session)
{
Event.register(session, new string[] { "*" });
}
///
/// Blocks until events are sent on the session, then processes any received events and adds them
/// to eventQueue. Will always add at least one event to eventQueue.
/// This function should be used with XenServer up to version 6.0. For XenServer higher than 6.0, GetEvents() should be used instead.
///
///
///
///
public static void GetNextEvents(Session session, LockFreeQueue eventQueue, HTTP.FuncBool cancelled)
{
Proxy_Event[] proxyEvents;
try
{
proxyEvents = Event.next(session);
}
catch (WebException e)
{
// Catch timeout, and turn it into an EventNextBlockedException so we can recognise it later (CA-33145)
if (e.Status == WebExceptionStatus.Timeout)
throw new EventNextBlockedException();
else
throw;
}
if (proxyEvents.Length == 0)
throw new IOException("Event.next() returned no events; the server is misbehaving.");
if (cancelled())
return;
//We want to do the marshalling on this bg thread so as not to block the gui thread
foreach (Proxy_Event proxyEvent in proxyEvents)
{
ObjectChange objectChange = ProcessEvent(proxyEvent);
if (objectChange != null)
eventQueue.Enqueue(objectChange);
}
}
///
/// Returns null if we get an event we're not interested in, or an unparseable event (e.g. for an object type we don't know about).
///
///
///
private static ObjectChange ProcessEvent(Proxy_Event proxyEvent)
{
switch (proxyEvent.class_.ToLowerInvariant())
{
case "session":
case "event":
case "vtpm":
case "user":
case "secret":
// We don't track events on these objects
return null;
default:
Type typ = Marshalling.GetXenAPIType(proxyEvent.class_);
if (typ == null)
{
log.DebugFormat("Unknown {0} event for class {1}.", proxyEvent.operation, proxyEvent.class_);
return null;
}
switch (proxyEvent.operation)
{
case "add":
case "mod":
return new ObjectChange(typ, proxyEvent.opaqueRef, Marshalling.convertStruct(typ, (Hashtable)proxyEvent.snapshot));
case "del":
return new ObjectChange(typ, proxyEvent.opaqueRef, null);
default:
log.DebugFormat("Unknown event operation {0} for opaque ref {1}", proxyEvent.operation, proxyEvent.opaqueRef);
return null;
}
}
}
///
/// Downloads all objects from the server. Used in order to fill the cache.
/// This function should be used with XenServer up to version 6.0. For XenServer higher than 6.0, GetAllObjects() should be used instead.
///
/// The session over which to download the objects. Must not be null.
/// The queue that the ObjectChanges will be put into. Must not be null.
public static void DownloadObjects(Session session, LockFreeQueue changes)
{
List list = new List();
Download_Task(session, list);
Download_Pool(session, list);
Download_VM(session, list);
Download_VM_metrics(session, list);
Download_VM_guest_metrics(session, list);
Download_Host(session, list);
Download_Host_crashdump(session, list);
Download_Host_patch(session, list);
Download_Host_metrics(session, list);
Download_Host_cpu(session, list);
Download_Network(session, list);
Download_VIF(session, list);
Download_VIF_metrics(session, list);
Download_PIF(session, list);
Download_PIF_metrics(session, list);
Download_SM(session, list);
Download_SR(session, list);
Download_VDI(session, list);
Download_VBD(session, list);
Download_VBD_metrics(session, list);
Download_PBD(session, list);
Download_Crashdump(session, list);
Download_Console(session, list);
if (session.APIVersion >= API_Version.API_1_2)
{
// Download Miami-only objects
Download_Pool_patch(session, list);
Download_Bond(session, list);
Download_VLAN(session, list);
}
if (session.APIVersion >= API_Version.API_1_3)
{
// Download Orlando-only objects
Download_Blob(session, list);
Download_Message(session, list);
}
if (session.APIVersion >= API_Version.API_1_6)
{
// Download George-only objects
Download_Subject(session, list);
}
if (session.APIVersion >= API_Version.API_1_7)
{
// Download Midnight Ride-only objects
Download_Role(session, list);
}
if (session.APIVersion >= API_Version.API_1_8)
{
// Download Cowley-only objects
Download_VMPP(session, list);
Download_Tunnel(session, list);
}
if (session.APIVersion >= API_Version.API_1_9)
{
// Download Boston-only objects
Download_VM_appliance(session, list);
Download_DR_task(session, list);
Download_PCI(session, list);
Download_PGPU(session, list);
Download_GPU_group(session, list);
Download_VGPU(session, list);
}
foreach (ObjectChange o in list)
{
changes.Enqueue(o);
}
}
private static void Download_Subject(Session session, List changes)
{
Dictionary, Subject> records = Subject.get_all_records(session);
foreach (KeyValuePair, Subject> entry in records)
changes.Add(new ObjectChange(typeof(Subject), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Role(Session session, List changes)
{
Dictionary, Role> records = Role.get_all_records(session);
foreach (KeyValuePair, Role> entry in records)
changes.Add(new ObjectChange(typeof(Role), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Task(Session session, List changes)
{
Dictionary, Task> records = Task.get_all_records(session);
foreach (KeyValuePair, Task> entry in records)
changes.Add(new ObjectChange(typeof(Task), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Pool(Session session, List changes)
{
Dictionary, Pool> records = Pool.get_all_records(session);
foreach (KeyValuePair, Pool> entry in records)
changes.Add(new ObjectChange(typeof(Pool), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Pool_patch(Session session, List changes)
{
Dictionary, Pool_patch> records = Pool_patch.get_all_records(session);
foreach (KeyValuePair, Pool_patch> entry in records)
changes.Add(new ObjectChange(typeof(Pool_patch), entry.Key.opaque_ref, entry.Value));
}
private static void Download_VM(Session session, List changes)
{
Dictionary, VM> records = VM.get_all_records(session);
foreach (KeyValuePair, VM> entry in records)
changes.Add(new ObjectChange(typeof(VM), entry.Key.opaque_ref, entry.Value));
}
private static void Download_VM_metrics(Session session, List changes)
{
Dictionary, VM_metrics> records = VM_metrics.get_all_records(session);
foreach (KeyValuePair, VM_metrics> entry in records)
changes.Add(new ObjectChange(typeof(VM_metrics), entry.Key.opaque_ref, entry.Value));
}
private static void Download_VM_guest_metrics(Session session, List changes)
{
Dictionary, VM_guest_metrics> records = VM_guest_metrics.get_all_records(session);
foreach (KeyValuePair, VM_guest_metrics> entry in records)
changes.Add(new ObjectChange(typeof(VM_guest_metrics), entry.Key.opaque_ref, entry.Value));
}
private static void Download_VMPP(Session session, List changes)
{
Dictionary, VMPP> records = VMPP.get_all_records(session);
foreach (KeyValuePair, VMPP> entry in records)
changes.Add(new ObjectChange(typeof(VMPP), entry.Key.opaque_ref, entry.Value));
}
private static void Download_VM_appliance(Session session, List changes)
{
Dictionary, VM_appliance> records = VM_appliance.get_all_records(session);
foreach (KeyValuePair, VM_appliance> entry in records)
changes.Add(new ObjectChange(typeof(VM_appliance), entry.Key.opaque_ref, entry.Value));
}
private static void Download_DR_task(Session session, List changes)
{
Dictionary, DR_task> records = DR_task.get_all_records(session);
foreach (KeyValuePair, DR_task> entry in records)
changes.Add(new ObjectChange(typeof(DR_task), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Host(Session session, List changes)
{
Dictionary, Host> records = Host.get_all_records(session);
foreach (KeyValuePair, Host> entry in records)
changes.Add(new ObjectChange(typeof(Host), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Host_crashdump(Session session, List changes)
{
Dictionary, Host_crashdump> records = Host_crashdump.get_all_records(session);
foreach (KeyValuePair, Host_crashdump> entry in records)
changes.Add(new ObjectChange(typeof(Host_crashdump), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Host_patch(Session session, List changes)
{
Dictionary, Host_patch> records = Host_patch.get_all_records(session);
foreach (KeyValuePair, Host_patch> entry in records)
changes.Add(new ObjectChange(typeof(Host_patch), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Host_metrics(Session session, List changes)
{
Dictionary, Host_metrics> records = Host_metrics.get_all_records(session);
foreach (KeyValuePair, Host_metrics> entry in records)
changes.Add(new ObjectChange(typeof(Host_metrics), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Host_cpu(Session session, List changes)
{
Dictionary, Host_cpu> records = Host_cpu.get_all_records(session);
foreach (KeyValuePair, Host_cpu> entry in records)
changes.Add(new ObjectChange(typeof(Host_cpu), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Network(Session session, List changes)
{
Dictionary, Network> records = Network.get_all_records(session);
foreach (KeyValuePair, Network> entry in records)
changes.Add(new ObjectChange(typeof(Network), entry.Key.opaque_ref, entry.Value));
}
private static void Download_VIF(Session session, List changes)
{
Dictionary, VIF> records = VIF.get_all_records(session);
foreach (KeyValuePair, VIF> entry in records)
changes.Add(new ObjectChange(typeof(VIF), entry.Key.opaque_ref, entry.Value));
}
private static void Download_VIF_metrics(Session session, List changes)
{
Dictionary, VIF_metrics> records = VIF_metrics.get_all_records(session);
foreach (KeyValuePair, VIF_metrics> entry in records)
changes.Add(new ObjectChange(typeof(VIF_metrics), entry.Key.opaque_ref, entry.Value));
}
private static void Download_PIF(Session session, List changes)
{
Dictionary, PIF> records = PIF.get_all_records(session);
foreach (KeyValuePair, PIF> entry in records)
changes.Add(new ObjectChange(typeof(PIF), entry.Key.opaque_ref, entry.Value));
}
private static void Download_PIF_metrics(Session session, List changes)
{
Dictionary, PIF_metrics> records = PIF_metrics.get_all_records(session);
foreach (KeyValuePair, PIF_metrics> entry in records)
changes.Add(new ObjectChange(typeof(PIF_metrics), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Bond(Session session, List changes)
{
Dictionary, Bond> records = Bond.get_all_records(session);
foreach (KeyValuePair, Bond> entry in records)
changes.Add(new ObjectChange(typeof(Bond), entry.Key.opaque_ref, entry.Value));
}
private static void Download_VLAN(Session session, List changes)
{
Dictionary, VLAN> records = VLAN.get_all_records(session);
foreach (KeyValuePair, VLAN> entry in records)
changes.Add(new ObjectChange(typeof(VLAN), entry.Key.opaque_ref, entry.Value));
}
private static void Download_SM(Session session, List changes)
{
Dictionary, SM> records = SM.get_all_records(session);
foreach (KeyValuePair, SM> entry in records)
changes.Add(new ObjectChange(typeof(SM), entry.Key.opaque_ref, entry.Value));
}
private static void Download_SR(Session session, List changes)
{
Dictionary, SR> records = SR.get_all_records(session);
foreach (KeyValuePair, SR> entry in records)
changes.Add(new ObjectChange(typeof(SR), entry.Key.opaque_ref, entry.Value));
}
private static void Download_VDI(Session session, List changes)
{
Dictionary, VDI> records = VDI.get_all_records(session);
foreach (KeyValuePair, VDI> entry in records)
changes.Add(new ObjectChange(typeof(VDI), entry.Key.opaque_ref, entry.Value));
}
private static void Download_VBD(Session session, List changes)
{
Dictionary, VBD> records = VBD.get_all_records(session);
foreach (KeyValuePair, VBD> entry in records)
changes.Add(new ObjectChange(typeof(VBD), entry.Key.opaque_ref, entry.Value));
}
private static void Download_VBD_metrics(Session session, List changes)
{
Dictionary, VBD_metrics> records = VBD_metrics.get_all_records(session);
foreach (KeyValuePair, VBD_metrics> entry in records)
changes.Add(new ObjectChange(typeof(VBD_metrics), entry.Key.opaque_ref, entry.Value));
}
private static void Download_PBD(Session session, List changes)
{
Dictionary, PBD> records = PBD.get_all_records(session);
foreach (KeyValuePair, PBD> entry in records)
changes.Add(new ObjectChange(typeof(PBD), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Crashdump(Session session, List changes)
{
Dictionary, Crashdump> records = Crashdump.get_all_records(session);
foreach (KeyValuePair, Crashdump> entry in records)
changes.Add(new ObjectChange(typeof(Crashdump), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Console(Session session, List changes)
{
Dictionary, Console> records = Console.get_all_records(session);
foreach (KeyValuePair, Console> entry in records)
changes.Add(new ObjectChange(typeof(Console), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Blob(Session session, List changes)
{
Dictionary, Blob> records = Blob.get_all_records(session);
foreach (KeyValuePair, Blob> entry in records)
changes.Add(new ObjectChange(typeof(Blob), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Message(Session session, List changes)
{
Dictionary, Message> records = Message.get_all_records(session);
foreach (KeyValuePair, Message> entry in records)
changes.Add(new ObjectChange(typeof(Message), entry.Key.opaque_ref, entry.Value));
}
private static void Download_Tunnel(Session session, List changes)
{
Dictionary, Tunnel> records = Tunnel.get_all_records(session);
foreach (KeyValuePair, Tunnel> entry in records)
changes.Add(new ObjectChange(typeof(Tunnel), entry.Key.opaque_ref, entry.Value));
}
private static void Download_PCI(Session session, List changes)
{
Dictionary, PCI> records = PCI.get_all_records(session);
foreach (KeyValuePair, PCI> entry in records)
changes.Add(new ObjectChange(typeof(PCI), entry.Key.opaque_ref, entry.Value));
}
private static void Download_PGPU(Session session, List changes)
{
Dictionary, PGPU> records = PGPU.get_all_records(session);
foreach (KeyValuePair, PGPU> entry in records)
changes.Add(new ObjectChange(typeof(PGPU), entry.Key.opaque_ref, entry.Value));
}
private static void Download_GPU_group(Session session, List changes)
{
Dictionary, GPU_group> records = GPU_group.get_all_records(session);
foreach (KeyValuePair, GPU_group> entry in records)
changes.Add(new ObjectChange(typeof(GPU_group), entry.Key.opaque_ref, entry.Value));
}
private static void Download_VGPU(Session session, List changes)
{
Dictionary, VGPU> records = VGPU.get_all_records(session);
foreach (KeyValuePair, VGPU> entry in records)
changes.Add(new ObjectChange(typeof(VGPU), entry.Key.opaque_ref, entry.Value));
}
}
}