Esta es la presentación de Web sites del WAPU en Colombia, esta presentación es parte del
Azure tranning Kit. Este es el kit de entrenamiento de Azure el cual es muy recomnedable para aprender a utilizar la Nube.
Links relacionados
Esta es la presentación de Web sites del WAPU en Colombia, esta presentación es parte del
Azure tranning Kit. Este es el kit de entrenamiento de Azure el cual es muy recomnedable para aprender a utilizar la Nube.
Links relacionados
Para el evento llamado Windows Azure Partner Academy, presenté como utilizar Windows Azure media Services de manera programática utilizando el SDK. La idea de la demostración es poder automatizar el flujo de ingestión, codificación y publicación de contenido en Media Services. En este post, se explica el código utilizado en esa demostración.
El propósito del ejemplo es tomar un video desde el disco local, subirlo a windows Azure Media Services, codificarlo primero en H264 y luego en HLS para que pueda ser visto por los sistemas IOS como es un Iphone, Ipad, etc. Todo esto de manera automática.
Para poder implementar un cliente que se conecte a Media Services y utilice las API para la ingestión, codificación, empaquetamiento y publicación del contenido se requieren los componentes y configuración en tú equipo.
Para comenzar, se crea un proyecto de consola del tipo C# en Visual Studio. Para que este programa pueda acceder a Media Services se debe utilizar el Windows Azure media Services SDK for .NET. Este se puede obtener de manera muy simple utilizando NugetPackage como se ve en la siguiente figura.

Con esta referencia ya podemos hacer código que se comunique con Media Services.
Ahora, debemos obtener las Manage Keys del servicio. Para ello vamos al portal, entramos al servicio y las obtenemos utilizando el botón del menú de la barra inferior, como se muestra en la siguiente figura.

La idea es que utilicemos el Account Name y Primary Media Service Access Key para obtener el contexto del servicio y así poder comenzar a trabajar con media Services.
static CloudMediaContext ObtenerContexto()
{
return new CloudMediaContext(_accountName, _accountKey);
}
Para subir el archivo, vamos a utilizar el método Subir1ArchivoCrear que recibe como argumentos la opción de creación del Asset y el nombre del archivo que contiene el video. Las opciones de creación definen por ejemplo si el Asset estará cifrado en el storage. Para efectos de nuestro ejemplo usamos la opción por None. El método lo que hace es crear un nombre para identificar el Asset en Media Services, crearlo con ese nombre vacío y subir el video a media services.
static IAsset Subir1ArchivoCrear(AssetCreationOptions assetCreationOptions, string singleFilePath)
{
//1. Crear el nomrbe del Asset en Media Services
var assetName = "TMP_UploadSingleFile_" + DateTime.UtcNow.ToString();
var asset = CreateEmptyAsset(assetName, assetCreationOptions);
var fileName = Path.GetFileName(singleFilePath);
//2. Crear un Asset vacio
var assetFile = asset.AssetFiles.Create(fileName);
_PrefijoNombreAsset = assetFile.Name;
Console.WriteLine("Creado el Archivo {0}", assetFile.Name);
//3. Crear la Politica
var accessPolicy = _contexto.AccessPolicies.Create(assetName, TimeSpan.FromDays(30),
AccessPermissions.Write | AccessPermissions.List);
//4. Crear el Locator
var locator = _contexto.Locators.CreateLocator(LocatorType.Sas, asset, accessPolicy);
Console.WriteLine("Upload {0}", assetFile.Name);
//5. Se crea un blob transfer para subir el video a Media Services
var blobTransferClient = new BlobTransferClient();
blobTransferClient.NumberOfConcurrentTransfers = 20;
blobTransferClient.ParallelTransferThreadCount = 20;
//6. se define el evento que muestra el avance del upload
blobTransferClient.TransferProgressChanged += blobTransferClient_TransferProgressChanged;
//7. Se genera un task list asincrono para subir el archivo
var uploadTasks = new List<Task>();
uploadTasks.Add(assetFile.UploadAsync(singleFilePath, blobTransferClient, locator, CancellationToken.None));
//8. Se espera que termine de subir
Task.WaitAll(uploadTasks.ToArray());
Console.WriteLine("Done uploading of {0}", assetFile.Name);
locator.Delete();
accessPolicy.Delete();
return asset;
}
El primer trabajo de codificación es al formato H264. Los Jobs son trabajos de media Services que están compuestos por tareas, pudiendo un Job contener varias tareas. En este caso, por simpleza del ejemplo, vamos a crear un Job con solo una tarea.
En la tarea se especifica el tipo de codificación que se utilizará y la codificación del Asset de salida. La codificación utilizada es estándar y se especifica como «H264 Smooth Streaming 720p».
Luego de ejecutar el JOB, en vez de utilizar progressJobTask.Wait() que solo muestra cuando el JOB termina, se utiliza código para mostrar avance en porcentaje, como en el portal. Por último, se retorna la referencia al JOB una vez finalizado.
static IJob CrearTrabajoCod_H264Smooth(IAsset asset)
{
//1. Crear un Nuevo JOB
IJob job = _contexto.Jobs.Create("Primer JOB de encode: H264 Smooth Streaming 720p");
// Get a media processor reference, and pass to it the name of the
// processor to use for the specific task.
IMediaProcessor processor = GetLatestMediaProcessorByName("Windows Azure Media Encoder");
//2. Se crea una tarea con los detalles de la codificación.
//en este caso "H264 Adaptive Bitrate MP4 Set 720p"
ITask task = job.Tasks.AddNew("task: H264 Smooth Streaming 720p",processor,
"H264 Smooth Streaming 720p", TaskOptions.ProtectedConfiguration);
//3. Especifica el Asset a codificar
task.InputAssets.Add(asset);
//4. Crea y especifica el Asset de salida, sin cifrado
string nombreContenido = _PrefijoNombreAsset + "_H264";
task.OutputAssets.AddNew(nombreContenido,AssetCreationOptions.None);
//5. El manejador de evento que muestra el avance del job
job.StateChanged += new EventHandler<JobStateChangedEventArgs>(StateChanged);
//6. Se ejecuta el JOB
job.Submit();
//7. Validar el estado del Job
Task progressJobTask = job.GetExecutionProgressTask(CancellationToken.None);
//8. en vez de utilizar progressJobTask.Wait(); que solo muestra cuando el JOB termina
//se utiliza el siguiente codigo para mostrar avance en porcentaje, como en el portal
double avance = 0;
while ((job.State != JobState.Finished) && (job.State != JobState.Canceled) && (job.State != JobState.Error))
{
if (job.State==JobState.Processing)
{
if (avance != (job.Tasks[0].Progress / 100))
{
avance = job.Tasks[0].Progress / 100;
Console.WriteLine("Percent complete: {0}", avance.ToString("#0.##%"));
}
}
Thread.Sleep(TimeSpan.FromSeconds(5));
}
// 9. se regresa el la referencia al JOB
job = GetJob(job.Id);
return job;
}
Una vez codificado el video tendremos 2 Assets en media Services. El video original y el codificado a H264. Ya no necesitamos el original, por lo cual lo borramos utilizando el siguiente método.
static void BorrarAsset(IAsset asset)
{
// delete the asset
asset.Delete();
// Verify asset deletion
if (GetAsset(asset.Id) == null)
Console.WriteLine("Deleted the Asset");
}
Como el objetivo es poder mostrar el video en IOs también, entonces debemos codificarlo en HLS para que así los Iphone, Ipad, etc puedan verlo. Para ellos vamos a crear un JOB, con una tarea que haga la codificación. Ahora, para especificar la codificación, a diferencia a lo hecho en el paso anterior con el formato H264, vamos a especificar la codificación con el siguiente archivo XML.
<?xml version="1.0" encoding="utf-8" ?>
<taskDefinition xmlns="http://schemas.microsoft.com/iis/media/v4/TM/TaskDefinition#">
<name>Smooth Streams to Apple HTTP Live Streams</name>
<id>A72D7A5D-3022-45f2-89B4-1DDC5457C111</id>
<description xml:lang="en">Converts on-demand Smooth Streams encoded with H.264 (AVC) video and AAC-LC audio codecs to Apple HTTP Live Streams (MPEG-2 TS) and creates an Apple HTTP Live Streaming playlist (.m3u8) file for the converted presentation.</description>
<inputDirectory></inputDirectory>
<outputFolder>TS_Out</outputFolder>
<properties namespace="http://schemas.microsoft.com/iis/media/AppleHTTP#" prefix="hls">
<property name="maxbitrate" required="true" value="1600000" helpText="The maximum bit rate, in bits per second (bps), to be converted to MPEG-2 TS. On-demand Smooth Streams at or below this value are converted to MPEG-2 TS segments. Smooth Streams above this value are not converted. Most Apple devices can play media encoded at bit rates up to 1,600 Kbps."/>
<property name="manifest" required="false" value="" helpText="The file name to use for the converted Apple HTTP Live Streaming playlist file (a file with an .m3u8 file name extension). If no value is specified, the following default value is used: <ISM_file_name>-m3u8-aapl.m3u8"/>
<property name="segment" required="false" value="10" helpText="The duration of each MPEG-2 TS segment, in seconds. 10 seconds is the Apple-recommended setting for most Apple mobile digital devices."/>
<property name="log" required="false" value="" helpText="The file name to use for a log file (with a .log file name extension) that records the conversion activity. If you specify a log file name, the file is stored in the task output folder." />
<property name="encrypt" required="false" value="false" helpText="Enables encryption of MPEG-2 TS segments by using the Advanced Encryption Standard (AES) with a 128-bit key (AES-128)." />
<property name="pid" required="false" value="" helpText="The program ID of the MPEG-2 TS presentation. Different encodings of MPEG-2 TS streams in the same presentation use the same program ID so that clients can easily switch between bit rates." />
<property name="codecs" required="false" value="false" helpText="Enables codec format identifiers, as defined by RFC 4281, to be included in the Apple HTTP Live Streaming playlist (.m3u8) file." />
<property name="backwardcompatible" required="false" value="false" helpText="Enables playback of the MPEG-2 TS presentation on devices that use the Apple iOS 3.0 mobile operating system." />
<property name="allowcaching" required="false" value="true" helpText="Enables the MPEG-2 TS segments to be cached on Apple devices for later playback." />
<property name="passphrase" required="false" value="" helpText="A passphrase that is used to generate the content key identifier." />
<property name="key" required="false" value="" helpText="The hexadecimal representation of the 16-octet content key value that is used for encryption." />
<property name="keyuri" required="false" value="" helpText="An alternate URI to be used by clients for downloading the key file. If no value is specified, it is assumed that the Live Smooth Streaming publishing point provides the key file." />
<property name="overwrite" required="false" value="true" helpText="Enables existing files in the output folder to be overwritten if converted output files have identical file names." />
</properties>
<taskCode>
<type>Microsoft.Web.Media.TransformManager.SmoothToHLS.SmoothToHLSTask, Microsoft.Web.Media.TransformManager.SmoothToHLS, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</type>
</taskCode>
</taskDefinition>
Este archivo le indica al encoder de media Services que va a recibir un video H264 (AVC) video y AAC-LC audio y que lo convierta a Apple HTTP Live Streams (MPEG-2 TS).
El método CreateSmoothToHlsJob utiliza el archivo de configuración para crear la tarea de codificación en base a esa configuración.
public static IJob CreateSmoothToHlsJob(string configFilePath, IAsset asset)
{
// 1. Usamos la configuración de codificacón desde un Archivo XML
string configuration = File.ReadAllText(Path.GetFullPath(configFilePath
+ @"\MediaPackager_SmoothToHLS.xml"));
//2. Valida que el Asset contenga un solo archivo ISM
var ismAssetFiles = asset.AssetFiles.ToList().
Where(f => f.Name.EndsWith(".ism", StringComparison.OrdinalIgnoreCase)).ToArray();
if (ismAssetFiles.Count() != 1)
throw new ArgumentException("The asset should have only one, .ism file");
ismAssetFiles.First().IsPrimary = true;
ismAssetFiles.First().Update();
// 3. Creal el JOB
IJob job = _contexto.Jobs.Create("My Smooth Streams to Apple HLS job");
IMediaProcessor processor = GetLatestMediaProcessorByName("Windows Azure Media Packager");
// 4. Crea la tarea con los detalles de codificación del archivo XML
ITask task = job.Tasks.AddNew("My Smooth to HLS Task",
processor,
configuration,TaskOptions.ProtectedConfiguration);
// 5. Define el Asset de entrada
task.InputAssets.Add(asset);
// 6. Crea el Asset de Salida con el sufijo HLS.
string nombreContenido = _PrefijoNombreAsset + "_HLS";
task.OutputAssets.AddNew(nombreContenido, AssetCreationOptions.None);
//7. define el manejador del evento
job.StateChanged += new EventHandler<JobStateChangedEventArgs>(StateChanged);
//8. Lanza el JOB
job.Submit();
//9. Revisa el estado de ejecución del JOB
Task progressJobTask = job.GetExecutionProgressTask(CancellationToken.None);
//10. en vez de utilizar progressJobTask.Wait(); que solo muestra cuando el JOB termina
//se utiliza el siguiente codigo para mostrar avance en porcentaje, como en el portal
double avance = 0;
while ((job.State != JobState.Finished) && (job.State != JobState.Canceled) && (job.State != JobState.Error))
{
if (job.State == JobState.Processing)
{
if (avance != (job.Tasks[0].Progress / 100))
{
avance = job.Tasks[0].Progress / 100;
Console.WriteLine("Percent complete: {0}", avance.ToString("#0.##%"));
}
}
Thread.Sleep(TimeSpan.FromSeconds(5));
}
//11. regresa la referencia al JOB terminado
return job;
}
Para esto se debe obtener la referencia al manifiesto del Asset, el archivo de extensión ISM. Al publicar debemos especificar la política de uso de lo que estamos publicando, en este caso le vamos a decir que se publica solo lectura por 30 días. Inmediatamente después debemos crear un origen donde localizar nuestro manifiesto y asignarle una URL. Esta es la que utilizaran los clientes para acceder al contenido. Ahora, en el caso de ser HLS debemos agregar «(format=m3u8-aapl)» a la URL.
public static ILocator GetStreamingOriginLocator(string targetAssetID, MediaContentType contentType)
{
// 1. Obtener la referencia al Asset que se quiere publicar
IAsset assetToStream = GetAsset(targetAssetID);
//2. Obtener la referencia del manifiesto
var theManifest =
from f in assetToStream.AssetFiles
where f.Name.EndsWith(".ism")
select f;
// 3. crear una referencia al manifiesto
IAssetFile manifestFile = theManifest.First();
//3. Crear la politica de acceso de 30 dias solo lectura.
IAccessPolicy policy = _contexto.AccessPolicies.Create("Streaming policy",
TimeSpan.FromDays(30),
AccessPermissions.Read);
//4.Crear un Locator
ILocator originLocator = _contexto.Locators.CreateLocator(LocatorType.OnDemandOrigin, assetToStream,
policy,
DateTime.UtcNow.AddMinutes(-5));
Console.WriteLine("Streaming asset base path on origin: ");
Console.WriteLine(originLocator.Path);
Console.WriteLine();
//5. Crear una URL completa para el manifiesto
string urlForClientStreaming = originLocator.Path + manifestFile.Name + "/manifest";
//5.1 En el caso de ser HLS se debe agregar al final de la URL "(format=m3u8-aapl)"
if (contentType == MediaContentType.HLS)
urlForClientStreaming = String.Format("{0}{1}", urlForClientStreaming, "(format=m3u8-aapl)");
Console.WriteLine("URL to manifest for client streaming: ");
Console.WriteLine(urlForClientStreaming);
Console.WriteLine();
Console.WriteLine("Origin locator Id: " + originLocator.Id);
Console.WriteLine("Access policy Id: " + policy.Id);
Console.WriteLine("Streaming asset Id: " + assetToStream.Id);
// 6. regresa el origen
return originLocator;
}
Ahora que vimos cada uno de los métodos principales que necesitamos para hacer la ingestión, codificación y publicación del contenido vamos a ver el cómo se articulan todos estos métodos en una secuencia coherente. El siguiente código lleva a cabo la secuencia completa.
static void Demo1()
{
_accountName = "xxxxxx";
_accountKey = "xxxxxx=";
_contexto = ObtenerContexto();
//Video
string fileName = @"K:\video\interview2.wmv";
Console.WriteLine("\n 1.Subir el Archivo\n");
IAsset VideoOriginal = Subir1ArchivoCrear(AssetCreationOptions.None, fileName);
//Crear el JOB para H264
Console.WriteLine("\n 2.Crear el JOB para H264\n");
IJob myJob = CrearTrabajoCod_H264Smooth(VideoOriginal);
// Paso 4: Borrar el Video Origial
Console.WriteLine("\n 3.Borrar el Video Origial\n");
BorrarAsset(VideoOriginal);
// Paso 4: Crear el JOB para HLS
Console.WriteLine("\n 4.Crear el JOB para HLS\n");
IJob myHLSjob = CreateSmoothToHlsJob(_configFile, myJob.Tasks[0].OutputAssets[0]);
//Paso 5: Publicar H264
Console.WriteLine("\n 5.Publicar H264\n");
GetStreamingOriginLocator(myJob.Tasks[0].OutputAssets[0].Id, MediaContentType.SmoothStreaming);
//Publicar HLS
Console.WriteLine("\n 6.Publicar HLS\n");
GetStreamingOriginLocator(myHLSjob.Tasks[0].OutputAssets[0].Id, MediaContentType.HLS);
//Lista de Videos
ListAssets();
//Salir
Console.WriteLine("\nEspera click para borrar todo.....[s/n]");
string siono=Console.ReadLine();
if (siono == "s")
{
BorrarTodo();
}
Console.WriteLine("\nFin.....");
Console.ReadLine();
}
Al ejecutar el proceso obtenemos las siguientes salidas.
En el paso 1 podemos ver como se sube el archivo original al contenido de Media Services.
En el paso 2, podemos ver el avance del primer JOB(H.264)
En el paso 3, se muestra cuando se borra el archivo original.
En el paso 4, vemos el avance en la codificación HLS
por último, podemos ver la publicación de ambos videos en Media Services.
EL código fuente del ejemplo desarrollado pueden descargarlo desde aquí.
Este es un simple ejemplo de cómo podemos utilizar Windows Azure Media Services de manera programática para poder llevar a cabo un proceso de publicación de videos para dos plataformas distintas de manera automática.
Links relacionados
Durante abril voy a apoyar a Alex en la ejecución de los ITCAMP, actividades de entrenamiento técnico para profesionales de IT con foco en el uso de Windows Azure.
Esta es la invitación al evento.
——————————————————————————————————————————————
![itcamps[1]](https://jpgarcia.cl/wp-content/uploads/2013/04/itcamps1.gif?w=625)
La nube, la alternativa para extender nuestro Datacenter.
Te invitamos a participar de nuestros próximos laboratorios de 5 horas basados en escenarios prácticos aplicables a cualquier tipo de empresa. Esta vez utilizaremos la nube de Microsoft, en su visión de Infraestructura como Servicios para crear máquinas virtuales que complementaran nuestro Datacenter.
En estas sesiones aprenderá como desplegar un SharePoint en la nube de Microsoft, una réplica de su Active Directory y la publicación de una aplicación Web. Todo esto mediante el panel de administración de Windows Azure o mediante PowerShell.
Estos laboratorios se repiten en 4 sesiones, regístrate en la que prefieras escogiendo de las siguientes alternativas:
——————————————————————————————————————————————
inks relacionados
Esta es una presentación introductoria a Windows Azure Media Services que presenté en el WAPU de Bogotá.
El ejemplo de código que automatiza la ingestión, codificación, empaquetamiento y publicación lo voy a publicar la próxima semana, con una explicación de cómo funciona.
Links relacionados
Esta es la presentación de Windows Azure Mobile Services que hice en el Windows Azure Partner university de Colombia.
Otros Links relacionados
Esta es la presentación que expuse en el WAPU de Colombia. Es un replay de la presentación que hicimos en el Open Source Summit de Buenos Aires la semana pasada.
En esta presentación se explican los conceptos de IaaS, vNet y Paas. Lo mejor es que se muestran los escenarios híbridos.
Otros links relacionados
Invitación al evento Windowa Azure University en Colombia, que se realizará los días 20 y 21 de Marzo. Yo presento 4 temas 😉

Agenda

Ya están publicadas las presentaciones, en español, del evento Windows Azure Open Platform Services Summit – Buenos Aires.
Pueden descargarlas desde el Blog de Ricardo Villalobos o directamente desde skydrive.
![soos_1[1]](https://jpgarcia.cl/wp-content/uploads/2013/03/soos_11.jpg?w=625&h=126)

Conozca más sobre cómo Windows Azure ha ayudado a las empresas a realizar un alto valor en la nube. Windows Azure soporta múltiples plataformas de desarrollo, lenguajes y herramientas, ofreciendo a los desarrolladores diversas opciones más allá de .NET. Regístrese ahora para atender a este único evento, en el cual aprenderá como tomar ventaja de una plataforma abierta, flexible y robusta para el desarrollo de sus aplicaciones.
Objetivos del Windows Azure Open Platform Summit
Conducido por expertos en Windows Azure OSS de la corporación, los asistentes aprenderán y entenderán temas como los siguientes:
Agenda

Detalles del Evento
A quien está dirigido el evento?
Dirigido a tomadores de decisiones en su organización –CTOs, Arquitectos, Lideres de equipos de Ingeniería o Desarrollo y/o si está incorporando tecnologías de OSS como parte de la solución de su empresa.
Pre-requisito
Ver el siguiente video introductorio de Windows Azure – http://www.meetwindowsazure.com/Conversations#ScottGuthrieMeet
Trabajando con una empresa que tiene un ERP de nicho, quieren mover su operación a Azure. Dentro de los temas que importantes a tener en cuenta apareció el tema del cumplimiento de normativas que les obligan a tener respaldos y manejarlos siguiendo una política.
En ese sentido, el resapldo en Azure SQL Database es diferente a lo que ofrece SQL Server. Puede verse un detalle de esto en http://msdn.microsoft.com/en-us/library/jj650016.aspx. En Azure SQL Database podemos realizar un respaldo lógico de la siguiente manera.
El Worker Role hace una copia de la base de datos en Azure Sql Database, esta se utilizará para hacer el export. No se hace directamente de la base de datos original porque el export no garantiza consistencia ya que va exportando los datos tabla a tabla y si se hace un cambio mientras el proceso está corriendo podría quedar inconsistente. La Copia si asegura consistencia. Luego de copiar la db sobre este duplicado se hace el export. Una vez terminado el proceso entonces se borra la copia de la db. El proceso se muestra en el siguiente diagrama de secuencia.

En este artículo vamos a desarrollar un ejemplo de un servicio que realiza el export de una base de datos siguiendo el proceso antes descrito.
Las ventajas de desarrollarlo de esta forma es que podemos dejarlo corriendo de manera programada y así asegurarnos de cumplir con la política. Además, que el Export se hace en la nube por lo cual no hay costos de bajar la data a nuestro datacenter u otro lugar.
Los prerrequisitos para poder desarrollar esta solución son:
Los pasos a seguir para construir el proyecto de ejemplo son los siguientes
En visual studio 2012 se crea un proyecto cloud, con un Worker Role. La siguiente figura ilustra el proyecto a crear.

Los CloudDrive son discos duros virtuales que se pueden montar en las máquinas de los Cloud Services. En nuestro caso vamos utilizar un CloudDrive para guardar ahí los archivos export que sacaremos desde la base de datos. CloudDrive es un concepto importante, y pueden leer un buen documento en el siguiente link
Para poder utilizar CloudDrive debemos agregar Local Storage a la definición del servicio, esto lo hacemos en la configuración del Worker Role como se muestra en la siguiente imagen.

El CloudDrive se alancena en un Blob Storage. Para poder conectarnos a ese Blob Storage necesitamos una cade de conexiones, que declaramos en la configuración del servicio como aparece en la siguiente figura. Yo estoy configurando un Storage en la nube, pero se puede utilizar el de desarrollo de igual manera mientas se desarrolla.

Las variables siguientes deben estar definidas para realizar el export:
DefaultEndpointsProtocol=http;AccountName=jpgarciaexportdb;AccountKey=xxxxxxxxxx==
Server=tcp:abc.database.windows.net,1433;Database={0};User ID=SQLDATASERVER@e4rvhdsl4i;Password=P@ssw0rd;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;
La siguiente figura muestra las variables de configuración

Para poder utilizar CloudDriver necesitamos agregar referencias a ODataLib 5.0.2 y Storage Client. Para agregar ODataLib en la versión 5.0.2 podemos utilizar Package Manager Console con la siguiente línea de comandos
PM> Install-Package Microsoft.Data.OData -Version 5.0.2
Ahora, debemos instalar las librearías de Windows Azure Store, utilizando para ellos NuGet Packages como se muestra en la siguiente pantalla.

Para poder acceder a al storage debemos agregar la referencia Microsoft.WindowsAzure.StorageClient.dll.
Por ultimo agregamos la referencia a Microsoft.WindowsAzure.CloudDrive.dll , para poder utilizar CloudDrive.

Para simplificar el desarrollo vamos a crear una clase que nos ayude a manejar todos los aspectos relacionados a CloudDirve. Esa clase se llamará CloudDriveHelp.
Esta clase tiene dos métodos, el primero para iniciar el disco. Para ello se debe definir cuál Storage se almacena en disco virtual. En este método se monta el disco virtual y se obtiene la letra del drive. Esta letra se almacena en la propiedad azureDrivePath de la clase CloudDriveHelp. myCloudDrive también es una propiedad de la clase CloudDriveHelp porque es necesario que su alcance sea general para así poder utilizarla para desmontar el dirve al finalizar el ciclo de vida del Worker Role.
Este método se llama solo una vez al iniciar el Worker Role y lo que hace es montar (literalmente) el disco virtual (VHD) en la máquina virtual que corre nuestro Worker Role.
public void IniciarDisco() { CloudStorageAccount storageAccount; //1. Storage a Utilizar if (RoleEnvironment.IsEmulated) { storageAccount = CloudStorageAccount.DevelopmentStorageAccount; } else { storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("miStoragevhd")); } //2. Referencia al disco de cache local LocalResource localCache = RoleEnvironment.GetLocalResource("midisco"); //3. Inicializa el disco de cache CloudDrive.InitializeCache(localCache.RootPath, localCache.MaximumSizeInMegabytes); //4. Validar que exista Drives CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); blobClient.GetContainerReference("drives").CreateIfNotExist(); //5. Crear el cloudDrive myCloudDrive = storageAccount.CreateCloudDrive(blobClient.GetContainerReference("drives"). GetPageBlobReference("myexportdrive.vhd").Uri.ToString()); try { // 6. Crearlo si no existe, ojo que este drive es persistente myCloudDrive.CreateIfNotExist(localCache.MaximumSizeInMegabytes); } catch (CloudDriveException ex) { error = ex.Message; } //6. Aqui se obtiene la letra del Drive azureDrivePath = myCloudDrive.Mount(25, DriveMountOptions.Force); }
El método IniciarDisco() se llama desde el método OnStrat() del Worker Role, al iniciar el role se inicia el disco.
public override bool OnStart() { ConfigurarTraza(); // Set the maximum number of concurrent connections ServicePointManager.DefaultConnectionLimit = 12; //1. Crea el objeto myDrive myDrive = new CloudDriveHelp(); //2. Inicia el Drive y lo monta myDrive.IniciarDisco(); return base.OnStart(); }
El método FinalizarDisco() de la clase CloudDriveHelp simplemente desmonta el drive de la VM donde se está ejecutando el role.
public override bool OnStart() { ConfigurarTraza(); // Set the maximum number of concurrent connections ServicePointManager.DefaultConnectionLimit = 12; //1. Crea el objeto myDrive myDrive = new CloudDriveHelp(); //2. Inicia el Drive y lo monta myDrive.IniciarDisco(); return base.OnStart(); }
Este método se llama cuando el Worker Role se detiene, es decir desde el método OnStop() como muestra el siguiente código
public override void OnStop() { myDrive.FinalizarDisco(); }
Para validar que nuestro disco este funcionado correctamente vamos a ejecutar el proyecto en modo debug e inspeccionar el disco. Esto lo haremos en el emulador, para simplificar. Una vez ejecutado el proyecto y que el Worker Role ya se inicie vamos al Storage Emulator y utilizando el menú abrimos la carpeta donde se almacenan los Azure Drive.

Ahora, vamos a la carpeta Drives y vemos que se ha creado nuestro disco myexportdrive.vhd como se muestra en la siguiente imagen.

Ahora vamos a utilizar DAC para acceder a Azure Sql Database y hacer un export de una base de datos. Para ello debemos agregar la referencia a Microsoft.SqlServer.Dac.dll. Esta DLL es parte de los prerrequisitos que aparecen al inicio del artículo.
Para simplificar el uso de DAC vamos a utilizar la clase DACHelp. El método principal que implementamos es Exportar() que realiza un export de la base de datos a un archivo. La idea es que ese archivo se almacene en el CloudDrive.
El argumento Mensajes y Progreso son referencia a manejadores de eventos, que reciben información del progreso y mensajes de la tarea de exportación.
public bool Exportar(string sourceDatabaseName, string targetConnectionString, string fullNombreArchivo, EventHandler<DacMessageEventArgs> Mensajes, EventHandler<DacProgressEventArgs> Progreso) { try { DacServices svc; //1. Crear el servicio DAC con la cadena de conexión svc = new DacServices(targetConnectionString); if (Mensajes != null) { //a. asignar los métodos de manejo de eventos de mensajes y Progreso svc.Message += new EventHandler<DacMessageEventArgs>(Mensajes); svc.ProgressChanged += new EventHandler<DacProgressEventArgs>(Progreso); } //2. Ejecutar el Export de la base de datos a un archivo svc.ExportBacpac(fullNombreArchivo, sourceDatabaseName); } catch (DacServicesException ) { throw; } //TODO: dar mejor info del proceso return true; }
Para poder copiar la base de datos vamos a utilizar una instrucción SQL que nos permite crear una base de datos como una copia de una existente. Este comando se debe ejecutar en la base de datos MASTER. El comando SQL a ejecutar es:
CREATE DATABASE {nombreCopia} AS COPY OF {nombreDB}
Una vez ejecutado el comando de copia debemos esperar que la copia esté lista antes de continuar con el proceso de export. Para saber el estado de la copia ejecutamos el comando SQL
select SD.state_desc AS [Current State] from sys.databases SD
LEFT JOIN sys.dm_database_copies DDC ON DDC.database_id = SD.database_id
WHERE SD.name = ‘nombreCopia’
La idea es repetir esta consulta hasta obtener como respuesta «ONLINE» que significa que la copia terminó y podemos utilizarla para exportar el esquema y los datos. El tiempo que se espera, medido e segundos, se almacena en la variable segundos. El valor se lee desde el archivo de configuración app.config, de la llave RespaldarCada[Minutos]. Lo coloqué ahí para poder cambiarlo libremente sin tener que hacer update de la configuración del ROL con propósitos de pruebas. Las llaves que se agregarón al app.config se muestran a continuación.
<appSettings> <add key="switch" value ="1"/> <add key="transferTime" value ="5"/> <add key="snapshotPoolingSegundos" value ="30"/> <add key="RespaldarCada[Minutos]" value ="60"/> </appSettings>
El método Copiardb() es el siguiente.
public string CopiarDb(string sourceDatabaseName, string MasterConnectionString) { string dbCopyName; bool copySw = true; string currentState; string comando; Random random = new Random(); int randomNumber = random.Next(0, 100); try { //1. Nombre de la DB copia dbCopyName = string.Format("{0}_Copy_{1}", sourceDatabaseName, randomNumber.ToString()); using (SqlConnection con = new SqlConnection(MasterConnectionString)) { //2. Comando crear base de datos como copia de otra comando = string.Format("CREATE DATABASE {0} AS COPY OF {1}",dbCopyName, sourceDatabaseName); con.Open(); using (SqlCommand command = new SqlCommand(comando, con)) { //4.Ejecutar el comando de copia command.ExecuteNonQuery(); } Trace.WriteLine("Copiando base de datos:" + dbCopyName, "Snapshot"); do { //3. comando para consultar el estado de la copia, ya que toma tiempo comando = string.Format( "select SD.state_desc AS [Current State] from sys.databases SD LEFT JOIN sys.dm_database_copies DDC " + " ON DDC.database_id = SD.database_id WHERE SD.name = '{0}'", dbCopyName); using (SqlCommand command = new SqlCommand(comando, con)) { //4. ejecutar la consulta del estado de la copia currentState = command.ExecuteScalar().ToString(); Trace.WriteLine(currentState + ": " + dbCopyName, "Snapshot"); } if (currentState == "ONLINE") { //La copia esta lista copySw = false; } if (currentState == "SUSPECT") { //la copia fallo throw new Exception("The 'CREATE DATABASE AS COPY OF' operation failed"); } //Esperar antes de volver a preguntar por el estado de la copia int segundos = int.Parse(System.Configuration.ConfigurationManager.AppSettings.Get("snapshotPoolingSegundos")); System.Threading.Thread.Sleep(segundos*1000); } while (copySw); } } catch (Exception X) { Trace.WriteLine("Error: " + X.Message,"Snapshot"); throw; } return dbCopyName; }
Borrar una base de datos es simple utlizando una sentencia SQL estando conectado a la base de datos Master. Sólo se ejecuta
DROP DATABASE [NombreBaseDatos]
El método implementado es el siguiente
public void BorrarDB(string dbName, string MasterConnectionString) { try { using (SqlConnection con = new SqlConnection(MasterConnectionString)) { string comando = string.Format("DROP DATABASE {0}", dbName); con.Open(); using (SqlCommand command = new SqlCommand(comando, con)) { command.ExecuteNonQuery(); } } } catch (Exception x) { Trace.WriteLine(x.Message,"Borrar DB error:"); } }
Para poder recibir y mostrar el estado de avance, así como los mensajes del proceso de Export vamos a crear dos métodos que escriben esos detalles en la traza. Los métodos son los siguientes y se crean en el Worker Role.
public void receiveDacServiceMessageEvent(object sender, DacMessageEventArgs e) { Trace.WriteLine(string.Format("Message Type:{0} Prefix:{1} Number:{2} Message:{3}", e.Message.MessageType, e.Message.Prefix, e.Message.Number, e.Message.Message)); } public void receiveDacServiceProgessEvent(object sender, DacProgressEventArgs e) { Trace.WriteLine(string.Format("Progress Event:{0} Progrss Status:{1}", e.Message, e.Status)); }
Cuando trabajamos en el emulador, toda la traza aparece en la consola del emulador. Ahora, cuando estamos en Azure y queremos ver la traza debemos configurar dónde vamos a almacenar la información de la traza. En este caso utilizaremos una tabla en el store. Para ello debemos agregar lo siguiente en el app.config
<system.diagnostics> <trace> <listeners> <add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="AzureDiagnostics"> <filter type="" /> </add> </listeners> </trace> </system.diagnostics>
Luego en el método OnStrat() llamar al método ConfigurarTraza() que se hace cargo de configurar la conexión a la tabla y el tiempo de transferencia de los datos. El método es el siguiente.
private void ConfigurarTraza() { string wadConnectionString = "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"; CloudStorageAccount storageAccount = CloudStorageAccount.Parse( RoleEnvironment.GetConfigurationSettingValue(wadConnectionString)); RoleInstanceDiagnosticManager roleInstanceDiagnosticManager = storageAccount.CreateRoleInstanceDiagnosticManager( RoleEnvironment.DeploymentId, RoleEnvironment.CurrentRoleInstance.Role.Name, RoleEnvironment.CurrentRoleInstance.Id); DiagnosticMonitorConfiguration config = roleInstanceDiagnosticManager.GetCurrentConfiguration(); if (config == null) { config = DiagnosticMonitor.GetDefaultInitialConfiguration(); } //Configura cada cuanto tiempo se envía la información a la tabla double transferTime = double.Parse(System.Configuration.ConfigurationManager.AppSettings.Get("transferTime")); config.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(transferTime); config.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose; roleInstanceDiagnosticManager.SetCurrentConfiguration(config); }
La configuración que utiliza para conectarse a la tabla se define en la configuración del Web Role.

El método Run() del Worker Role implementa el proceso de copia, exportación y borrado de la base de datos. Esto se realiza casa cierto tiempo definido en minutos en la variable de configuración «RespaldarCada[Minutos]«. El proceso es controlado por la variable de configuración «switch» del app.config. La idea es que si esa llave tiene el valor 0 no se realiza el proceso. Esto está pensando en poder detener los export sin necesidad de bajar el Worker Role.
Al iniciar el proceso se captura la hora de inicio para poder calcular el tiempo que toma el proceso. Luego se obtiene desde la configuración del role el nombre de la base de datos, la cadena de conexión a la db a exportar y a la Master. Con estos datos comienza el proceso con el primer paso que es copiar la base de datos. Para ello se invoca el método privado CopiarDb() que retorna el nombre de la copia.
private string CopiarDB(string NombreBaseDatos, string CadenaConexionDbMaster) { string respuesta = null; //1. Crer el Helper DACHelp myDAC = new DACHelp(); try { DateTime tiempoInicio = System.DateTime.Now; respuesta = myDAC.CopiarDb(NombreBaseDatos, CadenaConexionDbMaster); Trace.WriteLine("Tiempo Copia: " + DateTime.Now.Subtract(tiempoInicio).ToString(), "Copia DB"); } catch (Exception x) { Trace.WriteLine("************************************************************"); Trace.WriteLine(x.Message, "Error Copia DB:"); Trace.WriteLine("************************************************************"); } return respuesta; }
Si la copia es exitosa, esto lo sabe porque CopiarDB() entrega el nombre de la base de datos copiada, entonces se llama el método privado exportar() que también utiliza DACHelp. Este es el encargado de formar el nombre del archivo donde se guarda el export, agregándole al nombre la fecha y hora en que se hizo. Un punto importante de notar en el nombre del archivo es como se obtiene la ruta del mismo utilizando myDrive.azureDrivePath. Cuando se monta un disco se le asigna una letra, pero no se asegura que siempre sea la misma por lo cual es importante recuperar la letra asignada para confeccionar el nombre completo del archivo.
También, al invocar el Exportar indica cuales son los manejadores de eventos que reciben la información del proceso y sus mensajes.
private void exportar(string NombreBaseDatos, string targetConnectionString, string idRespaldo) { //1. Crer el Helper DACHelp myDAC = new DACHelp(); //2. Arma el nombre del archivo para el BACPAC string fullNombreArchivo = string.Format("{0}{1}_{2}.BACPAC ", myDrive.azureDrivePath, NombreBaseDatos, idRespaldo); try { //3. Ejecuta el Exportar del Helper myDAC.Exportar(NombreBaseDatos, targetConnectionString, fullNombreArchivo, this.receiveDacServiceMessageEvent, this.receiveDacServiceProgessEvent); } catch (Exception x) { Trace.WriteLine("************************************************************"); Trace.WriteLine(x.Message,"Error Copiar DB:"); Trace.WriteLine("************************************************************"); } }
Una vez terminado el export, se manda a la traza el tiempo que se demoró el proceso y se listan los respaldos que se encuentran en el cloudDrive.
Por último, se llama al método privado BorrarTemp() para que borre la copia de la DB. Este también es un método simple porque utiliza DACHelp.
private void BorrarDBTemp(string nombreDbTmp, string CadenaConexionDbMaster) { //1. Crer el Helper DACHelp myDAC = new DACHelp(); //2. Arma el nombre del archivo para el BACPAC try { //3. Ejecuta el Exportar del Helper myDAC.BorrarDB(nombreDbTmp, CadenaConexionDbMaster); } catch (Exception x) { Trace.WriteLine("************************************************************"); Trace.WriteLine(x.Message, "Error Borrar DB TMP:"); Trace.WriteLine("************************************************************"); } }
Por último el método Run() del Worker Role espera el tiempo definido en minutos en la variable de configuración RespaldarCada[Minutos] para iniciar nuevamente el proceso completo.
Ahora estamos listos para probar el proyecto apuntando a una DB en Azure y utilizando el Storage del emulador. Si se ejecuta el proyecto, en la consola del emulador se puede ver el progreso.
Como primer paso aparece el mensaje de copiando la base de datos AdventureWorks2012 a AdventureWorks2012_Copy_94. 94 es un número que se genera aleatoriamente para evitar que se repitan los nombres de la bases de datos de copia. EL mensaje en la consola es:
Snapshot: COPYING: AdventureWorks2012_Copy_94
Cuando termina la copia de la base de datos aparece el mensaje:
Copia DB: Tiempo Copia: 00:05:37.8043059
Ahora comienza el proceso de exportación. En la consola se pueden ver información del proceso y los mensajes de DAC. Por ejemplo el evento cuando completa el export de la tabla : [Sales].[CurrencyRate] es el siguiente:
Progress Event:Processing table: [Sales].[CurrencyRate] Progrss Status:Completed
Por otra parte, un ejemplo de un mensaje donde muestra la cantidad de registros exportados de la tabla [Sales].[PersonCreditCard] sería:
Message Type:Message Prefix:SQL Number:73152 Message:Table [Sales].[PersonCreditCard] has exported 19118 rows.
Yo estoy utilizando la base de datos AdventureWorks2012 que descargue desde el siguiente link.
http://msftdbprodsamples.codeplex.com/releases/view/55330
Las propiedades de la base de datos que me muestra el portal de administración se muestran en la Siguiente imagen.

El tamaño de la base de datos es de 141.51 MB y en mi prueba tomó 5 minutos con 37 segundo las copia de la base de datos. La exportación del esquema y los datos tomó 9 minutos con treinta y dos segundos. Cuando el proceso termina nos entrega la siguiente información:
Progress Event:Exporting data from database Progrss Status:Completed
Information: Fin Respaldo
Information: Tiempo Respaldo: 00:09:32.6295793
info respaldo:: The directory a:\ contains the following files:
info respaldo:: The size of AdventureWorks2012_2013-01-10_07-15-02-PM.BACPAC is 17681362 bytes.
Information: proximo respaldo:3600000
Ahora, revisando el disco virtual en el Storage Emulator podemos ver que el archivo pesa 17 MB.

Una vez probado en el ambiente local podemos preparar el proyecto para el Deploy en Azure. Cuando se ejecuta en Azure el Worker Role, es necesario instalar los componentes de Data-Tier Application Framework de la versión 2008 R2 y de la versión 2012. Estos podemos encontrarlos respectivamente en los siguientes links:
http://www.microsoft.com/en-us/download/details.aspx?id=35756
http://www.microsoft.com/en-us/download/details.aspx?id=24000
Los DAC en la versión 2012 requiere instalar los MSI de x86 y x64 para correr en los sistemas operativos de 64 bits.
Para instalar estos componentes vamos a agregar los archivos MSI al proyecto para poder instalarlos antes de ejecutar el ROL. La siguiente imagen muestra los archivos ya incorporados al proyecto.

Una vez agregados, vamos a sus propiedades y configuramos Copy to Output Directory con el valor Copy always. Esto es necesario para que cuando se hace deploy en la VM del Worker Role, se copien los instaladores.
Ahora agregamos un archivo llamado intallDAC.cmd al proyecto. Este archivo para asegurarnos que se ejecuta demos grabarlo con la codificación Western European (Windows) codepage 1252. Esto se hace en la opción Adavanced Save Options. En este cmd también vamos a sus propiedades y configuramos Copy to Output Directory con el valor Copy always.
El archivo CMD lo primero que hace es discriminar si se está corriendo en el emulador o no. Si se corre en el emulador no instala los MSI. Esto porque en mi maquina ya tengo todo instalado y no es necesario.
REM Check if this task is running on the compute emulator. IF "%ComputeEmulatorRunning%" == "true" ( REM This task is running on the compute emulator. Perform tasks that must be run only in the compute emulator. ) ELSE ( ECHO This task is running on the cloud. Perform tasks that must be run only in the cloud. IF EXIST "MSI\DAC2012x64\log_DACFramework.txt" ( ECHO Ya se instalaron los MSI. ) ELSE ( ECHO \DAC2012x64 MSI\DAC2012x64\DACFramework.msi /qn /l* MSI\DAC2012x64\log_DACFramework.txt MSI\DAC2012x64\SQLSysClrTypes.msi /qn /l* MSI\DAC2012x64\log_SQLSysClrTypes.txt MSI\DAC2012x64\SqlDom.msi /qn /l* MSI\DAC2012x64\log_SqlDom.txt ECHO \DAC2012x86 MSI\DAC2012x86\DACFramework.msi /qn /l* MSI\DAC2012x86\log_DACFramework.txt MSI\DAC2012x86\SQLSysClrTypes.msi /qn /l* MSI\DAC2012x86\log_SQLSysClrTypes.txt MSI\DAC2012x86\SqlDom.msi /qn /l* MSI\DAC2012x86\log_SqlDom.txt ECHO \DAC2008R2 MSI\DAC2008R2\DACFramework.msi /qn /l* MSI\DAC2008R2\log_DACFramework.txt MSI\DAC2008R2\SQLSysClrTypes.msi /qn /l* MSI\DAC2008R2\log_SQLSysClrTypes.txt MSI\DAC2008R2\TSqlLanguageService.msi /qn /l* MSI\DAC2008R2\log_TSqlLanguageService.txt MSI\DAC2008R2\SharedManagementObjects.msi /qn /forcerestart /l* MSI\DAC2008R2\log_SharedManagementObjects.txt ) )
Ahora, si está corriendo en Azure valida que los componentes no hayan instalado antes. Para eso lo que hace es ver si existe el archivo log_DACFramework.txt que se crea al instalar utilizando este CMD. Esta validación es necesaria porque al instalar el último componente se reinicia la vm del Worker Role. Sin esta validación, la VM entraría en el loop infinito de reinicios. El archivo CMD es el siguiente.Por último, en el archive ServiceDefinition.csdef debemos agregar la tarea de inicio para que se ejecuten las instalaciones de los archivos MSI y la variable de control ComputeEmulatorRunning para poder saber si estamos en el emulador o en Azure. Esto se hace agregando el siguiente TAG al archivo.
<Startup> <Task commandLine="installDAC.cmd" executionContext="elevated" taskType="background"> <Environment> <Variable name="ComputeEmulatorRunning"> <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" /> </Variable> </Environment> </Task> </Startup>
Más información de cómo definir tareas de inicio para un role pueden verlo en How to Define Startup Tasks for a Role
Ahora hacemos deploy a Windows azure. Para poder revisar el archivo en el CloudDrive, activamos en el wizard de publicación la opción de Remote Desktop como muestra la siguiente imagen.

Una vez hecho el deploy, podemos revisar que tengamos nuestro Azure CloudDrive.

Se puede revisar en el disco de la aplicación, que se encuentran los archivos de LOG de instalación, como se de en la siguiente imagen.

Como estamos en Azure y no en el emulador, para poder ver el avance nos conectamos a la tabla de Traza para poder ver el avance. La cadena de conexión es la que se configuró en las propiedades del role. Podemos ver la traza de inicio del respaldo y que se comenzó a hacer la copia de la base de datos con el nombre AdventureWorks2012_Copy_43.

Cuando termina la copia de la base de datos, aparece el registro que nos indica que la copia tomo 16 minutos.
Copia DB: Tiempo Copia: 00:16:05.5937425
Por último podemos ver el término del proceso, el cual tomó 19 minutos y generó un archivo de 17MB.

Como validación final, vamos al disco F que en este caso es nuestro CloudDrive y vemos el archivo BACPAC creado de 17 MB, es decir ya tenemos el respaldo de nuestra base de datos.

Este ejemplo implementa el proceso de copia, exportación y borrado de la copia de una base de datos de Azure en un CloudDrive. El uso de CLoudDrive es opcional, ya que se podría haber implementado el guardar los archivos directamente en el Blob Storage sin usar un disco virtual.
El código es de ejemplo y no debe ser utilizado en producción, puede ser descargado desde aquí.
Otros artículos relacionados con Azure: