2011年5月18日水曜日

SQL Azure Management REST API をアクティビティ化

SQL Azure には管理用の REST API が多数提供されています。
今回はサーバー管理用の REST API をアクティビティ化してみました。REST API 利用ですので、SDK 等の導入は一切必要ありません。
殆ど似た形になるので今回はベースクラスを作成し、各アクティビティはそれを継承する形にしています。まずはベースとなるアクティビティクラスから。

   1: Imports System.Activities
   2: Imports System.IO
   3: Imports System.Net
   4: Imports System.Security.Cryptography.X509Certificates
   5:  
   6: Public MustInherit Class AzureActivityBase
   7:     Inherits AsyncCodeActivity
   8:  
   9:     Protected Const SQL_API_PATH = "https://management.database.windows.net:8443/{0}/servers"
  10:  
  11:     Public Property SubscriptionID As String
  12:     Public Property CertThumbPrint As String
  13:  
  14:     Public Property Account As String
  15:     Public Property Password As String
  16:     Public Property Location As String
  17:  
  18:     Public Property ResultDocument As OutArgument(Of String)
  19:     Public Property ResultStatus As OutArgument(Of String)
  20:     Public Property RequestID As OutArgument(Of String)
  21:  
  22:     Private Delegate Function AsyncGetWebDataDelegate(ByVal subscription As String,
  23:                                                                                ByVal thumbprint As String,
  24:                                                                                ByVal admAccount As String,
  25:                                                                                ByVal admPassword As String,
  26:                                                                                ByVal locations As String) As String()
  27:  
  28:     Protected Overrides Function BeginExecute(context As System.Activities.AsyncCodeActivityContext, callback As System.AsyncCallback, state As Object) As System.IAsyncResult
  29:         Dim asyncExecute = New AsyncGetWebDataDelegate(AddressOf GetWebData)
  30:         context.UserState = asyncExecute
  31:  
  32:         Return asyncExecute.BeginInvoke(Me.SubscriptionID, Me.CertThumbPrint,
  33:                                                        Me.Account, Me.Password, Me.Location, callback, state)
  34:     End Function
  35:  
  36:     Protected Overrides Sub EndExecute(context As System.Activities.AsyncCodeActivityContext, result As System.IAsyncResult)
  37:         Dim asyncExecute = TryCast(context.UserState, AsyncGetWebDataDelegate)
  38:         Dim getResult = asyncExecute.EndInvoke(result)
  39:  
  40:         context.SetValue(Me.ResultDocument, getResult(0))
  41:         context.SetValue(Me.ResultStatus, getResult(1))
  42:         context.SetValue(Me.RequestID, getResult(2))
  43:     End Sub
  44:  
  45:     Protected Overridable Function GetWebData(ByVal subscription As String,
  46:                                                                   ByVal thumbprint As String,
  47:                                                                   ByVal admin As String,
  48:                                                                   ByVal pass As String,
  49:                                                                   ByVal loc As String) As String()
  50:         Dim resultStrings As String = ""
  51:         Dim resultStatus As String = ""
  52:         Dim resultRequestID As String = ""
  53:         Try
  54:             Dim certificate As X509Certificate2 = CreateCertification(thumbprint)
  55:  
  56:             Dim requestUri As Uri = Me.CreateApiPath(subscription)
  57:             Dim localWebRequest = TryCast(HttpWebRequest.Create(requestUri), HttpWebRequest)
  58:             localWebRequest.ClientCertificates.Add(certificate)
  59:             Me.SetHttpMethod(localWebRequest)
  60:             Me.AddRequestHeader(localWebRequest)
  61:  
  62:             Dim bodyString = Me.CreateBodyMessage(admin, pass, loc)
  63:             If bodyString IsNot Nothing Then
  64:                 Dim sendBytes() As Byte = Text.Encoding.UTF8.GetBytes(bodyString)
  65:                 localWebRequest.ContentLength = sendBytes.Length
  66:                 localWebRequest.ContentType = "application/xml; charset=utf-8"
  67:                 Using reqStream = localWebRequest.GetRequestStream
  68:                     reqStream.Write(sendBytes, 0, sendBytes.Length)
  69:                 End Using
  70:             End If
  71:  
  72:             Using webResponse = TryCast(localWebRequest.GetResponse(), HttpWebResponse)
  73:                 resultStatus = webResponse.StatusCode.ToString
  74:                 resultRequestID = webResponse.Headers("x-ms-request-id")
  75:                 Using responseStream = webResponse.GetResponseStream()
  76:                     Using reader As New StreamReader(responseStream)
  77:                         resultStrings = reader.ReadToEnd
  78:                     End Using
  79:                 End Using
  80:             End Using
  81:  
  82:         Catch ex As Exception
  83:             resultStrings = ex.Message
  84:         End Try
  85:         Return New String() {resultStrings, resultStatus, resultRequestID}
  86:     End Function
  87:  
  88:     Protected Overridable Function CreateApiPath(ByVal subscript As String) As Uri
  89:         Return New Uri(String.Format(SQL_API_PATH, subscript))
  90:     End Function
  91:  
  92:     Protected Overridable Function CreateCertification(ByVal thumbPrintString As String) As X509Certificate2
  93:         Dim certStore As X509Store = New X509Store(StoreName.My, StoreLocation.CurrentUser)
  94:         certStore.Open(OpenFlags.ReadOnly)
  95:  
  96:         Dim certCollection As X509Certificate2Collection = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbPrintString, False)
  97:         certStore.Close()
  98:  
  99:         If certCollection.Count <= 0 Then
 100:             Throw New ApplicationException("証明書がありません")
 101:         End If
 102:         Return certCollection(0)
 103:     End Function
 104:  
 105:     Protected Overridable Sub AddRequestHeader(ByVal wq As WebRequest)
 106:         wq.Headers.Add("x-ms-version", "1.0")
 107:     End Sub
 108:  
 109:     Protected Overridable Sub SetHttpMethod(ByVal wq As WebRequest)
 110:         wq.Method = "POST"
 111:     End Sub
 112:  
 113:     Protected Overridable Function CreateBodyMessage(ByVal adminAccount As String, ByVal adminPassword As String, ByVal locationStrings As String) As String
 114:         Dim xmlBody = "<?xml version=""1.0"" encoding=""utf-8""?>" + ControlChars.NewLine +
 115:                   "<Server xmlns=""http://schemas.microsoft.com/sqlazure/2010/12/"">" + ControlChars.NewLine +
 116:                   "  <AdministratorLogin>" + adminAccount + "</AdministratorLogin>" + ControlChars.NewLine +
 117:                   "  <AdministratorLoginPassword>" + adminPassword + "</AdministratorLoginPassword>" + ControlChars.NewLine +
 118:                   "  <Location>" + locationStrings + "</Location>" + ControlChars.NewLine +
 119:                   "</Server>"
 120:         Return xmlBody
 121:     End Function
 122:  
 123: End Class

