1// +build go1.7 2 3// Package virtualmachine provides a client for Virtual Machines. 4package virtualmachine 5 6// Copyright 2017 Microsoft Corporation 7// 8// Licensed under the Apache License, Version 2.0 (the "License"); 9// you may not use this file except in compliance with the License. 10// You may obtain a copy of the License at 11// 12// http://www.apache.org/licenses/LICENSE-2.0 13// 14// Unless required by applicable law or agreed to in writing, software 15// distributed under the License is distributed on an "AS IS" BASIS, 16// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17// See the License for the specific language governing permissions and 18// limitations under the License. 19 20import ( 21 "encoding/xml" 22 "fmt" 23 24 "github.com/Azure/azure-sdk-for-go/services/classic/management" 25) 26 27const ( 28 azureDeploymentListURL = "services/hostedservices/%s/deployments" 29 azureDeploymentURL = "services/hostedservices/%s/deployments/%s" 30 azureListDeploymentsInSlotURL = "services/hostedservices/%s/deploymentslots/Production" 31 deleteAzureDeploymentURL = "services/hostedservices/%s/deployments/%s?comp=media" 32 azureAddRoleURL = "services/hostedservices/%s/deployments/%s/roles" 33 azureRoleURL = "services/hostedservices/%s/deployments/%s/roles/%s" 34 azureOperationsURL = "services/hostedservices/%s/deployments/%s/roleinstances/%s/Operations" 35 azureRoleSizeListURL = "rolesizes" 36 37 errParamNotSpecified = "Parameter %s is not specified." 38) 39 40//NewClient is used to instantiate a new VirtualMachineClient from an Azure client 41func NewClient(client management.Client) VirtualMachineClient { 42 return VirtualMachineClient{client: client} 43} 44 45// CreateDeploymentOptions can be used to create a customized deployement request 46type CreateDeploymentOptions struct { 47 DNSServers []DNSServer 48 LoadBalancers []LoadBalancer 49 ReservedIPName string 50 VirtualNetworkName string 51} 52 53// CreateDeployment creates a deployment and then creates a virtual machine 54// in the deployment based on the specified configuration. 55// 56// https://msdn.microsoft.com/en-us/library/azure/jj157194.aspx 57func (vm VirtualMachineClient) CreateDeployment( 58 role Role, 59 cloudServiceName string, 60 options CreateDeploymentOptions) (management.OperationID, error) { 61 62 req := DeploymentRequest{ 63 Name: role.RoleName, 64 DeploymentSlot: "Production", 65 Label: role.RoleName, 66 RoleList: []Role{role}, 67 DNSServers: options.DNSServers, 68 LoadBalancers: options.LoadBalancers, 69 ReservedIPName: options.ReservedIPName, 70 VirtualNetworkName: options.VirtualNetworkName, 71 } 72 73 data, err := xml.Marshal(req) 74 if err != nil { 75 return "", err 76 } 77 78 requestURL := fmt.Sprintf(azureDeploymentListURL, cloudServiceName) 79 return vm.client.SendAzurePostRequest(requestURL, data) 80} 81 82// GetDeploymentName queries an existing Azure cloud service for the name of the Deployment, 83// if any, in its 'Production' slot (the only slot possible). If none exists, it returns empty 84// string but no error 85// 86//https://msdn.microsoft.com/en-us/library/azure/ee460804.aspx 87func (vm VirtualMachineClient) GetDeploymentName(cloudServiceName string) (string, error) { 88 var deployment DeploymentResponse 89 if cloudServiceName == "" { 90 return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName") 91 } 92 requestURL := fmt.Sprintf(azureListDeploymentsInSlotURL, cloudServiceName) 93 response, err := vm.client.SendAzureGetRequest(requestURL) 94 if err != nil { 95 if management.IsResourceNotFoundError(err) { 96 return "", nil 97 } 98 return "", err 99 } 100 err = xml.Unmarshal(response, &deployment) 101 if err != nil { 102 return "", err 103 } 104 105 return deployment.Name, nil 106} 107 108func (vm VirtualMachineClient) GetDeployment(cloudServiceName, deploymentName string) (DeploymentResponse, error) { 109 var deployment DeploymentResponse 110 if cloudServiceName == "" { 111 return deployment, fmt.Errorf(errParamNotSpecified, "cloudServiceName") 112 } 113 if deploymentName == "" { 114 return deployment, fmt.Errorf(errParamNotSpecified, "deploymentName") 115 } 116 requestURL := fmt.Sprintf(azureDeploymentURL, cloudServiceName, deploymentName) 117 response, azureErr := vm.client.SendAzureGetRequest(requestURL) 118 if azureErr != nil { 119 return deployment, azureErr 120 } 121 122 err := xml.Unmarshal(response, &deployment) 123 return deployment, err 124} 125 126func (vm VirtualMachineClient) DeleteDeployment(cloudServiceName, deploymentName string) (management.OperationID, error) { 127 if cloudServiceName == "" { 128 return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName") 129 } 130 if deploymentName == "" { 131 return "", fmt.Errorf(errParamNotSpecified, "deploymentName") 132 } 133 134 requestURL := fmt.Sprintf(deleteAzureDeploymentURL, cloudServiceName, deploymentName) 135 return vm.client.SendAzureDeleteRequest(requestURL) 136} 137 138func (vm VirtualMachineClient) GetRole(cloudServiceName, deploymentName, roleName string) (*Role, error) { 139 if cloudServiceName == "" { 140 return nil, fmt.Errorf(errParamNotSpecified, "cloudServiceName") 141 } 142 if deploymentName == "" { 143 return nil, fmt.Errorf(errParamNotSpecified, "deploymentName") 144 } 145 if roleName == "" { 146 return nil, fmt.Errorf(errParamNotSpecified, "roleName") 147 } 148 149 role := new(Role) 150 151 requestURL := fmt.Sprintf(azureRoleURL, cloudServiceName, deploymentName, roleName) 152 response, azureErr := vm.client.SendAzureGetRequest(requestURL) 153 if azureErr != nil { 154 return nil, azureErr 155 } 156 157 err := xml.Unmarshal(response, role) 158 if err != nil { 159 return nil, err 160 } 161 162 return role, nil 163} 164 165// AddRole adds a Virtual Machine to a deployment of Virtual Machines, where role name = VM name 166// See https://msdn.microsoft.com/en-us/library/azure/jj157186.aspx 167func (vm VirtualMachineClient) AddRole(cloudServiceName string, deploymentName string, role Role) (management.OperationID, error) { 168 if cloudServiceName == "" { 169 return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName") 170 } 171 if deploymentName == "" { 172 return "", fmt.Errorf(errParamNotSpecified, "deploymentName") 173 } 174 175 data, err := xml.Marshal(PersistentVMRole{Role: role}) 176 if err != nil { 177 return "", err 178 } 179 180 requestURL := fmt.Sprintf(azureAddRoleURL, cloudServiceName, deploymentName) 181 return vm.client.SendAzurePostRequest(requestURL, data) 182} 183 184// UpdateRole updates the configuration of the specified virtual machine 185// See https://msdn.microsoft.com/en-us/library/azure/jj157187.aspx 186func (vm VirtualMachineClient) UpdateRole(cloudServiceName, deploymentName, roleName string, role Role) (management.OperationID, error) { 187 if cloudServiceName == "" { 188 return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName") 189 } 190 if deploymentName == "" { 191 return "", fmt.Errorf(errParamNotSpecified, "deploymentName") 192 } 193 if roleName == "" { 194 return "", fmt.Errorf(errParamNotSpecified, "roleName") 195 } 196 197 data, err := xml.Marshal(PersistentVMRole{Role: role}) 198 if err != nil { 199 return "", err 200 } 201 202 requestURL := fmt.Sprintf(azureRoleURL, cloudServiceName, deploymentName, roleName) 203 return vm.client.SendAzurePutRequest(requestURL, "text/xml", data) 204} 205 206func (vm VirtualMachineClient) StartRole(cloudServiceName, deploymentName, roleName string) (management.OperationID, error) { 207 if cloudServiceName == "" { 208 return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName") 209 } 210 if deploymentName == "" { 211 return "", fmt.Errorf(errParamNotSpecified, "deploymentName") 212 } 213 if roleName == "" { 214 return "", fmt.Errorf(errParamNotSpecified, "roleName") 215 } 216 217 startRoleOperationBytes, err := xml.Marshal(StartRoleOperation{ 218 OperationType: "StartRoleOperation", 219 }) 220 if err != nil { 221 return "", err 222 } 223 224 requestURL := fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName) 225 return vm.client.SendAzurePostRequest(requestURL, startRoleOperationBytes) 226} 227 228func (vm VirtualMachineClient) ShutdownRole(cloudServiceName, deploymentName, roleName string, postaction PostShutdownAction) (management.OperationID, error) { 229 if cloudServiceName == "" { 230 return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName") 231 } 232 if deploymentName == "" { 233 return "", fmt.Errorf(errParamNotSpecified, "deploymentName") 234 } 235 if roleName == "" { 236 return "", fmt.Errorf(errParamNotSpecified, "roleName") 237 } 238 239 shutdownRoleOperationBytes, err := xml.Marshal(ShutdownRoleOperation{ 240 OperationType: "ShutdownRoleOperation", 241 PostShutdownAction: postaction, 242 }) 243 if err != nil { 244 return "", err 245 } 246 247 requestURL := fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName) 248 return vm.client.SendAzurePostRequest(requestURL, shutdownRoleOperationBytes) 249} 250 251func (vm VirtualMachineClient) RestartRole(cloudServiceName, deploymentName, roleName string) (management.OperationID, error) { 252 if cloudServiceName == "" { 253 return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName") 254 } 255 if deploymentName == "" { 256 return "", fmt.Errorf(errParamNotSpecified, "deploymentName") 257 } 258 if roleName == "" { 259 return "", fmt.Errorf(errParamNotSpecified, "roleName") 260 } 261 262 restartRoleOperationBytes, err := xml.Marshal(RestartRoleOperation{ 263 OperationType: "RestartRoleOperation", 264 }) 265 if err != nil { 266 return "", err 267 } 268 269 requestURL := fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName) 270 return vm.client.SendAzurePostRequest(requestURL, restartRoleOperationBytes) 271} 272 273func (vm VirtualMachineClient) DeleteRole(cloudServiceName, deploymentName, roleName string, deleteVHD bool) (management.OperationID, error) { 274 if cloudServiceName == "" { 275 return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName") 276 } 277 if deploymentName == "" { 278 return "", fmt.Errorf(errParamNotSpecified, "deploymentName") 279 } 280 if roleName == "" { 281 return "", fmt.Errorf(errParamNotSpecified, "roleName") 282 } 283 284 requestURL := fmt.Sprintf(azureRoleURL, cloudServiceName, deploymentName, roleName) 285 if deleteVHD { 286 requestURL += "?comp=media" 287 } 288 return vm.client.SendAzureDeleteRequest(requestURL) 289} 290 291func (vm VirtualMachineClient) GetRoleSizeList() (RoleSizeList, error) { 292 roleSizeList := RoleSizeList{} 293 294 response, err := vm.client.SendAzureGetRequest(azureRoleSizeListURL) 295 if err != nil { 296 return roleSizeList, err 297 } 298 299 err = xml.Unmarshal(response, &roleSizeList) 300 return roleSizeList, err 301} 302 303// CaptureRole captures a VM role. If reprovisioningConfigurationSet is non-nil, 304// the VM role is redeployed after capturing the image, otherwise, the original 305// VM role is deleted. 306// 307// NOTE: an image resulting from this operation shows up in 308// osimage.GetImageList() as images with Category "User". 309func (vm VirtualMachineClient) CaptureRole(cloudServiceName, deploymentName, roleName, imageName, imageLabel string, 310 reprovisioningConfigurationSet *ConfigurationSet) (management.OperationID, error) { 311 if cloudServiceName == "" { 312 return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName") 313 } 314 if deploymentName == "" { 315 return "", fmt.Errorf(errParamNotSpecified, "deploymentName") 316 } 317 if roleName == "" { 318 return "", fmt.Errorf(errParamNotSpecified, "roleName") 319 } 320 321 if reprovisioningConfigurationSet != nil && 322 !(reprovisioningConfigurationSet.ConfigurationSetType == ConfigurationSetTypeLinuxProvisioning || 323 reprovisioningConfigurationSet.ConfigurationSetType == ConfigurationSetTypeWindowsProvisioning) { 324 return "", fmt.Errorf("ConfigurationSet type can only be WindowsProvisioningConfiguration or LinuxProvisioningConfiguration") 325 } 326 327 operation := CaptureRoleOperation{ 328 OperationType: "CaptureRoleOperation", 329 PostCaptureAction: PostCaptureActionReprovision, 330 ProvisioningConfiguration: reprovisioningConfigurationSet, 331 TargetImageLabel: imageLabel, 332 TargetImageName: imageName, 333 } 334 if reprovisioningConfigurationSet == nil { 335 operation.PostCaptureAction = PostCaptureActionDelete 336 } 337 338 data, err := xml.Marshal(operation) 339 if err != nil { 340 return "", err 341 } 342 343 return vm.client.SendAzurePostRequest(fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName), data) 344} 345