Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / containers / virtual-machine

Windows Azure Drives: Part 1: Configure and Mounting at Startup of Web Role Lifecycle

4.97/5 (7 votes)
28 May 2010BSD22 min read 59.4K  
An approach for providing Windows Azure Drives (a.k.a. XDrive) to any cloud-based web applications through RoleEntryPoint callback methods and exposing successful mounting results within an environment variable through Global.asax callback method derived from the HttpApplication base class.

Doxygen generated view of CodePlex download: Source Documentation

Introduction

This article presents an approach for providing Windows Azure Drives (a.k.a. XDrive) to any cloud-based web applications through RoleEntryPoint callback methods and exposing successful mounting results within an environment variable through Global.asax callback method derived from the HttpApplication base class. The article will demonstrate how to use this approach in mounting XDrives before a .NET (C#) cloud-base web application is running.

The next article will take this approach and apply it to PHP cloud-based web applications.

Part 1: WebRole mounting XDrives in RoleEntryPoint.OnStart(): C#

This article implements RoleEntryPoint.OnStart() callback to perform the following:

  1. Read ServiceConfiguration.cscfg and ServiceDefinition.csdef files that is configured for mounting zero (if none is provided) or more XDrives.
  2. Create LocalStorage that will be used as local read cache when mounting XDrives.
  3. Mount XDrives having an access mode of either write or read-only.
  4. Expose successful XDrive mounts by populating an environment variable X_DRIVES, and provide the following information:
    1. Friendly XDrive Label
    2. Drive Letter
    3. Drive Access Mode

In addition:

  1. How to configure mounting of XDrives within ServiceConfiguration.cscfg and ServiceDefinition.csdef files.
  2. Demonstrate using created environment variable X_DRIVES within a web role application.

Part 2: Cloud-Based PHP Web Application using XDrives

The next article presents how to create and deploy a cloud-based PHP web application to be deployed upon Windows Azure platform, and be able to leverage Windows Azure Drives.

It will include a discussion of how add this feature using either of the following PHP Developer Tools that are integrated with Windows Azure platform support:

Windows Azure Drive Overview

Most, if not all applications, require a durable data store. Windows Azure Drives is an infrastructure solution that provides a durable NTFS drive within the Windows Azure platform.

One of the challenges is taking existing Windows applications and running them in the cloud while making sure their data is durable while using the standard Windows NTFS APIs. With Windows Azure Drive, Windows Azure applications running in the cloud can use existing NTFS APIs to access a durable drive. This can significantly ease the migration of existing Windows applications to the cloud. The Windows Azure application can read from or write to a drive letter (e.g., X:\) that represents a durable NTFS volume for storing and accessing data.

The durable drive is implemented as a Windows Azure Page Blob containing an NTFS-formatted Virtual Hard Drive (VHD). An additional benefit of this approach is that the VHD can be downloaded from the Blob store at any time, thus providing customers easy access to their data.

For additional information on Windows Azure drives, please refer to the Windows Azure Drive Whitepaper.

About Web Applications needing Durable Volumes

Existing or new web applications, whether running in Windows or not, many will require a durable location to hold content that are essential in running the site. This content could include files of any type: site configuration, images, style sheets, logs, databases, etc... Access modes to these files will either be read-only or writable, and access location will either be unbuffered on-disc or cached.

One of the challenges of migrating these aforementioned web applications and running them in cloud is making sure their data needs are durable. Windows Azure Drives eases the migration of web applications running on Windows platform to the cloud because their durable needs are still accessible because any of their file operations are still using standard Windows NTFS APIs.

Configuring Web Applications access to Durable Volumes

The implementation question in using XDrives are how they are integrated into a migrated web application that expect a durable location while in the cloud.

When a web application is installed on local Windows host, the installation administrator is chooses a hard drive (e.g., HOMEDRIVE C:\) that will be expected to hold the durable data based upon available disc space and assigning what amount of local on-disk cache of the drive’s data. With this chosen hard drive, known by its drive letter, the administrator then builds out the directories, sets access permissions, and copies in both the web application and its data.

When establishing XDrives as the durable storage for a migrated web application to the cloud, the installation administrator is still given choice to in assigning how disc space and assigning what amount of local on-disk cache of the drive’s data will be that will be required. However, what is different from the standard local host installation is that the location of the drive is not known until runtime in the cloud. In other words, the web application defines its durable needs when creating and mount an XDrive, then Windows Azure assigns a drive letter for this mounted XDrive to the web application. With this runtime assigned drive letter to an XDrive, the web application now has a known location in the cloud to a durable NTFS volume for storing and accessing file-based data.

So the twist in providing durable volumes to a web application in the cloud is for it be malleable to receive XDrives' location via drive letters which will only occur at runtime.

Approaches to Mounting XDrives

There are two approaches in mounting XDrives and providing their locations to a cloud-based web applications. Both approaches require that the web applications have been started as a process within Windows Azure Web instance host. The differences are how web applications at runtime (i.e., started) are informed as to the status of all successfully mounted XDrives: volume location or drive letter, and established access mode.

  1. Mounting XDrives during the loading of the web application's assembly before it starts.
    The location (drive letters) of the successfully mounted XDrives have to be provided indirectly only after the web application has started.
  2. Mounting XDrives after web application has started and anytime during it runtime.
    The web application can acquire the location (drive letters) of the successfully mounted XDrives directly since it was performed during its runtime.

Providing XDrives to any Cloud-based Web Applications

If a web application is mounting XDrives during its runtime, then this web application has to be written in a .NET supported languages (VB, C#, C++, F#, and JScript) to access the Windows Azure Cloud functionality within Microsoft.WindowsAzure.StorageClient namespace.

However, if XDrives are being mounted before a web application has started, then the web application can be any programming language, including PHP and Java. What has to be provided is shared binary (DLL) that is used during loading of the web application's assembly. This DLL will have to perform the following functionality in this order, as for example to service a PHP web application, which will be approach established by this article:

  1. While Windows Azure is initializing the WebRole instance of web application
    1. Read configuration expectation for one or more XDrives, which will include:
      • Drive size
      • Size of local on-disk cache of the drive’s data
      • Access mode
    2. Mounting Windows Azure Drives
    3. Provide the following information for each successfully mounted XDrives to a location that is accessible by any web application. For example, by setting an environment variable
      • friendly label
      • drive letters
      • established access mode
  2. During web application runtime (after startup)
    1. Read environment variable set during the initiation of WebRole.
    2. Web application will define any directory path that is expected to reference an XDrive by its drive letter, will instead reference by association to a friendly label.
    3. Durable NTFS volumes are provided to web application.
  3. While Windows Azure is stopping the WebRole instance of PHP web applicationspan>
    1. Unmounting Windows Azure Drives

Windows Azure Drives: Phases

Web Role Lifecycle in Windows Azure

To fill this void between XDrives and PHP applications, the approach of this article is to create a WebRole DLL in C# that would manage all XDrives' mounting before the PHP web application starts. To implement this, first one needs to understand the lifecycle of a web role.

Web Roles

The architecture of a service hosted in Windows Azure is based on discrete scalable components built with managed code. These components are termed roles.

A web role is a role that is customized for web application programming.

Lifecycle Management

A web role's lifecycle is managed by callback methods that handles its startup and stopping sequences.

  1. WaWebHost.exe (Windows Azure Web instance Host) starts a process and web role instance within it.
  2. Internet Information Services (IIS) 7.0 Hosted Web Core is activated and web role runs in integrated pipeline mode upon it.
  3. Web role application's assembly is loaded and RoleEntryPoint.OnStart() is called.
  4. Global.Application_Start() is called on the start of the web service.
    1. Fired when the first instance of the HttpApplication class is created.
    2. It allows you to create objects that are accessible by all HttpApplication instances.
  5. Web role application runs.
  6. Global.Application_End() is called as the web application's end event.
    1. Fired when the last instance of an HttpApplication class is destroyed.
    2. It's fired only once during an application's lifetime.
  7. RoleEntryPoint.OnStop() is called.
  8. Internet Information Services (IIS) 7.0 Hosted Web Core is deactivated.
  9. WaWebHost.exe process is stopped.

Sample Cloud-Based C# Web Application using Windows Azure Drives

Setup Prerequisites

  • Windows 7, Windows Server 2008 or Windows Vista SP1
  • Windows Azure Software Development Kit (February 2010 or later): Download
  • Visual Studio 2008 or above

CodePlex Download Contents

This download site is from the open source project hosted upon CodePlex: Windows Azure Command-line Tools for PHP Developers.

The Windows Azure Command-line Tools for PHP enable developers to easily package and deploy PHP applications to Windows Azure using a simple command-line tool. The tools allow creating new applications or converting existing PHP applications to Windows Azure and by creating the deployment package (.cspkg) and Configuration file (.cscfg).

From the the zip file within this page contains the WebRole sources that deals with mounting Windows Azure Drives: Download

After download and unzipping azurephptools-*.zip, the discussion of this article pertains sub-directory \azurephptools-*\web_role_dll_zip. Its contents contain a Visual Studio 2008 project that creates a WebRole DLL and C# Web Application that mounts XDrives based upon provided Service Configuration.

Doxygen generated view of these C# sources in CodePlex are available here: Documentation

The download contains the following files:

Web Role DLL

  1. WebRole.cs - Uses RoleEntryPoint event handlers are used to mount and unmount XDrives.
  2. XDrives.cs - class XDrives reads requested XDrives configuration and mounts them.
  3. Global application class (Global.asax & Global.asax.cs) - Gathers information of successfully mounted XDrives and establishes Windows environment variable X_DRIVES with this information.

Example Service Configuration

  1. ServiceDefinition.csdef - Declares LocalStorage and defines settings for XDrives
  2. ServiceConfiguration.cscfg - Provides example configuration setting on how to mount XDrives at runtime by aforementioned Web Role DLL.

Example Windows Azure C# Web Application

  1. XDrivesTest.cs - Gathers mounted XDrives information from X_DRIVES, and based upon each mounted XDrives' access mode, either creates & writes files or read files.
  2. Default page (Default.asap, Default.asap.cs) - Presents results from testing as performed by class XDrivesTest.

Added Reference

Added to solution's references is required Microsoft.WindowsAzure.CloudDrive:

add_ref_clouddrive.png

How It Works

Implementing Web Role Event Handlers

When implementing this Web Role, only the following web role lifecycle event handlers were implemented:

WebRole.cs

C#
public class WebRole : RoleEntryPoint
{    private XDrives m_XDrives = new XDrives();

    public override bool OnStart()
    {
        DiagnosticMonitor.Start("DiagnosticsConnectionString");

        // Mount XDrives
        m_XDrives.MountXDrives();

        // For information on handling configuration changes
        // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
        RoleEnvironment.Changing += RoleEnvironmentChanging;

        return base.OnStart();
    }

    public override void OnStop()
    {
        // Unmount XDrives
        m_XDrives.UnmountXDrives();

        base.OnStop();
    }

    // Continue...
}<}

Global.asax.cs

C#
public class Global : System.Web.HttpApplication
{
    private XDrives m_XDrives = new XDrives();

    protected void Application_Start(object sender, EventArgs e)
    {
        m_XDrives.SetXDrivesEnvironmentVariable();
    }

    // Continue
}</pre}< />

RoleEntryPoint.OnStart()

The event handler mounts XDrives based upon provided configuration settings by calling XDrives.MountXDrives().

Code: XDrives.MountXDrives()

  1. Reads configurations from ServiceConfiguration.cscfg. More later. RoleEnvironment.GetConfigurationSettingValue()
  2. If there are not any XDrives configurations within ServiceConfiguration.cscfg, then it exits.
  3. Parses the XDrive configurations into a dictionary.
  4. Reads Local Storage configuration from ServiceDefinition.csdef: RoleEnvironment.GetLocalResource()
  5. Get Cloud Storage Account.
  6. Initialized mount read cache to be used by XDrives within this VM: CloudDrive.InitializeCache()
  7. Gathers a listing of currently mounted XDrives: CloudDrive.GetMountedDrives()
  8. Create a new Blob service client: CloudStorageAccountStorageClientExtensions.CreateCloudBlobClient()
  9. Loop through configuration and mount XDrive: XDrives.MountXDrive()

Windows Azure Drives mounting during RoleEntryPoint.OnStart()

C#
/// <summary>
/// Mount VHDs based upon settings within ServiceConfiguration.cscfg file.
/// </summary>
public void MountXDrives()
{
    try
    {
        if (!GetXDrivesConfig())
        {
            return;
        }

        Trace.TraceInformation("Mounting X-Drives");
        if (!InitializeXDrives())
        {
            return;
        }

        /*
         * Interate over each X-Drive configuration and
         * perform setup as requested.
         */
        Trace.TraceInformation("Mounting {0} X-Drives Configurations",
            m_aXDriveConfigs.Count
            );
        foreach (IDictionary<string, string> dicXDriveConfig in m_aXDriveConfigs)
        {
            MountXDrive(dicXDriveConfig);
        }
    }
    catch (Exception ex)
    {
        Trace.TraceError("Unexpected Exception: Failed to mount X-drives: {0): {1)",
            ex.GetBaseException(),
            ex.Message
            );
    }
}
Code: XDrives.GetXDrivesConfig()
C#
/// <summary>
/// Gather from ServiceConfiguration.cscfg, XDrives' configuration
/// </summary>
/// <returns>True if XDrives configuration is found and has contents.</returns>
private bool GetXDrivesConfig()
{
    /*
     * X-Drives configuration string
     * from ServiceConfiguration.cscfg file.
     */
    string sXDrivesConfig = ReadXDrivesConfig();
    if (null == sXDrivesConfig || 0 == sXDrivesConfig.Length)
    {
        return false;
    }

    /*
     * Parse provide XDrives string from Service Configuration file.
     */
    m_aXDriveConfigs = ParseXDriveConfigs(sXDrivesConfig);
    if (null == m_aXDriveConfigs || 0 == m_aXDriveConfigs.Count)
    {
        return false;
    }

    return true;
}
Code: XDrives.ReadXDrivesConfig()
C#
/// <summary>
/// Read X-Drives configuration settings from ServiceConfiguration.cscfg
/// </summary>
/// <returns>X-Drives Configuration String</returns>
public string ReadXDrivesConfig()
{
    string sXDrivesConfig = string.Empty;
    try
    {
        sXDrivesConfig = RoleEnvironment.GetConfigurationSettingValue(XDRIVES_CONFIG_SETTING);

        if (null != sXDrivesConfig || string.Empty != sXDrivesConfig)
        {
            sXDrivesConfig = sXDrivesConfig.Trim();
        }
    }
    catch (Exception ex)
    {
        Trace.TraceError(
            "X-Drives: Unexpected Exception: Failed Read X-Drives Config: {0}",
            ex.Message
            );
    }

    return sXDrivesConfig;
}
Code: XDrives.InitializeXDrives()
C#
/// <summary />
/// Initializes for mounting X-Drives
/// </summary />
private bool InitializeXDrives()
{
    Trace.TraceInformation("Initializing for mounting X-Drives");

    /*
     * Cloud Storage Account
     */
    if (!GetCloudStorageAccount() || (null == m_oCloudStorageAccount) )
    {
        return false;
    }

    /*
     * Configuration: Get name of LocalStorage that will be used
     * for initializing X-Drives' mounts read cache size.
     */
    string sXDrivesLocalCacheName
        = RoleEnvironment.GetConfigurationSettingValue(XDRIVES_CONFIG_LOCAL_CACHE_SETTING).Trim();

    if (null == sXDrivesLocalCacheName || 0 == sXDrivesLocalCacheName.Length)
    {
        Trace.TraceWarning("No Local Cache for X-Drives was defined in Service Configuration file.");
        return false;
    }

    /*
     * Windows Azure Drive environment has to be initialized.
     * 
     * Create local storage which will act as the local cache
     * for all X-Drive mounts.
     */
    LocalResource localCache = RoleEnvironment.GetLocalResource(sXDrivesLocalCacheName);
    Char[] backSlash = { '\\' };
    String localCachePath = localCache.RootPath.TrimEnd(backSlash);
    CloudDrive.InitializeCache(localCachePath, localCache.MaximumSizeInMegabytes);

    m_iMaximumSizeInMegabytes = localCache.MaximumSizeInMegabytes;

    /*
     * Validate the sum of all request mount read cache size does
     * not exceed requested Local Cache size.
     */
    int iSumAllMountReadCacheSizeMB = XDrives.GetSumXDrivesMountCache(m_aXDriveConfigs);
    if (iSumAllMountReadCacheSizeMB > localCache.MaximumSizeInMegabytes)
    {
        Trace.TraceWarning(
            "Sum of all X-Drives Mount Read Cache size {0} MB exceeds requested Local Cache size {1} MB",
            iSumAllMountReadCacheSizeMB,
            localCache.MaximumSizeInMegabytes
            );
        return false;
    }

    /*
     * Get URI paths for all currently mounted X-Drives.
     */
    m_aXDrivesPathMounted = GetMountedDrivesPaths();

    /*
     * Validate: Maximum number of X-Drives has not been exceeded
     */
    if (MAX_XDRIVES_MOUNTS_PER_INSTANCE < m_aXDriveConfigs.Count)
    {
        Trace.TraceWarning(
            "Number of X-Drives Mount request exceeds maximum {0}",
            MAX_XDRIVES_MOUNTS_PER_INSTANCE
            );
        return false;
    }

    /*
     * Create a new Blob service client
     */
    m_blobClient
        = m_oCloudStorageAccount.CreateCloudBlobClient();

    return true;
}

Code: XDrives.MountXDrive()

  1. Validates provided XDrive configuration
  2. Using previously created Blob service client, create Blob container, which will hold page blob VHDs: CloudBlobClient.GetContainerReference()
  3. Create a new instance of a CloudDrive object from a storage account: CloudStorageAccountCloudDriveExtensions.CreateCloudDrive()
  4. If requested by XDrive configuration, then create a NTFS-formatted Windows Azure drive and its associated page blob: CloudDrive.Create()
  5. Attempt mounting XDrive: XDrives.DoCloudDriveMount()
  6. If first attempt fails mounting XDrive with access_mode write, but it is determined to try redoing mounting XDrive with access_mode readonly, a second attempt is performed: XDrives.DoCloudDriveMount()

Mounting One X-Drive

C#
/// <summary>
/// Setup X-Drive Configuration.
/// </summary>
/// <param name="dicXDriveConfig"></param>
private bool MountXDrive(
    IDictionary<string, string> dicXDriveConfig
    )
{
    try
    {
        string sXDriveLabel = string.Empty;
        string sBlobContainerName = string.Empty;
        string sPageBlobVhdName = string.Empty;
        AccessMode eAccessMode = AccessMode.None;
        bool bXDriveCreateIfNotExist = false;
        int iXDriveSizeMB = 0;
        int iXDriveMountCacheSizeMB = 0;

        if (!GetXDriveConfig(
                dicXDriveConfig,
                ref sXDriveLabel,
                ref sBlobContainerName,
                ref sPageBlobVhdName,
                ref eAccessMode,
                ref bXDriveCreateIfNotExist,
                ref iXDriveSizeMB,
                ref iXDriveMountCacheSizeMB
                )
        ) {
            return false;
        }

        /*
         * Create a blob storage container
         */
        if (!CreateXDriveBlobContainer(
            sXDriveLabel,
            sBlobContainerName
            )
        ) {
            return false;
        }

        /*
         * Get a reference to a Windows Azure Drive
         */
        CloudDrive oCloudDrive
            = m_oCloudStorageAccount.CreateCloudDrive(
                m_blobClient
                .GetContainerReference(sBlobContainerName)
                .GetPageBlobReference(sPageBlobVhdName)
                .Uri.ToString()
            );

        /*
         * If requested, try creating X-Drive
         * if not listed as mounted.
         */
        if (bXDriveCreateIfNotExist)
        {
            if (!CreateXDrive(
                oCloudDrive,
                sXDriveLabel,
                sPageBlobVhdName,
                iXDriveSizeMB
                )
            )
            {
                return false;
            }
        }

        /*
         * If a mount fails to get Write access because another
         * already as write access (ERROR_LEASE_LOCKED), 
         * then redo mount as ReadOnly.
         */
        bool bReDo = false;
        if (!DoCloudDriveMount(oCloudDrive, sXDriveLabel, iXDriveMountCacheSizeMB, eAccessMode, ref bReDo))
        {
            return false;
        }

        if (bReDo && !DoCloudDriveMount(oCloudDrive, sXDriveLabel, iXDriveMountCacheSizeMB, AccessMode.ReadOnly, ref bReDo))
        {
            return false;
        }
    }
    catch (NullReferenceException ex)
    {
        Trace.TraceError("NullReferenceException: Failed to mount X-Drive: {0}",
            ex.Message
            );
        return false;
    }
    catch (Exception ex)
    {
        Trace.TraceError("Unexpected Exception: Failed to mount X-Drive: {0}",
            ex.Message
            );
        return false;
    }

    return true;
}
Code: XDrives.CreateXDriveBlobContainer()
C#
/// <summary>
/// Create Blob Container that will hold Page Blob VHDs
/// </summary>
/// <param name="sXDriveLabel">Friendly X-Drive Label</param>
/// <param name="sBlobContainerName">Blob Container Name</param>
/// <returns>True upon success, else False</returns>
private bool CreateXDriveBlobContainer(
    string sXDriveLabel,
    string sBlobContainerName
) {
    try
    {
        m_blobClient
            .GetContainerReference(sBlobContainerName)
            .CreateIfNotExist();
    }
    catch (StorageClientException ex)
    {
        Trace.TraceError(
            "X-Drive {0}: StorageClientException: Failed to create Page Blob Container {1}: {2}",
            sXDriveLabel,
            sBlobContainerName,
            ex.Message
            );
        return false;
    }
    catch (Exception ex)
    {
        Trace.TraceError(
            "X-Drive {0}: Unexpected Exception: Failed to create Page Blob Container {1}: {2}",
            sXDriveLabel,
            sBlobContainerName,
            ex.Message
            );
        return false;
    }

    return true;
}
Code: XDrives.CreateXDrive()
C#
/// <summary>
/// Create an X-Drive
/// </summary>
/// <param name="oCloudDrive">Cloud Drive reference</param>
/// <param name="sXDriveLabel">Friendly X-Drive Label</param>
/// <param name="sPageBlobVhdName">Page Blob VHD name</param>
/// <param name="iXDriveSizeMB">Size of X-Drive in MBs</param>
/// <returns>True upon success, else False</returns>
private bool CreateXDrive(
    CloudDrive oCloudDrive,
    string sXDriveLabel,
    string sPageBlobVhdName,
    int iXDriveSizeMB
) {
    bool bSuccess = false;
    try
    {
        Trace.TraceInformation(
            "X-Drive {0}: Creating drive and its associated page blob {1}",
            sXDriveLabel,
            sPageBlobVhdName
            );
        oCloudDrive.Create(iXDriveSizeMB);
        bSuccess = true;
    }
    catch (CloudDriveException)
    {
        /*
         * Ignore: Throws this exception if drive already exists.
         */
        bSuccess = true;
    }
    catch (Exception ex)
    {
        Trace.TraceError(
            "X-Drive {0}: Unexpected Exception: Failed to create drive: {1}",
            sXDriveLabel,
            ex.Message
            );
    }

    return bSuccess;
}

Code: XDrives.DoCloudDriveMount()

  1. If mount request for XDrive is readonly, then create a readonly snapshot: CloudDrive.Snapshot(), and create a new instance of a CloudDrive object with this snapshot.
  2. Mounting XDrive is performed and results evaluated as follows: CloudDrive.Mount()
    1. If access_mode none (default), then attempt write mount first with DriveMountOptions.None:
      • If mount succeeds, then it exits with a success condition.
      • If mount fails because CloudDriveException is thrown and its error message is ERROR_LEASED_LOCKED, then page blob VHD currently has a lease locked by another web role instance that has write access mode, then it will recommend that the next mount attempt should be readonly.
      • If mount fails for any other condition, then there is not another mount attempt.
    2. If access_mode write, then attempt write mount first with DriveMountOptions.Force:
      • The terms for how to proceed will be the same as if access_mode is none, with this option it will try to break the lease lock if it exists on page blob.
    3. If access_mode readonly, then there is only one attempt with created snapshot in mounting.

Mounting X-Drive Attempt

C#
/// <summary>
/// Attempt to perform mount of X-Drive
/// </summary>
/// <param name="oCloudDrive">Cloud Drive reference</param>
/// <param name="sXDriveLabel">X-Drive Label</param>
/// <param name="iXDriveMountCacheSizeMB">X-Drive mount cache size MB</param>
/// <param name="eAccessMode"></param>
/// <param name="bReDo">Reference Redo mount attempt</param>
/// <returns>True if no error has occurred, else False</returns>
private bool DoCloudDriveMount(
    CloudDrive oCloudDrive,
    string sXDriveLabel,
    int iXDriveMountCacheSizeMB,
    AccessMode eAccessMode,
    ref bool bReDo
)
{
    /*
     * A mount is made readonly if requested by X-Drive
     * configuration or X-Drive is already mounted.
     */
    if (AccessMode.ReadOnly == eAccessMode)
    {
        CloudDrive oCloudDriveSnapshot = null;
        if (!CreateXDriveSnapshot(
                oCloudDrive, 
                sXDriveLabel,
                ref oCloudDriveSnapshot
                ))
        {
            return false;
        }

        oCloudDrive = oCloudDriveSnapshot;
    }

    if (!DoAttemptXDriveMount(
            oCloudDrive, 
            sXDriveLabel, 
            iXDriveMountCacheSizeMB, 
            eAccessMode, 
            ref bReDo
            )
    ) {
        return false;
    }

    return true;
}
Code: XDrives.CreateXDriveSnapshot()
C#
/// <summary>
/// Create snapshot of X-Drive
/// </summary>
/// <param name="oCloudDrive">Cloud Drive reference</param>
/// <param name="sXDriveLabel">X-Drive Label</param>
/// <param name="oCloudDriveSnapshot">Snapshot of Cloud Drive</param>
/// <returns>True if no error has occurred, else False</returns>
private bool CreateXDriveSnapshot(
    CloudDrive oCloudDrive,
    string sXDriveLabel,
    ref CloudDrive oCloudDriveSnapshot
) {
    try
    {
        Trace.TraceInformation(
            "X-Drive {0}: Creating snapshot",
            sXDriveLabel
            );

        Uri uriSnapshot = oCloudDrive.Snapshot();
        oCloudDriveSnapshot = m_oCloudStorageAccount.CreateCloudDrive(
            uriSnapshot.ToString()
        );
    }
    catch (CloudDriveException ex)
    {
        Trace.TraceError(
            "X-Drive {0}: CloudDriveException: Failed to create snapshot: {1}",
            sXDriveLabel,
            ex.Message
            );

        return false;
    }
    catch (Exception ex)
    {
        Trace.TraceError(
            "X-Drive {0}: Unexpected Exception: Failed to create snapshot: {1}",
            sXDriveLabel,
            ex.Message
            );
        return false;
    }

    return true;
}
Code: XDrives.DoAttemptXDriveMount()
C#
/// <summary>
/// Attempt to perform mount of X-Drive
/// </summary>
/// <param name="oCloudDrive">Cloud Drive reference</param>
/// <param name="sXDriveLabel">X-Drive Label</param>
/// <param name="iXDriveMountCacheSizeMB">X-Drive mount cache size MB</param>
/// <param name="eAccessMode"></param>
/// <param name="bReDo">Reference Redo mount attempt</param>
/// <returns>True if no error has occurred, else False</returns>
private bool DoAttemptXDriveMount(
    CloudDrive oCloudDrive,
    string sXDriveLabel,
    int iXDriveMountCacheSizeMB,
    AccessMode eAccessMode,
    ref bool bReDo
) {
    try
    {
        string sAccessMode = string.Empty;
        DriveMountOptions option = DriveMountOptions.None;
        switch (eAccessMode)
        {
            case AccessMode.ReadOnly:
                sAccessMode = "ReadOnly";
                break;
            case AccessMode.Write:
                sAccessMode = "Write";
                option = DriveMountOptions.Force;
                break;
            case AccessMode.None:
            default:
                sAccessMode = "Default";
                break;
        }

        Trace.TraceInformation("X-Drive {0}: Mounting: Mode \"{1}\"",
            sXDriveLabel,
            sAccessMode
            );

        string sDriveLetter
            = oCloudDrive.Mount(iXDriveMountCacheSizeMB, option);

        Trace.TraceInformation(
            "X-Drive {0}: Mounted: Letter \"{1}\": Mode \"{2}\", Uri \"{3}\"",
            sXDriveLabel,
            sDriveLetter,
            sAccessMode,
            oCloudDrive.Uri.ToString()
            );
    }
    catch (CloudDriveException ex)
    {
        if (ex.Message.Equals("ERROR_LEASE_LOCKED"))
        {
            if (AccessMode.ReadOnly != eAccessMode)
            {
                Trace.TraceInformation("X-Drive {0}: Mount attempt had lease locked. Redo in mode ReadOnly.",
                    sXDriveLabel
                    );

                bReDo = true;
                return true;
            }

            return false;
        }
        else
        {
            Trace.TraceError(
                "X-Drive {0}: CloudDriveException: Failed to mount: {1}",
                sXDriveLabel,
                ex.Message
                );
            return false;
        }
    }
    catch (Exception ex)
    {
        Trace.TraceError(
            "X-Drive {0}: Unexpected Exception: Failed to mount: {1}",
            sXDriveLabel,
            ex.Message
            );
        return false;
    }

    return true;
}

Global.Application_Start()

After event handler RoleEntryPoint.OnStart() has completed mounting the XDrives, event handler Global.Application_Start() proceeds by calling XDrives.SetXDrivesEnvironmentVariable():
  1. If there are not any XDrives configurations within ServiceConfiguration.cscfg, then it exits.
  2. Parses the XDrive configurations into a dictionary.
  3. Get all successfully mounted XDrives: CloudDrive.GetMountedDrives(). If there are none. then exit.
  4. Match XDrive configuration's requested path (blob container name + page blob VHD name) with one of the mounted XDrives.
  5. Create setting for environment variable X_DRIVES.

Setting Environment Variable X_DRIVES

Code: XDrives.SetXDrivesEnvironmentVariable()

C#
/// <summary>
/// Sets Environment variable X_Drives with currently mounted
/// X-Drives
/// </summary>
public void SetXDrivesEnvironmentVariable()
{
    /*
     * X-Drives configuration string
     * from ServiceConfiguration.cscfg file.
     */
    string sXDrivesConfig = ReadXDrivesConfig();
    if (null == sXDrivesConfig || 0 == sXDrivesConfig.Length)
    {
        return;
    }

    /*
     * Parse provide XDrives string from Service Configuration file.
     */
    ICollection<IDictionary<string, string>> aXDriveConfigs = ParseXDriveConfigs(sXDrivesConfig);
    if (null == aXDriveConfigs || 0 == aXDriveConfigs.Count)
    {
        Trace.TraceInformation("X-Drives: None are currently defined to be mounted.");
        return;
    }

    IDictionary<String, Uri> listDrives = CloudDrive.GetMountedDrives();
    if (0 == listDrives.Count)
    {
        Trace.TraceInformation("X-Drives: None are currently mounted");
        return;
    }

    string[] aXDrives = new string[listDrives.Count];

    int i = 0;
    foreach (KeyValuePair<String, Uri> kvpDrive in listDrives)
    {
        string sXDriveLetter = XDrives.GetDriveLetter(kvpDrive.Key);
        Uri uriXDrivePath = kvpDrive.Value;

        string sXDriveLabel;
        string sPageBlobContainer = String.Empty;
        string sPageBlobVHD = String.Empty;
        bool bReadOnly = false;

        XDrives.GetPathPageBlobVHD(
                uriXDrivePath,
                ref sPageBlobContainer,
                ref sPageBlobVHD,
                ref bReadOnly
                );

        sXDriveLabel
            = XDrives.GetXDriveLabel(
                aXDriveConfigs,
                sPageBlobContainer,
                sPageBlobVHD
                );

        string sAccess = bReadOnly ? "r" : "w";
        aXDrives[i] = String.Format("{0}=[{1},{2}]", sXDriveLabel, sXDriveLetter, sAccess);
        i++;
    }

    if (0 < aXDrives.Length)
    {
        string sXDriveEnv = String.Join(";", aXDrives);

        Trace.TraceInformation("{0} = {1}", XDRIVES_ENV_VAR, sXDriveEnv);

        Environment.SetEnvironmentVariable(XDRIVES_ENV_VAR, sXDriveEnv);
    }
}

Code: XDrives.GetPathPageBlobVHD()

C#
/// <summary>
/// Get Page Blob VHD Path Parts.
/// </summary>
/// <param name="uri">Page Blob VHD URI</param>
/// <param name="sPageBlobContainer">OUT Page Blob Container name</param>
/// <param name="sPageBlobVHD">Page Blob VHD name</param>
public static void GetPathPageBlobVHD(
    Uri uri,
    ref string sPageBlobContainer,
    ref string sPageBlobVHD,
    ref bool bReadOnly
    )
{
    sPageBlobContainer = String.Empty;
    sPageBlobVHD = String.Empty;

    string sPath;
    string sPathAndQuery = uri.PathAndQuery;
    string sQuery = uri.Query;

    if (null != sQuery && sQuery.Length > 0)
    {
        sPath = sPathAndQuery.Substring(0, sPathAndQuery.Length - sQuery.Length);
        bReadOnly = sQuery.StartsWith("?snapshot");
    }
    else
    {
        sPath = sPathAndQuery;
        bReadOnly = false;
    }

    char[] aPathDelims = { '/' };
    sPath = sPath.TrimStart(aPathDelims);

    if (sPath.StartsWith(DEV_STORAGE_ACCT_PREFIX))
    {
        sPath = sPath.Substring(DEV_STORAGE_ACCT_PREFIX.Length);
        sPath = sPath.TrimStart(aPathDelims);
    }

    int iPageBlobContainerDelim = sPath.IndexOf('/');
    sPageBlobContainer = sPath.Substring(0, iPageBlobContainerDelim);
    sPageBlobVHD = sPath.Substring(iPageBlobContainerDelim + 1);
}

RoleEntryPoint.OnStop()

Finally, called by Windows Azure when the role instance is to be stopped, XDrives.UnmountXDrives():
  1. If there are not any XDrives configurations within ServiceConfiguration.cscfg, then it exits.
  2. Get all successfully mounted XDrives: CloudDrive.GetMountedDrives(). If there are none. then exit.
  3. Loop through the page blob VHD path for each mounted XDrive, and create CloudDrive object: CloudStorageAccountCloudDriveExtensions.CreateCloudDrive()
  4. Unmount each: CloudDrive.Unmount()

Unmounting X-Drives

C#
/// <summary>
/// Unmount a VHDs based upon settings within ServiceConfiguration.cscfg file.
/// </summary>
public void UnmountXDrives()
{
    try
    {
        /*
         * X-Drives configuration string
         * from ServiceConfiguration.cscfg file.
         */
        string sXDrivesConfig = ReadXDrivesConfig();
        if (null == sXDrivesConfig || 0 == sXDrivesConfig.Length)
        {
            return;
        }

        if (null == m_oCloudStorageAccount)
        {
            return;
        }

        /*
         * Get a listing of all X-Drives currently mounted.
         */
        IDictionary<String, Uri> listDrives = CloudDrive.GetMountedDrives();
        if (0 == listDrives.Count)
        {
            return;
        }

        Trace.TraceInformation("Unmounting X-Drives");

        /*
         * Iterate over each mounted X-Drive and
         * unmount them.
         */
        foreach (KeyValuePair<String, Uri> kvpDrive in listDrives)
        {
            string sXDriveLetter = GetDriveLetter(kvpDrive.Key);
            Uri uriXDrivePath = kvpDrive.Value;

            try
            {
                CloudDrive myCloudDrive = m_oCloudStorageAccount.CreateCloudDrive(
                    uriXDrivePath.ToString()
                );

                myCloudDrive.Unmount();
                Trace.TraceInformation(
                    "X-Drive {0}: Unmounted {1}",
                    sXDriveLetter,
                    uriXDrivePath.ToString()
                    );
            }
            catch (CloudDriveException ex)
            {
                Trace.TraceError(
                    "X-Drive {0}: CloudDriveException: Failed to unmount {1}: {2}",
                    sXDriveLetter,
                    uriXDrivePath.ToString(),
                    ex.Message
                    );
            }
            catch (Exception ex)
            {
                Trace.TraceError(
                    "X-Drive {0}: Unexpected Exception: Failed to unmount {1}: {2}",
                    sXDriveLetter,
                    uriXDrivePath.ToString(),
                    ex.Message
                    );
            }
        }
    }
    catch (Exception ex)
    {
        Trace.TraceError(
            "Unexpected Exception: Failed to unmount X-Drives: {0): {1)",
            ex.GetBaseException(),
            ex.Message
            );
    }
}

Service Configuration Settings

To access XDrives functionality with XDrives.cs, there are three required configurations that need to be performed within trong>ServiceConfiguration.cscfg and ServiceDefinition.csdef files:
  1. Define one LocalStorage for XDrives.
  2. Define which LocalStorage will be used by XDrives.
  3. Define configuration for each XDrive to be mounted.

Local Storage for XDrive Mount Read Cache

In a new or existing cloud service, make sure you have a LocalStorage defined in ServiceDefinition.csdef. This local storage will be used by the Windows Azure Drive API to cache virtual hard disks on the virtual machine that is running, enabling faster access times. For XDrives, provide only one reference to LocalStorage in order to define the read cache for all subsequently mounted drives associated with the role instance. For example, a LocalStorage was added with name MyXDrivesLocalCache with a size of 1000 MB.

Adding LocalStorage for X-Drives' Mount Read Cache

Since there can be multiple LocalStorages added to LocalResources, class XDrives will need to know the name of which LocalStorage that was specifically created for XDrives' local cache. For this example, configuration setting XDrivesLocalCache will reference the name of the LocalStorage MyXDrivesLocalCache.

Add Setting XDrivesLocalCache

New Service Setting: "XDrives"

This next setting is used for defining how to configure all XDrives should be mounted. Its value contains a listing of all mounted XDrives to be associated with this web role instance. Here is an example of a single XDrive configuration, explanation will follow later in this article:
[xdrive_label=MyDriveA,blob_container=MyDrives,page_blob_vhd=MyDriveA.vhd,xdrive_sizeMB=40,mount_cache_sizeMB=10,create=Yes];

Add Setting XDrives

Example Modification of ServiceDefinition.csdef and ServiceConfiguration.cscfg

The following setting will modify >ServiceDefinition.csdef as follows:
XML
<ServiceDefinition >
    <WebRole name="WebRole">

        <ConfigurationSettings>
        </ConfigurationSettings>

        <LocalResources>
            <LocalStorage name="MyXDrivesLocalCache"
                          cleanOnRoleRecycle="false"
                          sizeInMB="1000" />
        </LocalResources>
    </WebRole>
</ServiceDefinition>
The last two settings will modify ServiceConfiguration.cscfg as follows:
XML
  <ConfigurationSettings>
    <!-- Windows Azure XDrive Configuration Settings -->
    <Setting name="XDrives" value="
    [xdrive_label=MyDriveA,blob_container=MyDrives,page_blob_vhd=MyDriveA.vhd,xdrive_sizeMB=40,mount_cache_sizeMB=10,create=Yes];"/>

    <Setting name="XDrivesLocalCache" value="MyXDrivesLocalCache" />
  <ConfigurationSettings>

Configuring Windows Azure Drives

At this point, defining web role to handle mounting XDrives is complete. Next is need to understand how to configure XDrive mounting within value of setting XDrives.

Configuration Structure

The value for setting XDrives is semi-colon delimited string, and each value is a XDrive configuration request. "[x_drive_config];[x_drive_config];[x_drive_config];..." Each x_drive_config request is a bracked set of comma delimited configuration settings, and each setting is a key value pair. [key=value,key=value,key=value,...]

Configuration Legend

This a legend of all the available keys and their expected values that can be used to define an XDrive configuration request.
xdrive_label
Required: Friendly Label to Identify XDrive configuration with XDrive's letter assigned after mounting.
blob_container
Required: Blob Container name.
Values: 3 through 63 lower-case characters long. See Naming Containers, Blobs, and Metadata.
page_blob_vhd
Required: Page Blob name.
Values: 1 to 1024 any combination of characters long. See Naming Containers, Blobs, and Metadata.
xdrive_sizeMB
Required if Creating XDrive:
Values: Integer from 16 to 1000000 MB
mount_cache_sizeMB
Optional: Mount Read Cache Size.
Values: Integer from 0 to xdrive_sizeMB
Default: 0 - No read cache is used during mount.
create
Optional: Create XDrive if it does not exist.
Values: Yes|No
Default: No
access_mode
Optional: Forces XDrive to mount a specific access mode versus getting available access mode.
Values:
none - Assign currently available access. If first mount, then assign XDrive with write access mode, else read-only.
write - Force lease lock to break of a another XDrive mount having write access mode, and assign this mount with write access mode. Note: Not functional at this time. See Current Bugs section below.
readonly - Assign this mount with read-only access mode, regardless if mount is available with write access mode.
Default: none

Configuration Examples

To illustrate how source XDrives.cs services XDrives, it will be presented by an assortment of XDrive configurations.
[xdrive_label=MyDriveA,blob_container=mydrives,page_blob_vhd=MyDriveA.vhd]
  1. XDrive expects to mount upon an existing page blob path mydrives/MyDriveA.vhd, and it will succeed mounting if it exists.
  2. By not providing mount_cache_sizeMB value, thereby set mount for unbuffered access to the drive.
  3. If this page blob exists and is not mounted by another role instance, then the XDrive will have write access, else read-only.
[xdrive_label=MyDriveA,blob_container=mydrives,page_blob_vhd=MyDriveA.vhd,xdrive_sizeMB=10,create=Yes]
  1. XDrive expects to mount upon a page blob path mydrives/MyDriveA.vhd, and if it does not exist then it will be created before mounting.
  2. When requesting to create an page blob to be associated with XDrive, key xdrive_sizeMB is required.
  3. The size of the XDrive to create is 10 MBs. The minimum size permitted is 16 MB.
  4. If this page blob exists and is not mounted by another role instance, then the XDrive will have write access, else read-only.
  5. If this page blob does not exist, then the XDrive will create this page blob and have write access.
[xdrive_label=MyDriveA,blob_container=mydrives,page_blob_vhd=MyDriveA.vhd,mount_cache_sizeMB=10,readonly=Yes]
  1. XDrive expects to mount upon an existing page blob path mydrives/MyDriveA.vhd, and it will succeed mounting if it exists.
  2. If this page blob exists, then the XDrive will have read-only access, regardless if it is mounted by another role instance or not.
  3. The size of the read cache for this mounted XDrive is 10 MBs.

Code: XDrives.ParseXDriveConfigs()

This following code is used for parsing the XDrives setting from ServiceConfiguration.cscfg.

C#
/// <summary>
/// Parse returned mounting configuration requests for XDrives
/// </summary>
/// <param name="sXDrivesConfigs">string</param>
/// <returns>Collection defining each X-Drive configuration</returns>
public ICollection<IDictionary<string, string>> ParseXDriveConfigs(string sXDrivesConfigs)
{
    ICollection<IDictionary<string, string>> aXDrivesConfigs = new Collection<IDictionary<string, string>>();

    char[] aCloudDrivesConfigsDelims = { ';' };
    string[] aXDrivesConfigsSplit = sXDrivesConfigs.Split(aCloudDrivesConfigsDelims);

    foreach (string sXDriveConfigRaw in aXDrivesConfigsSplit)
    {
        string sXDriveConfigTrim = " []";
        char[] aXDriveConfigTrim = sXDriveConfigTrim.ToCharArray();
        string sXDriveConfig = sXDriveConfigRaw.Trim(aXDriveConfigTrim);

        if (0 == sXDriveConfig.Length)
        {
            continue;
        }

        char[] aXDriveConfigKeyValuesDelims = { ',' };
        string[] aXDriveConfigKeyValues = sXDriveConfig.Trim().Split(aXDriveConfigKeyValuesDelims);

        IDictionary<string, string> dicXDriveConfigKeyValue = new Dictionary<string, string>();

        foreach (string sXDriveConfigKeyValue in aXDriveConfigKeyValues)
        {
            string sXDriveConfigKeyValueDelimChars = "=";
            char[] aXDriveConfigKeyValueDelims = sXDriveConfigKeyValueDelimChars.ToCharArray();
            string[] aXDriveConfigKeyValue = sXDriveConfigKeyValue.Trim().Split(aXDriveConfigKeyValueDelims);

            string sXDriveConfigKey = aXDriveConfigKeyValue[0].Trim();
            string sXDriveConfigValue = aXDriveConfigKeyValue[1].Trim();

            if (0 == sXDriveConfigKey.Length)
            {
                Trace.TraceWarning("XDrive configuration has an undefined key",
                        sXDriveConfigKey
                        );
                continue;
            }

            if (dicXDriveConfigKeyValue.ContainsKey(sXDriveConfigKey))
            {
                Trace.TraceWarning("XDrive configuration has a duplicate key {0}",
                        sXDriveConfigKey
                        );
                continue;
            }

            if (0 == sXDriveConfigValue.Length)
            {
                Trace.TraceWarning("XDrive configuration has undefined value for key {0}",
                        sXDriveConfigKey
                        );
                continue;
            }

            dicXDriveConfigKeyValue.Add(sXDriveConfigKey, sXDriveConfigValue);
        }

        aXDrivesConfigs.Add(dicXDriveConfigKeyValue);
    }

    return 0 == aXDrivesConfigs.Count ? null : aXDrivesConfigs;
}

Packaging a Web Application to access Windows Azure Drives

For this XDrive access solution to work, the deployment will need to be packaged with the following files:
  1. WebRole.dll - Build using VS 2008 solution WindowsAzure_WebRole_XDrives, required for mounting and unmounting XDrives.
  2. ServiceDefinition.csdef - Defining LocalStorage to be used by XDrives.
  3. ServiceConfiguration.cscfg - Setting configuration for all XDrives to be mounted by WebRole.dll
  4. Global.asax - Global.Application_Start() event handler is specified within this file, which it is called to sets environment variable X_DRIVES within this VM, the XDrives status link for the web application within this deployment before it started.
  5. Web Application - Download provides two web applications (PHP and C#) that demonstrates access to mounted XDrives based upon provided value within environment variable X_DRIVES.

Testing access to Windows Azure Drives

Following the guidelines set for packaging this solution for mounting and accessing XDrives, this section will present an example run of two web application deployments using the same service configuration settings for mounting XDrives.

Sample Web Applications accessing XDrives

As stated, within download there two web applications (C# and PHP) for validating that XDrives are being mounted on start of Web Role DLL Both web applications do the following:
  1. Read X_DRIVES environment variable that should have been set by Web Role DLL.
  2. Parse it into individual XDrive mounting information
  3. If a provided XDrive is declared as writable, then create and write files within root directory of provided drive letter.
  4. Read contents of all files within root directory of provided drive letter.

Sample Configuration of XDrives

Here we will attempt to mount XDrives using the same configuration setting in two different web application deployments. The example setting XDrives within ServiceConfiguration.cscfg is to mount two XDrives labeled MyDriveA and MyDriveB:
XML
[xdrive_label=MyDriveA,blob_container=mydrives,page_blob_vhd=MyDriveA.vhd,xdrive_sizeMB=40,create=Yes];
[xdrive_label=MyDriveB,blob_container=mydrives,page_blob_vhd=MyDriveB.vhd,xdrive_sizeMB=40,mount_cache_sizeMB=20,create=Yes];

First Deployment

Packaged web application is deployed to run within Development Fabric using the aforementioned XDrives configuration value to mount two XDrives. Viewing the progress of the first deployment within Development Fabric UI, the deployment's log console shows that during RoleEntryPoint.OnStart() the mounting for both XDrives were successful:
[runtime] Role entrypoint . CALLING OnStart()
[WaWebHost.exe] RoleEntryPoint.OnStart()
[WaWebHost.exe] Get XDrives Configuration
[WaWebHost.exe] Mounting XDrives
[WaWebHost.exe] XDrive MyDriveA: Reading configuration
[WaWebHost.exe] XDrive MyDriveA: Mounted: Letter "a:": Mode "Default", Uri "http://127.0.0.1:10000/devstoreaccount1/myxdrives/MyDriveA.vhd"
[WaWebHost.exe] XDrive MyDriveB: Reading configuration
[WaWebHost.exe] XDrive MyDriveB: Mounted: Letter "b:": Mode "Default", Uri "http://127.0.0.1:10000/devstoreaccount1/myxdrives/MyDriveB.vhd"
[runtime] Role entrypoint . COMPLETED OnStart()
[runtime] Role entrypoint . CALLING Run()
[WaWebHost.exe] X_DRIVES = MyDriveA=[a,w];MyDriveB=[b,w]
[fabric] Role state Started
And Global.Application_Start() posts the following environment variable setting within the VM of the first deployment: X_DRIVES = MyDriveA=[a,w];MyDriveB=[b,w] Explanation of what occurred is that neither of requested page blob VHD paths existed (mydrives/MyDriveA.vhd and mydrives/MyDriveB.vhd), then both were created and mounted with writable access.
  • MyDriveA - Drive Letter: a, Access Mode: Writable (w)
  • MyDriveB - Drive Letter: b, Access Mode: Writable (w)
For first deployment, this is the default page results after creating, writing, and reading files for both XDrives:

First deployment with mounted XDrives

Second Deployment

While, the first deployment is still running, the same packaged web application is deployed again to run within Development Fabric using the same XDrives configuration value to mount two XDrives. Viewing the progress of the first deployment within Development Fabric UI, the deployment's log console shows that during RoleEntryPoint.OnStart() the mounting for both XDrives were successful, but the mounting results are different from the first deployment:
[runtime] Role entrypoint . CALLING OnStart()
[WaWebHost.exe] Get XDrives Configuration
[WaWebHost.exe] Mounting XDrives
[WaWebHost.exe] XDrive MyDriveA: Reading configuration
[WaWebHost.exe] XDrive MyDriveA: Mount attempt had lease locked. Redo in mode ReadOnly.
[WaWebHost.exe] XDrive MyDriveA: Creating snapshot
[WaWebHost.exe] XDrive MyDriveA: Mounting: Mode "ReadOnly"
[WaWebHost.exe] XDrive MyDriveA: Mounted: Letter "e:": Mode "ReadOnly", Uri "http://127.0.0.1:10000/devstoreaccount1/myxdrives/MyDriveA.vhd?snapshot=2010-04-17T03:33:08.8070000Z"
[WaWebHost.exe] XDrive MyDriveB: Reading configuration
[WaWebHost.exe] XDrive MyDriveB: Mount attempt had lease locked. Redo in mode ReadOnly.
[WaWebHost.exe] XDrive MyDriveB: Creating snapshot
[WaWebHost.exe] XDrive MyDriveB: Mounting: Mode "ReadOnly"
[WaWebHost.exe] XDrive MyDriveB: Mounted: Letter "f:": Mode "ReadOnly", Uri "http://127.0.0.1:10000/devstoreaccount1/myxdrives/MyDriveB.vhd?snapshot=2010-04-17T03:33:11.9620000Z"
[runtime] Role entrypoint . COMPLETED OnStart()
[runtime] Role entrypoint . CALLING Run()
[WaWebHost.exe] X_DRIVES = MyDriveA=[e,r];MyDriveB=[f,r]
[fabric] Role state Started
And Global.Application_Start() posts the following environment variable setting within the VM of the second deployment, which has a different setting from the first deployment: X_DRIVES = MyDriveA=[f,r];MyDriveB=[g,r] Explanation of what has occurred is that both of the requested page blob VHD paths existed (mydrives/MyDriveA.vhd and mydrives/MyDriveB.vhd) and were both already mounted with leases locked (write access) by the previous and still running deployment, thereby this deployment will be provide with read-only access to both XDrives by creating snapshots of page blob VHD paths and then mounting them.
  • MyDriveA - Drive Letter: f, Access Mode: Readonly (r)
  • MyDriveB - Drive Letter: g, Access Mode: Readonly (r)
For second deployment while first deployment is still running, this is the default page results after only reading files for both XDrives:

Second deployment with mounted XDrives

Final Result of Two Deployments

In the end, there are two concurrent deployments:

two_deployments.png

  1. First deployment can write to page blob VHDs represented by MyDriveA and MyDriveB.
  2. Second deployment can read a snapshot of what First deployment wrote within page blob VHDs represented by MyDriveA and MyDriveB up to the point of its read-only mounting. Afterwards, because it is a snapshot, no matter what the First deployment writes within these page blob VHDs, the Second deployment will not see these changes unless it performs another snapshot.
If the First deployment had ended and it successfully unmounted the XDrives it had a lease to write to page blob VHDs, then the lease lock would be broken and these page blobs would be available to be modified by the next mount request. If this action occurred before the Second deployment had started, then the Second would have write access to these page blobs instead of a snapshot.

Points of Interest

Initial Posting

Within Windows Azure Forum in March 2010, I started a discussion that pertains to this article: Proposal: Defining Multiple X-Drives within ServiceConfiguration.cscfg.

Another Approach

Maarten Balliauw is Technical Consultant at RealDolmen in Belgium. We have been working remotely together on PHP solutions for Windows Azure platform. Unbeknownst to either of us, we were writting articles that addresses a similar solution. To his credit, this is his blog article: Configuring and using Windows Azure Drive.

Windows Azure Command-line Tools for PHP Developers

The approach presented in this article in providing access to Windows Azure Drives was adopted within the following open source project hosted upon CodePlex: Windows Azure Command-line Tools for PHP Developers. More about this tool will be presented in Part 2 of this article.

Development Fabric Limitation

Only within Development fabric, DriveMountOptions.Force is currently not implemented to allow a mounting of an XDrive to break a lease lock of another mounting that has write access. In XDrives.cs, method XDrives.DoAttemptXDriveMount() will attempt to break lease lock with DriveMountOptions.Force when calling CloudDrive.Mount(), if provided XDrive's configuration specifically requests access_mode to be write.

Problems Not Unmounting XDrives Properly

At the time of writing this article, I have experienced that if XDrives were not properly unmounted at the end of a deployment, this can cause problems with the next deployment trying to mount the same page blob VHDs; like hanging indefinitely when calling CloudDrive.Mount(). As would be expected, this problem occurred a lot during the development of this solution, and my resolution was to restart both the Development Fabric and especially the Development Storage. And to my suprise during development, this problem would also occur when removing deployments manually within Development Fabric UI. If I had right-clicked a running deployment and selected Remove, then the next deployment hangs. The proper action is to first select Suspend, wait till it is finished suspending and deployment's dot goes red, and then select Remove. Only by selecting Suspend is the callback RoleEntryPoint.OnStop() initiated, thereby calling this solution's method XDrives.UnmountCloudDrives() within the WebRole.dll.

Conclusion

Windows Azure Drives is a great tool for the Windows Azure Platform in building its story in providing Infrastructure as a Service (IaaS) solutions. However, to truly understand it is to read Windows Azure Drive Whitepaper, and read the threads within Windows Azure Forum and blog of Maarten Balliauw.

Recognition

Windows Azure Forum: There were several Microsoft moderators and contributors of this forum who provided answers to my many questions pertaining to Windows Azure Drives: Allen Chen, Andrew Edwards, Yi-Lun Luo, and Neil Mackenzie.

License

This article, along with any associated source code and files, is licensed under The BSD License