かなり力技なロジックですw
そしてこれを継承して作成した SQL Azure サーバー新規作成アクティビティはこうなります。


   1: Imports System.Activities
   2:  
   3: Public Class CreateSQLAzureServerActivity
   4:     Inherits AzureActivityBase
   5:  
   6:     Public Sub New()
   7:         Me.DisplayName = "SQL Azure サーバーを作成する"
   8:     End Sub
   9:  
  10: End Class

まぁ、ベースロジックはこの新規作成でしたので・・・
続いては SQL Azure サーバーの列挙アクティビティです。


   1: Imports System.Activities
   2: Imports System.Net
   3:  
   4: Public Class EnumurateSQLAzureServerActivity
   5:     Inherits AzureActivityBase
   6:  
   7:     Public Property ServerName As String
   8:  
   9:     Public Sub New()
  10:         Me.DisplayName = "SQL Azure サーバー一覧を取得する"
  11:     End Sub
  12:  
  13:     Protected Overrides Sub SetHttpMethod(ByVal wq As WebRequest)
  14:         wq.Method = "GET"
  15:     End Sub
  16:  
  17:     Protected Overrides Function CreateBodyMessage(adminAccount As String, adminPassword As String, locationStrings As String) As String
  18:         Return Nothing
  19:     End Function
  20: End Class

リクエストのメソッドを GET にし、本体のメッセージは何も送信しないようにしています。
次は SQL Azure サーバーの削除アクティビティ。


   1: Imports System.Activities
   2: Imports System.Net
   3:  
   4: Public Class DropSQLAzureServerActivity
   5:     Inherits AzureActivityBase
   6:  
   7:     Public Property ServerName As String
   8:  
   9:     Public Sub New()
  10:         Me.DisplayName = "SQL Azure サーバーを削除する"
  11:     End Sub
  12:  
  13:     Protected Overrides Sub SetHttpMethod(ByVal wq As WebRequest)
  14:         wq.Method = "DELETE"
  15:     End Sub
  16:  
  17:     Protected Overrides Function CreateBodyMessage(adminAccount As String, adminPassword As String, locationStrings As String) As String
  18:         Return Nothing
  19:     End Function
  20:  
  21:     Protected Overrides Function CreateApiPath(subscript As String) As System.Uri
  22:         Return New Uri(String.Format(SQL_API_PATH, subscript) + "/" + Me.ServerName)
  23:     End Function
  24:  
  25: End Class

SQL Azure サーバーの削除は API のパスに対象となるサーバー名が必要になりますので、アドレスを作成している部分をオーバーライドしています。そして最後にパスワード変更アクティビティ。



   1: Imports System.Activities
   2:  
   3: Public Class ResetSQLAzurePasswordActivity
   4:     Inherits AzureActivityBase
   5:  
   6:     Public Property ServerName As String
   7:  
   8:     Public Sub New()
   9:         Me.DisplayName = "SQL Azure パスワードを変更する"
  10:     End Sub
  11:  
  12:     Protected Overrides Function CreateBodyMessage(adminAccount As String, adminPassword As String, locationStrings As String) As String
  13:         Dim xmlBody = "<?xml version=""1.0"" encoding=""utf-8""?>" + ControlChars.NewLine +
  14:                   "<AdministratorLoginPassword xmlns=""http://schemas.microsoft.com/sqlazure/2010/12/"">" + ControlChars.NewLine +
  15:                   adminPassword + ControlChars.NewLine +
  16:                   "</AdministratorLoginPassword>"
  17:         Return xmlBody
  18:     End Function
  19:  
  20:     Protected Overrides Function CreateApiPath(subscript As String) As System.Uri
  21:         Return New Uri(String.Format(SQL_API_PATH, subscript) + "/" + Me.ServerName + "?op=ResetPassword")
  22:     End Function
  23:  
  24: End Class

こちらも先程の削除と似ています。送信する本文として、変更後のパスワードを設定した形で POST メソッドにて送信します。

0 件のコメント:

コメントを投稿