面向 VMM 服务模板的 Windows Azure 包、Service Provider Foundation 和 IaaS API 支持
简介
特别感谢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订阅)提供服务模板访问权限:
在WAP门户中打开管理控制台,访问Automation部分(左侧导航窗格),创建一个新的Runbook。
将您在下面看到的脚本粘贴到这个新的Runbook中。
使用环境特定值替换变量。
发布Runbook,确保将标记值“SPF”输入Runbook配置(只有这三个字母,无引号,这一点很关键,否则将无法在下一步看到Runbook),单击 Save。
访问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创建新服务
- 连接到 Service Provider Foundation VMM服务。
- 创建一个新的方法。我们将它称为CreateService。
- 创建一个新的VMConfiguration类实例。
- 设置ComputerName属性。
- 设置VMName属性。
- 将VMConfiguration实例添加到ObservableCollection<VMConfiguration> 类的新实例。
- 创建一个新的ServiceTierAndVMConfiguration类实例。
- 设置Name属性。
- 使用之前创建的ObservableCollection<VMConfiguration>类实例设置VMConfigurations属性。
- 创建一个新的ObservableCollection<ServiceTierAndVMConfiguration> 类实例。
- 将该ServiceTierAndVMConfiguration实例添加到其中。
- 创建一个新的ServiceDeploymentConfiguration类实例。
- 设置Name 属性。
- 设置Description属性。
- 创建一个新的NewServiceDeployment类实例。
- 使用之前创建的NewServiceDeployment实例设置ServiceConfiguration属性。
- 使用之前创建的ObservableCollection<ServiceTierAndVMConfiguration>实例设置TierConfiguration属性。
- 创建一个新的Service实例。
- 设置Name属性。
- 设置StampID属性。
- 设置CloudID属性。
- 设置ServiceTemplateId属性。
- 使用上一步创建的NewServiceDeployment实例设置NewServiceDeployment属性。
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);