Windows Azure – Automatic Scaling System – Parte 2

Nell’articolo precedente abbiamo trattato il tema, dal punto di vista teorico e dell’analisi riguardante lo scaling automatico di un’applicazione pubblicata sulla piattaforma Cloud Windows Azure.

In questo vedremo in modo più approfondito la classe ManageServices adibita alla gestione delle API di Azure.


La classe principale che si occuperà solo della logica delle chiamate è la ManageServices che implementa i metodi principali per interagire con le API di Windows Azure.

getDeploymentslots

Recupera le informazioni sul deploy e su ogni singola istanza del deploy. Le informazioni vengono inserite nella classe Deployment e restituite al chiamante del metodo.

I parametri di ingresso sono

  • String servicename: nome del servizio Cloud che vogliamo analizzare.
  • DeploymentSlot deploymentslot: enum corrispondente al tipo di Slot utilizzato (Production | Staging).
updateDeploymentStatus

Aggiorna lo status del deploy (Runing | Suspended). L’operazione è di tipo asincrono, viene generato un reqid corrispondente all’operazione richiesta che viene restituito al chiamate in formato stringa e potrà essere utilizzato per recuperare lo status dell’operazione tramite il metodo getOperationStatus.

I parametri di ingresso sono:

  • String servicename: nome del servizio Cloud che vogliamo aggiornare.
  • DeploymentSlot deploymentslot: enum corrispondente al tipo di Slot utilizzato (Production | Staging).
  • DeploymentAction deploymentaction: enum corrispondente all’azione da eseguire sul depoy (Running | Suspended).
changeDeploymentConfiguration

Aggiorna il file di configurazione del deploy limitatamente al numero di istanze che vogliamo attive. Si occupa quindi di aumentare o diminuire il numero delle istanze. L’operazione è di tipo asincrono, viene generato un reqid corrispondente all’operazione richiesta che viene restituito al chiamate in formato stringa e potrà essere utilizzato per recuperare lo status dell’operazione tramite il metodo getOperationStatus.

I parametri di ingresso sono:

  • String servicename: nome del servizio Cloud che vogliamo aggiornare.
  • DeploymentSlot deploymentslot: enum corrispondente al tipo di Slot utilizzato (Production | Staging).
  • Deployment deployment: deployment del servizio che vogliamo aggiornare. Utilizzare getDeploymentslot per recuperarlo.
  • DeploymentConfigurationAction deploymentconfigurationaction: enum corrispondente all’azione da eseguire sul numero di istanze del deploy (Increment | Decrement).
  • Int32 value: valore che indica di quanto modificare il numero di istanze.
getOperationStatus

Recupera lo status dell’operazione tramite il valore reqid restituito dalle chiamate asincrone alle API. Al chiamante verrà restituita la classe Operation con lo stato dell’operazione (InProgress | Succeeded | Failed) ed eventuali errori.

I parametri di ingresso sono:

  • String opid: codice della richiesta asincrona di cui vogliamo sapere lo status.

Vengono utilizzate altre 3 classi di appoggio per i dati:

  • Deployment:contiene le informazioni di un deploy, conterrà una lista di RoleInstance.
    • RoleInstance: contiene le informazioni delle istanze di un deploy.
  • Operation: contiene le informazioni sullo stato dell’operazione richiesta.

Le proprietà di ogni classe corrispondono ai nodi dell’XML di risposta della chiamata API.

[codesyntax lang=”csharp” lines=”fancy” title=”Deployment” bookmarkname=”Deployment”]

enum DeploymentStatus
{
  Running,
  Suspended,
  RunningTransitioning,
  SuspendedTransitioning,
  Starting,
  Suspending,
  Deploying,
  Deleting,
  Empty
}

enum DeploymentAction
{
  Running,
  Suspended
}

enum DeploymentConfigurationAction
{
  Increment,
  Decrement
}

enum DeploymentSlot
{
  Production,
  Staging,
  Empty
}

class Deployment
{
  public String Name { get; set; }
  public DeploymentSlot DeploymentSlot { get; set; }
  public String PrivateId { get; set; }
  public String Label { get; set; }
  public String Url { get; set; }
  public String Configuration { get; set; }
  public Int32 ConfigurationInstanceCount { get; set; }
  public DeploymentStatus Status { get; set; }
  public Int32 UpgradeDomainCount { get; set; }
  public List<RoleInstance> RoleInstanceList { get; set; }

  public Deployment()
  {
    Name = String.Empty;
    DeploymentSlot = DeploymentSlot.Empty;
    PrivateId = String.Empty;
    Label = String.Empty;
    Url = String.Empty;
    Configuration = String.Empty;
    ConfigurationInstanceCount = 0;
    Status = DeploymentStatus.Empty;
    UpgradeDomainCount = 0;
    RoleInstanceList = new List<RoleInstance>();
  }
}

[/codesyntax]

[codesyntax lang=”csharp” lines=”fancy” title=”RoleInstance” bookmarkname=”RoleInstance”]

enum InstanceStatus
{
  Ready,
  Busy,
  Initializing,
  Stopping,
  Stopped,
  Unresponsive,
  Empty
}

class RoleInstance
{
  public String RoleName { get; set; }
  public String InstanceName { get; set; }
  public InstanceStatus Status { get; set; }

  public RoleInstance()
  {
    RoleName = String.Empty;
    InstanceName = String.Empty;
    Status = InstanceStatus.Empty;
  }
}

[/codesyntax]

[codesyntax lang=”csharp” lines=”fancy” title=”Operation” bookmarkname=”Operation”]

enum OperationStatus
{
  InProgress,
  Succeeded,
  Failed,
  Empty
}

class Operation
{
  public String Id { get; set; }
  public OperationStatus Status { get; set; }
  public String HTTPStatusCode { get; set; }
  public String ErrorCode { get; set; }
  public String ErrorMessage { get; set; }

  public Operation()
  {
    Id = String.Empty;
    Status = OperationStatus.Empty;
    HTTPStatusCode = String.Empty;
    ErrorCode = String.Empty;
    ErrorMessage = String.Empty;
  }
}

[/codesyntax]

[codesyntax lang=”csharp” lines=”fancy” title=”ManageServices” bookmarkname=”ManageServices”]

static class ManageServices
{
  public static Deployment getDeploymentslots(String servicename, DeploymentSlot deploymentslot)
  {
    String res = String.Empty;
    String reqid = String.Empty;

    Deployment deployment = null;
    RoleInstance roleinstance = null;
    HttpRequest httprequest = null;
    XDocument xDocument = null;
    XNamespace ns = null;
    XAttribute att = null;

    try
    {
      httprequest = new HttpRequest();
      httprequest.DoHttpRequest(String.Format(ServiceURL.getDeploymentslots, servicename, deploymentslot.ToString()), String.Empty, HttpMethod.GET, out res, out reqid);

      if (!String.IsNullOrEmpty(res))
      {
        deployment = new Deployment();

        xDocument = new XDocument();
        xDocument = XDocument.Parse(res);
        ns = xDocument.Root.Name.Namespace;

        deployment.Name = xDocument.Root.Element(ns + "Name").Value;
        deployment.DeploymentSlot = (DeploymentSlot)Enum.Parse(typeof(DeploymentSlot), xDocument.Root.Element(ns + "DeploymentSlot").Value);
        deployment.PrivateId = xDocument.Root.Element(ns + "PrivateID").Value;
        deployment.Status = (DeploymentStatus)Enum.Parse(typeof(DeploymentStatus), xDocument.Root.Element(ns + "Status").Value);
        deployment.Label = xDocument.Root.Element(ns + "Label").Value;
        deployment.Url = xDocument.Root.Element(ns + "Url").Value;
        deployment.Configuration = Encoding.UTF8.GetString(Convert.FromBase64String(xDocument.Root.Element(ns + "Configuration").Value));
        deployment.UpgradeDomainCount = Int32.Parse(xDocument.Root.Element(ns + "UpgradeDomainCount").Value);

        foreach (XElement element in xDocument.Root.Element(ns + "RoleInstanceList").Elements(ns + "RoleInstance"))
        {
          roleinstance = new RoleInstance();

          roleinstance.RoleName = element.Element(ns + "RoleName").Value;
          roleinstance.InstanceName = element.Element(ns + "InstanceName").Value;
          roleinstance.Status = (InstanceStatus)Enum.Parse(typeof(InstanceStatus), element.Element(ns + "InstanceStatus").Value);

          deployment.RoleInstanceList.Add(roleinstance);

          roleinstance = null;
        }

        xDocument = new XDocument();
        xDocument = XDocument.Parse(deployment.Configuration);

        ns = xDocument.Root.Name.Namespace;
        att = xDocument.Root.Element(ns + "Role").Element(ns + "Instances").Attribute("count");
        deployment.ConfigurationInstanceCount = Int32.Parse(att.Value);

        xDocument = null;
        ns = null;
        att = null;
      }
      else
        throw new Exception("getDeploymentslots error: Response error");
    }
    catch (Exception ex)
    {
      throw ex;
    } 

    return deployment;
  }

  public static String updateDeploymentStatus(String servicename, DeploymentSlot deploymentslot, DeploymentAction deploymentaction)
  {
    String res = String.Empty;
    String reqid = String.Empty;

    HttpRequest httprequest = null;
    XDocument xDocument = null;

    try
    {
      XNamespace ns = "http://schemas.microsoft.com/windowsazure";

      xDocument = new XDocument(
        new XElement(ns + "UpdateDeploymentStatus",
          new XElement(ns + "Status", deploymentaction.ToString())));

      httprequest = new HttpRequest();
      httprequest.DoHttpRequest(String.Format(ServiceURL.updateDeploymentStatus, servicename, deploymentslot.ToString()), xDocument.ToString(), HttpMethod.POST, out res, out reqid);

      if (!String.IsNullOrEmpty(res) && String.IsNullOrEmpty(reqid))
        throw new Exception("updateDeploymentStatus error: RequestId error");
    }
    catch (Exception ex)
    {
      throw ex;
    }

    return reqid;
  }

  public static String changeDeploymentConfiguration(String servicename, DeploymentSlot deploymentslot, Deployment deployment, DeploymentConfigurationAction deploymentconfigurationaction, Int32 value)
  {
    String res = String.Empty;
    String reqid = String.Empty;

    HttpRequest httprequest = null;
    XDocument xDocument = null;
    XElement xElement = null;
    XNamespace ns = null;

    Int32 newcount = 0;
    String configuration = String.Empty;

    try
    {
      if (value < 0)
        value *= -1;

      if (deploymentconfigurationaction == DeploymentConfigurationAction.Decrement)
        value *= -1;

      xDocument = new XDocument();
      xDocument = XDocument.Parse(deployment.Configuration);
      ns = xDocument.Root.Name.Namespace;
      xElement = xDocument.Root.Element(ns + "Role").Element(ns + "Instances");

      newcount = deployment.ConfigurationInstanceCount + value;
      if (newcount <= 0)
        throw new Exception("Instance count cannot be less than 1");

      xElement.SetAttributeValue("count", newcount.ToString());
      configuration = Convert.ToBase64String(Encoding.UTF8.GetBytes(xDocument.ToString()));

      ns = "http://schemas.microsoft.com/windowsazure";
      xDocument = new XDocument(
        new XElement(ns + "ChangeConfiguration",
          new XElement(ns + "Configuration", configuration)));

      httprequest = new HttpRequest();
      httprequest.DoHttpRequest(String.Format(ServiceURL.changeDeploymentConfiguration, servicename, deploymentslot.ToString()), xDocument.ToString(), HttpMethod.POST, out res, out reqid);
    }
    catch (Exception ex)
    {
      throw ex;
    }

    return reqid;
  }

  public static Operation getOperationStatus(String opid)
  {
    String res = String.Empty;
    String reqid = String.Empty;

    Operation operation = null;
    HttpRequest httprequest = null;
    XDocument xDocument = null;
    XNamespace ns = null;

    try
    {
      httprequest = new HttpRequest();
      httprequest.DoHttpRequest(String.Format(ServiceURL.getOperationStatus, opid), String.Empty, HttpMethod.GET, out res, out reqid);

      if (!String.IsNullOrEmpty(res))
      {
        operation = new Operation();

        xDocument = new XDocument();
        xDocument = XDocument.Parse(res);
        ns = xDocument.Root.Name.Namespace;

        operation.Id = xDocument.Root.Element(ns + "ID").Value;
        operation.Status = (OperationStatus)Enum.Parse(typeof(OperationStatus), xDocument.Root.Element(ns + "Status").Value);

        if (xDocument.Root.Element(ns + "HttpStatusCode") != null)
          operation.HTTPStatusCode = xDocument.Root.Element(ns + "HttpStatusCode").Value;

        if (xDocument.Root.Element(ns + "Error") != null)
        {
          operation.ErrorCode = xDocument.Root.Element(ns + "Error").Element(ns + "Code").Value;
          operation.ErrorMessage = xDocument.Root.Element(ns + "Error").Element(ns + "Message").Value;
        }
      }
      else
        throw new Exception("getOperationStatus error: Response error");
    }
    catch (Exception ex)
    {
      throw ex;
    }

    return operation;
  }
}

[/codesyntax]

Nella terza ed ultima parte dell’articolo tratteremo il tema della gestione delle azioni da eseguire e della logica di scaling (ServiceAction e RoleScaling).

Stay tuned.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *