面向 VMM 服务模板的 Windows Azure 包、Service Provider Foundation 和 IaaS API 支持

原文地址: https://blogs.technet.com/b/orchestrator/archive/2014/07/03/windows-azure-pack-service-provider-foundation-and-iaas-api-support-for-vmm-service-templates.aspx

John Ballard [MSFT]

 

简介

特别感谢SrimathiSanthanam、Thomas Yip和FilippoSeracini(博士)帮助收集和测试本文的示例。

继 2013 年秋季System Center 2012 R2 和 Windows Azure 包发布之后,我们收到的用户咨询数量越来越多,人们表示希望获取有关IaaS APIVMM 服务模板支持观点的帮助。有一点要澄清的是,Service Provider Foundation从一开始就纳入了VMM服务模板管理和使用支持。

造成人们提问的最大原因在于人们十分强调Azure一致性,这已经渗透到追求Cloud OS愿景的方方面面。为帮助人们真正认识Cloud OS的长期优势(一致体验、一个工具集、一个 API),不久前我们开始鼓励客户及合作伙伴重点将Windows Azure 包的服务管理 API作为基于IaaS功能(属于由Windows Server & System Center提供支持的Cloud OS的一部分)编程的切入点。从这个角度而言,Service Provider Foundation仅仅是Windows Azure 包的服务管理 API 与 System Center之间的桥梁,而不是主要IaaS API本身。

但是,同样由于强调Azure一致性,Windows Azure包的服务管理 API 不支持VMM服务模板。同时也不支持Azure一致的VM 角色项目(一个类似的概念),这个新项目目前缺乏VMM服务模板的一些功能(例如,一次定义多个层)。

所以,在等待VM角色填补这些空白的同时,人们该做些什么?答案是采用混合方法:继续重点运用Windows Azure包的服务管理 API 完成IaaS编程工作,同时如果需要对这些功能进行编程访问,请使用Service Provider Foundation作为 VMM 服务模板。正确完成操作后,就能保留Windows Azure包计划/订阅上下文,并在必要时使用VMM服务模板。

若要提供服务模板支持,首先必须为租户订阅添加一个服务模板,然后基于该服务执行所需的操作(创建、删除、扩展等)。

以下信息描述了使用方法并提供了一般示例(您需要编辑这些示例,才能保证它们在您部署的环境中正常运行)。

通过 Service Management Automation (SMA) Runbook为租户订阅添加服务模板

管理员通过SMA Runbook(由SPF在租户创建订阅时触发)为租户(通过WAP 租户 API订阅)提供服务模板访问权限:

  1. 在WAP门户中打开管理控制台,访问Automation部分(左侧导航窗格),创建一个新的Runbook。

  2. 将您在下面看到的脚本粘贴到这个新的Runbook中。

  3. 使用环境特定值替换变量。

  4. 发布Runbook,确保将标记值“SPF”输入Runbook配置(只有这三个字母,无引号,这一点很关键,否则将无法在下一步看到Runbook),单击 Save。

  5. 访问VM Clouds部分的Automation选项卡,创建一个新的事件到Runbook映射:对象为“subscription”、操作为create、Runbook是您刚刚创建的Runbook,与命名无关。

     workflow AddServiceTemplateToSubscription
     {
         param
         (
          [Parameter(Mandatory=$true)]
             [string] $name,
    
             [Parameter(Mandatory=$true)]
             [string] $operation,
    
             [Parameter(Mandatory=$true)]
             [object] $resourceObject
         )
    
         #only perform action when it is subscription create
    
         if (($name -ne "Subscription") -or ($operation -ne "Create"))
         {
             throw "Mismatch operation. Finishing runbook"
         }
      
             $subscriptionName = <your subscriptionName>
      
         if ($resourceObject.SubscriptionName -eq $subscriptionName)
         {
             $UserRoleId = $resourceObject.SubscriptionId
    
             #define variable for service template we are going to use
             $serviceTemplateName = <your ServiceTemplateName>
             $serviceTemplateRelease = <your ServiceTemplateRelease>
             $SystemCred = <Your Credentials>
             $VmmServer1 = <Your VMM Server>
    
             if ($VmmServer1 -eq $null -or $VmmServer1 -eq "") {    
                throw "Cannot get VMM Instance"
             }    
             if ($SystemCred -eq $null -or $SystemCred -eq "") {    
                throw "Cannot get SystemCred"
             }
             try
             {
                 InlineScript
                 {
                     Import-Module virtualmachinemanager
                     $VmmServer = get-scvmmserver -computername localhost
                     $UserRole = ""
                     $count = 0
                     do # it may take a while for the userrole to be created
                     {
                         $Userrole = Get-SCUserRole -ID $using:UserRoleID -VMMServer $VmmServer
                         sleep 10
                         $count++
                     } while (($UserRole -eq $null) -and ($count -lt 20))
                     if ($UserRole -eq $null)
                     {
                         throw "UserRole is not created in VMM"
                     }
    
                     #get service template
                     $serviceTemplate = get-SCServiceTemplate -Name $using:serviceTemplateName | where {$_.Release -eq $using:serviceTemplateRelease}
                     if ($serviceTemplate -eq $null)
                     {
                         throw "Service Template not found"
                     }
    
                     #Grant permission 
                     Grant-SCResource -Resource $serviceTemplate -UserRoleName $UserRole.Name
    
                 } -PSComputerName $VmmServer1 -PSCredential $SystemCred
             }
             catch
             {
                 throw $_
             }
    
         }
     }
    

 

                       

通过使用 .NET创建新服务

  1. 连接到 Service Provider Foundation VMM服务。
  2. 创建一个新的方法。我们将它称为CreateService。
  3. 创建一个新的VMConfiguration类实例。
  4. 将VMConfiguration实例添加到ObservableCollection<VMConfiguration> 类的新实例。
  5. 创建一个新的ServiceTierAndVMConfiguration类实例。
    • 设置Name属性。
    • 使用之前创建的ObservableCollection<VMConfiguration>类实例设置VMConfigurations属性。
  6. 创建一个新的ObservableCollection<ServiceTierAndVMConfiguration> 类实例。
    • 将该ServiceTierAndVMConfiguration实例添加到其中。
  7. 创建一个新的ServiceDeploymentConfiguration类实例。
    • 设置Name 属性。
    • 设置Description属性。
  8. 创建一个新的NewServiceDeployment类实例。
    • 使用之前创建的NewServiceDeployment实例设置ServiceConfiguration属性。
    • 使用之前创建的ObservableCollection<ServiceTierAndVMConfiguration>实例设置TierConfiguration属性。
  9. 创建一个新的Service实例。
 public Service CreateService(string subscriptionId, string name, ServiceTemplate serviceTemplate, Guid stampId, Guid cloudId)
         {
  
             ServiceDeploymentConfiguration sdc = new ServiceDeploymentConfiguration()
             {
                 Name = <name of your service deployment configuration>,
                 Description = <description of your service deployment configuration>
             };
             
             // Depending on the Service Template create the VMTier and VMConfig
             ObservableCollection<ServiceTierAndVMConfiguration> serviceTierAndVMConfiguration = new ObservableCollection<ServiceTierAndVMConfiguration>();
             ObservableCollection<VMConfiguration> vmConfigurations = new ObservableCollection<VMConfiguration>();
             VMConfiguration vmc = new VMConfiguration()
             {
                 ComputerName = <your tier VM name>
                 VMName = <your VM name here>
             };
             vmConfigurations.Add(vmc);
  
             ServiceTierAndVMConfiguration tierAndVMConfig  = new ServiceTierAndVMConfiguration()
             {
                 Name = "SQLServer Host Tier",
                 VMConfigurations = vmConfigurations
             };
             serviceTierAndVMConfiguration.Add(tierAndVMConfig);
  
             // Provide ServiceTemplate ID
             Guid serviceTemplateID = <your serviceTemplateID. You can retrieve the serviceTemplateID via RDFE query>
  
             // Provide Cloud ID
             Guid cloudID = <your cloudID>
  
             // Provide Stamp ID
             Guid stampID = <your stampID>
  
  
             // add the ServiceDeployment
             NewServiceDeployment serviceDep = new NewServiceDeployment { 
                 ServiceConfiguration = sdc ,
                 TierConfigurations = serviceTierAndVMConfiguration
             };
             
             // Instantiate a VMM Service        
             var service = new Service()
             {
                 Name = name,
                 StampId = stampID,
                 CloudId = cloudID,
                 ServiceTemplateId = serviceTemplateID
                 NewServiceDeployment = serviceDep
  
             };
  
             // POST a HTTP Request. Be sure to add the Tenant Certificate to the http request prior to this. See Snippet for Tenant Client below
             HttpResponseMessage response = TenantClient.PostAsJsonAsync<Service>(TenantServiceEndPoint + subscriptionId + @"/services/systemcenter/VMM/Services/", service);
             return response.Content.ReadAsAsync<Service>().Result;
         }

对 HTTP 请求执行POST

为了调用服务创建,您需要创建客户端代码执行HTTP POST。以租户客户端为例,请参阅下面的代码段:

 public TenantClient(string tenantServiceEndpoint, string authSiteEndPoint, string userName, string password, ref DateTime tokenExpiryTime)
         {
             httpClient = new HttpClient();
             var identityProviderEndpoint = new EndpointAddress(new Uri(authSiteEndPoint + "wstrust/issue/usernamemixed"));
  
             var identityProviderBinding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential);
             identityProviderBinding.Security.Message.EstablishSecurityContext = false;
             identityProviderBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
             identityProviderBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
  
             var trustChannelFactory = new WSTrustChannelFactory(identityProviderBinding, identityProviderEndpoint)
             {
                 TrustVersion = TrustVersion.WSTrust13,
             };
  
             trustChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication() { CertificateValidationMode = X509CertificateValidationMode.None };
             trustChannelFactory.Credentials.SupportInteractive = false;
             trustChannelFactory.Credentials.UserName.UserName = userName;
             trustChannelFactory.Credentials.UserName.Password = password;
  
             var channel = trustChannelFactory.CreateChannel();
             var rst = new RequestSecurityToken(RequestTypes.Issue)
             {
                 AppliesTo = new EndpointReference("https://azureservices/TenantSite"),
                 TokenType = "urn:ietf:params:oauth:token-type:jwt",
                 KeyType = KeyTypes.Bearer,
             };
  
             RequestSecurityTokenResponse rstr = null;
  
             var token = channel.Issue(rst, out rstr);
             tokenExpiryTime = new DateTime(token.ValidTo.Year, token.ValidTo.Month, token.ValidTo.Day, token.ValidTo.Hour, token.ValidTo.Minute, token.ValidTo.Second, DateTimeKind.Utc);
             var tokenString = (token as GenericXmlSecurityToken).TokenXml.InnerText;
             var jwtString = Encoding.UTF8.GetString(Convert.FromBase64String(tokenString));
  
             httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtString);
             this.tenantServiceEndPoint = tenantServiceEndpoint;
             httpClient.PrincipalId = <your ID>;
         }

为保证客户端正常使用,需要在 HTTP 请求前添加一个证书,如下面的示例所示:

 //Adding Certificate through Public Endpoint https://<>:30006/
  
             var handler = new WebRequestHandler();
             X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
             store.Open(OpenFlags.ReadOnly);
             var result = store.Certificates.Find(X509FindType.FindByIssuerName, <your trusted certificate autority>, false);
             if (result.Count == 0)
             {
                 throw new Exception("tenant public api certificate not found.");
             }
             X509Certificate2 cert = result[0];
             handler.ClientCertificates.Add(cert);
             httpClient = new HttpClient(handler);
             this.tenantServiceEndPoint = tenantServiceEndpoint;
  
               
             SubscriptionCertificate subscriptionCertificate = new SubscriptionCertificate()
             {
                 SubscriptionCertificateData = <data>,
                 SubscriptionCertificatePublicKey = <SubscriptionCertificatePublicKey>,
                 SubscriptionCertificateThumbprint = <SubscriptionCertificateThumbprint>
             };
  
             httpClient.PostAsXmlAsync<SubscriptionCertificate>(TenantServiceEndPoint + @"subscriptions/" + subscriptionId + @"/certificates", subscriptionCertificate);