xenadmin/XenModel/XenObjectDownloader.cs
Konstantina Chremmou 1a94ab1c5f CA-254412: Moved the XenObjectDownloader out of the XenAPI folder as it is
XenCenter specific and it is not retrieved from the SDK any more. Renamed
EventNextBlockedException to EventFromBlockedException since event.next() is now removed.

Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
2017-05-24 09:23:47 +01:00

166 lines
7.3 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:
*
* 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.Net;
using System.Runtime.Serialization;
using XenAPI;
namespace XenAdmin.Core
{
[Serializable]
public class EventFromBlockedException : Exception
{
public EventFromBlockedException() { }
public EventFromBlockedException(string message) : base(message) { }
public EventFromBlockedException(string message, Exception exception) : base(message, exception) { }
protected EventFromBlockedException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
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
/// <summary>
/// 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).
/// </summary>
/// <param name="session">The session over which to download the objects. Must not be null.</param>
/// <param name="changes">The queue that the ObjectChanges will be put into. Must not be null.</param>
/// <param name="cancelled">Used by GetEvents().</param>
/// <param name="token">Used by GetEvents().</param>
public static void GetAllObjects(Session session, LockFreeQueue<ObjectChange> changes, HTTP.FuncBool cancelled, ref string token)
{
// download objects that are not covered by event.from(), e.g. Roles
var roleRecords = Role.get_all_records(session);
foreach (KeyValuePair<XenRef<Role>, Role> entry in roleRecords)
changes.Enqueue(new ObjectChange(typeof(Role), entry.Key.opaque_ref, entry.Value));
// get all objects with event.from()
token = "";
GetEvents(session, changes, cancelled, ref token);
}
/// <summary>
/// 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).
/// </summary>
/// <param name="session"></param>
/// <param name="eventQueue"></param>
/// <param name="cancelled"></param>
/// <param name="token">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.
/// </param>
public static void GetEvents(Session session, LockFreeQueue<ObjectChange> eventQueue, HTTP.FuncBool cancelled, ref string token)
{
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 EventFromBlockedException so we can recognise it later (CA-33145)
if (e.Status == WebExceptionStatus.Timeout)
throw new EventFromBlockedException();
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);
}
}
/// <summary>
/// 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).
/// </summary>
/// <param name="proxyEvent"></param>
/// <returns></returns>
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;
}
}
}
}
}