[Windows Azure] Service Management API 初體驗

Service Management API (服務管理API) 是 Windows Azure 管理工具之一,不過它不是給 IT 管理人員,而是給程式開發人員用的,因為它開放的是 API 而不是使用者介面,開發人員可以撰寫一些簡單的工具來輔助監控與管理 Windows Azure 平台上部署的各式服務,並且可以透過 API 進行服務的部署與管理等,而微軟在 Visual Studio 2010 中加入的雲端專案發行功能,也是利用 Service Management API 來開發的,因此只要練會了 Service Management API 的使用,那麼想要自行開發 Windows Azure 管理工具也不再是夢想。

Service Management API (服務管理API) 是 Windows Azure 管理工具之一,不過它不是給 IT 管理人員,而是給程式開發人員用的,因為它開放的是 API 而不是使用者介面,開發人員可以撰寫一些簡單的工具來輔助監控與管理 Windows Azure 平台上部署的各式服務,並且可以透過 API 進行服務的部署與管理等,而微軟在 Visual Studio 2010 中加入的雲端專案發行功能,也是利用 Service Management API 來開發的,因此只要練會了 Service Management API 的使用,那麼想要自行開發 Windows Azure 管理工具也不再是夢想。

Service Management API 全部都是 REST-style 的 Web Service API,它們都相容於 HTTP 協定,且不同的指令必須使用 HTTP 動詞才可以存取,同時,為了要確認發出 API 呼叫的應用程式都是來自訂閱的擁有者,Service Management API 要求每一次的 HTTP call 都要帶有用戶端憑證 (certificate),且憑證必須事先上傳到 Management Portal 中 (雖然 Service Management API 有管理憑證的 API,但第一個憑證必須要由管理人員手動使用 Management Portal 上傳)。

因為 Service Management API 是標準的 HTTP 服務,所以我們也不需使用 Web Service 那一套去存取它,只要使用 WebClient 或 HttpWebRequest 去操作即可。

private static HttpWebRequest CreateHttpRequest(string Method, string Url, HttpHeader Headers, X509Certificate2 ManagementCert, TimeSpan Timeout)
{
    HttpWebRequest request = WebRequest.Create(Url) as HttpWebRequest;

    request.Method = Method;
    request.KeepAlive = false;
    request.ProtocolVersion = HttpVersion.Version11;
    request.ClientCertificates.Add(ManagementCert);

    if (Headers != null && Headers.Count > 0)
    {
        foreach (KeyValuePair<string, string> header in Headers)
            request.Headers.Add(header.Key, header.Value);
    }

    request.Timeout = (int)Timeout.TotalMilliseconds;
    request.ReadWriteTimeout = (int)Timeout.TotalMilliseconds;

    return request;
}

public static byte[] PerformHttpGetRequest(string Url, HttpHeader Headers, X509Certificate2 ManagementCert, TimeSpan Timeout, out string RequestID)
{
    HttpWebRequest request = NetworkService.CreateHttpRequest("GET", Url, Headers, ManagementCert, Timeout);
    HttpWebResponse response = null;
    byte[] result = null;

    try
    {
        response = request.GetResponse() as HttpWebResponse;

        BinaryReader br = new BinaryReader(response.GetResponseStream());
        MemoryStream ms = new MemoryStream();
        int readcount = 0;
        int bufferSize = 32768;
        byte[] data = null;

        do
        {
            data = new byte[bufferSize];
            readcount = br.Read(data, 0, data.Length);
            ms.Write(data, 0, readcount);
        }
        while (readcount > 0);

        ms.Flush();
        ms.Position = 0;

        RequestID = response.Headers["x-ms-request-id"];

        result = ms.ToArray();
    }
    catch (WebException we)
    {
        throw HandleHttpException(we);
    }
    finally
    {
        if (response != null)
            response.Close();
    }

    return result;
}

雖然呼叫 Service Management API 並不會太難,但有兩個觀念必須要有:

  • API 是有版本控制的,在 Windows Azure SDK 的 "Service Management Versioning" 一文中,有說明每次 HTTP 呼叫都要在 HTTP 要求中加上 x-ms-version 的標頭,且要設定日期 (yyyy-MM-dd 格式),目前有 2009-10-01, 2010-04-01 與 2010-10-28 三種版本,如果沒有特別需要相容性的話,用新的就對了。
  • 管理憑證必須要先上傳,然後再於 HTTP 要求中附加用戶端憑證,這裡要注意的是,憑證必須要內含私密金鑰 (Private Key),這點在 Windows Azure SDK 的文件中並沒有特別說明,如果要把憑證匯出時,必須要帶私密金鑰 (.pfx),而不能只有公鑰 (.cer),否則在 HTTP 要求送出時,伺服器會回傳 HTTP 403 (Forbidden),且沒有任何的錯誤資訊。

 

每一個服務都有自己的 URL 以及 HTTP Method,不能用錯,如果用錯的話會得到錯誤訊息,而每一個回傳或必須上傳的資料都是 XML,並且具有 http://schema.microsoft.com/windowsazure 命名空間,所以在讀寫 XML 的時候,一定要加入這個命名空間,XML Parser 才會讀的到:

public void GetProperties()
{
    string requestID = null;
    EasyAzure.Core.HttpHeader httpHeader = EasyAzure.Core.ServiceVersions.Ver20101028;

    byte[] data = (new EasyAzure.Core.Network("Thumbprint String")).PerformHttpGetRequest(this.ServiceUrl, httpHeader, out requestID);

    XmlDocument doc = new XmlDocument();
    doc.LoadXml(Encoding.UTF8.GetString(data));

   XmlNamespaceManager xnm = new XmlNamespaceManager(doc.NameTable);
    xnm.AddNamespace("s", "
http://schemas.microsoft.com/windowsazure");

    List<Host> hosts = new List<Host>();

    XmlNode node = doc.DocumentElement.SelectSingleNode("//s:HostedService/s:HostedServiceProperties", xnm);

    if (node != null)
    {
        if (node.SelectSingleNode("s:Location", xnm) != null)
            this.Location = node.SelectSingleNode("s:Location", xnm).InnerText;
        if (node.SelectSingleNode("s:Description", xnm) != null)
            this.Description = node.SelectSingleNode("s:Description", xnm).InnerText;
        if (node.SelectSingleNode("s:AffinityGroup", xnm) != null)
            this.AffinityGroup = node.SelectSingleNode("s:AffinityGroup", xnm).InnerText;
        if (node.SelectSingleNode("s:Label", xnm) != null)
            this.Label = Encoding.UTF8.GetString(Convert.FromBase64String(node.SelectSingleNode("s:Label", xnm).InnerText));
    }

    doc = null;
}

public Host[] EnumServiceHosts()
{
    string requestUrl = "
https://management.core.windows.net/{subscription id}/services/hostedservices";
    string requestID = null;
    EasyAzure.Core.HttpHeader httpHeader = EasyAzure.Core.ServiceVersions.Ver20101028;

    byte[] data = (new EasyAzure.Core.Network("Thumbprint string")).PerformHttpGetRequest(requestUrl, httpHeader, out requestID);

    XmlDocument doc = new XmlDocument();
    doc.LoadXml(Encoding.UTF8.GetString(data));

    XmlNamespaceManager xnm = new XmlNamespaceManager(doc.NameTable);
    xnm.AddNamespace("s", "
http://schemas.microsoft.com/windowsazure");

    List<Host> hosts = new List<Host>();

    XmlNodeList nodes = doc.DocumentElement.SelectNodes("//s:HostedServices/s:HostedService", xnm);

    if (nodes != null)
    {
        foreach (XmlNode node in nodes)
        {
            hosts.Add(new Host(
                node.SelectSingleNode("s:Url", xnm).InnerText,
                node.SelectSingleNode("s:ServiceName", xnm).InnerText));
        }
    }

    doc = null;
    return hosts.ToArray();
}

 

Reference:

Windows Azure SDK Documentation: Service Management API References