yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/azure/classic_instance.go (about) 1 // Copyright 2019 Yunion 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package azure 16 17 import ( 18 "context" 19 "fmt" 20 "net/url" 21 "strings" 22 "time" 23 24 "yunion.io/x/jsonutils" 25 "yunion.io/x/log" 26 "yunion.io/x/pkg/errors" 27 "yunion.io/x/pkg/util/osprofile" 28 29 "yunion.io/x/cloudmux/pkg/apis" 30 billing_api "yunion.io/x/cloudmux/pkg/apis/billing" 31 api "yunion.io/x/cloudmux/pkg/apis/compute" 32 "yunion.io/x/cloudmux/pkg/cloudprovider" 33 "yunion.io/x/cloudmux/pkg/multicloud" 34 "yunion.io/x/onecloud/pkg/util/billing" 35 ) 36 37 type FormattedMessage struct { 38 Language string 39 Message string 40 } 41 42 type GuestAgentStatus struct { 43 ProtocolVersion string `json:"protocolVersion,omitempty"` 44 Timestamp time.Time `json:"timestamp,omitempty"` 45 GuestAgentVersion string `json:"guestAgentVersion,omitempty"` 46 Status string `json:"status,omitempty"` 47 FormattedMessage FormattedMessage `json:"formattedMessage,omitempty"` 48 } 49 50 type ClassicVirtualMachineInstanceView struct { 51 Status string `json:"status,omitempty"` 52 PowerState string `json:"powerState,omitempty"` 53 PublicIpAddresses []string `json:"publicIpAddresses,omitempty"` 54 FullyQualifiedDomainName string `json:"fullyQualifiedDomainName,omitempty"` 55 56 UpdateDomain int `json:"updateDomain,omitempty"` 57 FaultDomain int `json:"faultDomain,omitempty"` 58 StatusMessage string `json:"statusMessage,omitempty"` 59 PrivateIpAddress string `json:"privateIpAddress,omitempty"` 60 InstanceIpAddresses []string `json:"instanceIpAddresses,omitempty"` 61 ComputerName string `json:"computerName,omitempty"` 62 GuestAgentStatus GuestAgentStatus `json:"guestAgentStatus,omitempty"` 63 } 64 65 type SubResource struct { 66 ID string `json:"id,omitempty"` 67 Name string `json:"name,omitempty"` 68 Type string `json:"type,omitempty"` 69 } 70 71 type ClassicDisk struct { 72 Lun int32 73 DiskName string 74 Caching string 75 OperatingSystem string 76 IoType string 77 CreatedTime string 78 SourceImageName string 79 VhdUri string 80 DiskSize int32 `json:"diskSize,omitempty"` 81 StorageAccount SubResource 82 } 83 84 type ClassicStorageProfile struct { 85 OperatingSystemDisk ClassicDisk `json:"operatingSystemDisk,omitempty"` 86 DataDisks *[]ClassicDisk `json:"dataDisks,allowempty"` 87 } 88 89 type ClassicHardwareProfile struct { 90 PlatformGuestAgent bool 91 Size string 92 DeploymentName string 93 DeploymentId string 94 DeploymentLabel string 95 DeploymentLocked bool 96 } 97 98 type InputEndpoint struct { 99 EndpointName string 100 PrivatePort int32 101 PublicPort int32 102 Protocol string 103 EnableDirectServerReturn bool 104 } 105 106 type InstanceIp struct { 107 IdleTimeoutInMinutes int 108 ID string 109 Name string 110 Type string 111 } 112 113 type ClassicVirtualNetwork struct { 114 StaticIpAddress string `json:"staticIpAddress,omitempty"` 115 SubnetNames []string `json:"subnetNames,omitempty"` 116 ID string 117 Name string 118 Type string 119 } 120 121 type ClassicNetworkProfile struct { 122 InputEndpoints *[]InputEndpoint `json:"inputEndpoints,omitempty"` 123 InstanceIps *[]InstanceIp `json:"instanceIps,omitempty"` 124 ReservedIps *[]SubResource `json:"reservedIps,omitempty"` 125 VirtualNetwork ClassicVirtualNetwork `json:"virtualNetwork,omitempty"` 126 NetworkSecurityGroup *SubResource `json:"networkSecurityGroup,omitempty"` 127 } 128 129 type ClassicVirtualMachineProperties struct { 130 DomainName *SubResource `json:"domainName,omitempty"` 131 InstanceView *ClassicVirtualMachineInstanceView `json:"instanceView,omitempty"` 132 NetworkProfile ClassicNetworkProfile `json:"networkProfile,omitempty"` 133 HardwareProfile ClassicHardwareProfile `json:"hardwareProfile,omitempty"` 134 StorageProfile ClassicStorageProfile `json:"storageProfile,omitempty"` 135 } 136 137 type SClassicInstance struct { 138 multicloud.SInstanceBase 139 AzureTags 140 141 host *SClassicHost 142 143 idisks []cloudprovider.ICloudDisk 144 145 Properties ClassicVirtualMachineProperties `json:"properties,omitempty"` 146 ID string 147 Name string 148 Type string 149 Location string 150 } 151 152 func (self *SClassicInstance) GetSecurityGroupIds() ([]string, error) { 153 secgroupIds := []string{} 154 if self.Properties.NetworkProfile.NetworkSecurityGroup != nil { 155 secgroupIds = append(secgroupIds, self.Properties.NetworkProfile.NetworkSecurityGroup.ID) 156 } 157 return secgroupIds, nil 158 } 159 160 func (self *SClassicInstance) GetSysTags() map[string]string { 161 data := map[string]string{} 162 priceKey := fmt.Sprintf("%s::%s", self.Properties.HardwareProfile.Size, self.host.zone.region.Name) 163 data["price_key"] = priceKey 164 data["zone_ext_id"] = self.host.zone.GetGlobalId() 165 return data 166 } 167 168 func (self *SClassicInstance) GetHypervisor() string { 169 return api.HYPERVISOR_AZURE 170 } 171 172 func (self *SClassicInstance) GetInstanceType() string { 173 return self.Properties.HardwareProfile.Size 174 } 175 176 func (self *SRegion) GetClassicInstances() ([]SClassicInstance, error) { 177 instances := []SClassicInstance{} 178 err := self.list("Microsoft.ClassicCompute/virtualMachines", url.Values{}, &instances) 179 if err != nil { 180 return nil, err 181 } 182 return instances, nil 183 } 184 185 func (self *SRegion) GetClassicInstance(instanceId string) (*SClassicInstance, error) { 186 instance := SClassicInstance{} 187 params := url.Values{} 188 params.Add("$expand", "instanceView") 189 return &instance, self.get(instanceId, params, &instance) 190 } 191 192 func (self *SRegion) GetClassicInstanceDisks(instanceId string) ([]SClassicDisk, error) { 193 result := struct { 194 Value []SClassicDisk 195 }{} 196 resource := fmt.Sprintf("%s/disks", instanceId) 197 err := self.get(resource, url.Values{}, &result) 198 if err != nil { 199 return nil, errors.Wrapf(err, "list") 200 } 201 return result.Value, nil 202 } 203 204 func (self *SClassicInstance) getNics() ([]SClassicInstanceNic, error) { 205 instance, err := self.host.zone.region.GetClassicInstance(self.ID) 206 if err != nil { 207 return nil, err 208 } 209 networkProfile := instance.Properties.NetworkProfile 210 ip, id := "", "" 211 if len(networkProfile.VirtualNetwork.SubnetNames) > 0 { 212 id = fmt.Sprintf("%s/%s", networkProfile.VirtualNetwork.ID, networkProfile.VirtualNetwork.SubnetNames[0]) 213 } 214 if len(instance.Properties.NetworkProfile.VirtualNetwork.StaticIpAddress) > 0 { 215 ip = instance.Properties.NetworkProfile.VirtualNetwork.StaticIpAddress 216 } 217 if (len(id) == 0 || len(ip) == 0) && instance.Properties.InstanceView != nil && len(instance.Properties.InstanceView.PrivateIpAddress) > 0 { 218 if len(id) == 0 { 219 id = fmt.Sprintf("%s/%s", self.ID, instance.Properties.InstanceView.PrivateIpAddress) 220 } 221 if len(ip) == 0 { 222 ip = instance.Properties.InstanceView.PrivateIpAddress 223 } 224 } 225 if len(id) > 0 && len(ip) > 0 { 226 instanceNic := []SClassicInstanceNic{ 227 {instance: self, IP: ip, ID: id}, 228 } 229 return instanceNic, nil 230 } 231 return nil, nil 232 } 233 234 func (self *SClassicInstance) Refresh() error { 235 instance, err := self.host.zone.region.GetClassicInstance(self.ID) 236 if err != nil { 237 return err 238 } 239 return jsonutils.Update(self, instance) 240 } 241 242 func (self *SClassicInstance) GetStatus() string { 243 if self.Properties.InstanceView == nil { 244 err := self.Refresh() 245 if err != nil { 246 log.Errorf("failed to get status for classic instance %s", self.Name) 247 return api.VM_UNKNOWN 248 } 249 } 250 switch self.Properties.InstanceView.Status { 251 case "StoppedDeallocated": 252 return api.VM_READY 253 case "ReadyRole": 254 return api.VM_RUNNING 255 case "Stopped": 256 return api.VM_READY 257 case "RoleStateUnknown": 258 return api.VM_UNKNOWN 259 default: 260 log.Errorf("Unknow classic instance %s status %s", self.Name, self.Properties.InstanceView.Status) 261 return api.VM_UNKNOWN 262 } 263 } 264 265 func (self *SClassicInstance) GetIHost() cloudprovider.ICloudHost { 266 return self.host 267 } 268 269 func (self *SClassicInstance) AttachDisk(ctx context.Context, diskId string) error { 270 status := self.GetStatus() 271 if err := self.host.zone.region.AttachDisk(self.ID, diskId); err != nil { 272 return err 273 } 274 return cloudprovider.WaitStatus(self, status, 10*time.Second, 300*time.Second) 275 } 276 277 func (self *SClassicInstance) DetachDisk(ctx context.Context, diskId string) error { 278 status := self.GetStatus() 279 if err := self.host.zone.region.DetachDisk(self.ID, diskId); err != nil { 280 return err 281 } 282 return cloudprovider.WaitStatus(self, status, 10*time.Second, 300*time.Second) 283 } 284 285 func (self *SClassicInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error { 286 return cloudprovider.ErrNotImplemented 287 } 288 289 func (self *SClassicInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error { 290 return cloudprovider.ErrNotImplemented 291 //return self.host.zone.region.DeployVM(self.ID, name, password, publicKey, deleteKeypair, description) 292 } 293 294 func (self *SClassicInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) { 295 return "", cloudprovider.ErrNotImplemented 296 //return self.host.zone.region.ReplaceSystemDisk(self.ID, imageId, passwd, publicKey, int32(sysSizeGB)) 297 } 298 299 func (self *SClassicInstance) UpdateVM(ctx context.Context, name string) error { 300 return cloudprovider.ErrNotSupported 301 } 302 303 func (self *SClassicInstance) GetId() string { 304 return self.ID 305 } 306 307 func (self *SClassicInstance) GetName() string { 308 return self.Name 309 } 310 311 func (self *SClassicInstance) GetHostname() string { 312 return self.Name 313 } 314 315 func (self *SClassicInstance) GetGlobalId() string { 316 return strings.ToLower(self.ID) 317 } 318 319 func (self *SClassicInstance) DeleteVM(ctx context.Context) error { 320 if err := self.host.zone.region.DeleteVM(self.ID); err != nil { 321 return err 322 } 323 if self.Properties.NetworkProfile.NetworkSecurityGroup != nil { 324 self.host.zone.region.del(self.Properties.NetworkProfile.NetworkSecurityGroup.ID) 325 } 326 if self.Properties.DomainName != nil { 327 self.host.zone.region.del(self.Properties.DomainName.ID) 328 } 329 return nil 330 } 331 332 func (self *SClassicInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { 333 disks, err := self.host.zone.region.GetClassicInstanceDisks(self.ID) 334 if err != nil { 335 return nil, errors.Wrapf(err, "GetClassicInstanceDisks") 336 } 337 ret := []cloudprovider.ICloudDisk{} 338 for i := range disks { 339 disks[i].region = self.host.zone.region 340 ret = append(ret, &disks[i]) 341 } 342 return ret, nil 343 } 344 345 func (self *SClassicInstance) GetOsType() cloudprovider.TOsType { 346 return cloudprovider.TOsType(osprofile.NormalizeOSType(self.Properties.StorageProfile.OperatingSystemDisk.OperatingSystem)) 347 } 348 349 func (self *SClassicInstance) GetFullOsName() string { 350 return self.Properties.StorageProfile.OperatingSystemDisk.SourceImageName 351 } 352 353 func (self *SClassicInstance) GetBios() cloudprovider.TBiosType { 354 return cloudprovider.BIOS 355 } 356 357 func (sci *SClassicInstance) GetOsArch() string { 358 return apis.OS_ARCH_X86_64 359 } 360 361 func (sci *SClassicInstance) GetOsVersion() string { 362 return "" 363 } 364 365 func (sci *SClassicInstance) GetOsDist() string { 366 return "" 367 } 368 369 func (sci *SClassicInstance) GetOsLang() string { 370 return "" 371 } 372 373 func (self *SClassicInstance) GetINics() ([]cloudprovider.ICloudNic, error) { 374 instancenics := make([]cloudprovider.ICloudNic, 0) 375 nics, err := self.getNics() 376 if err != nil { 377 return nil, err 378 } 379 for i := 0; i < len(nics); i++ { 380 nics[i].instance = self 381 instancenics = append(instancenics, &nics[i]) 382 } 383 return instancenics, nil 384 } 385 386 func (self *SClassicInstance) GetMachine() string { 387 return "pc" 388 } 389 390 func (self *SClassicInstance) GetBootOrder() string { 391 return "dcn" 392 } 393 394 func (self *SClassicInstance) GetVga() string { 395 return "std" 396 } 397 398 func (self *SClassicInstance) GetVdi() string { 399 return "vnc" 400 } 401 402 func (self *SClassicInstance) GetVcpuCount() int { 403 if vmSize, ok := CLASSIC_VM_SIZES[self.Properties.HardwareProfile.Size]; ok { 404 return vmSize.NumberOfCores 405 } 406 log.Errorf("failed to find classic VMSize for %s", self.Properties.HardwareProfile.Size) 407 return 0 408 } 409 410 func (self *SClassicInstance) GetVmemSizeMB() int { 411 if vmSize, ok := CLASSIC_VM_SIZES[self.Properties.HardwareProfile.Size]; ok { 412 return vmSize.MemoryInMB 413 } 414 log.Errorf("failed to find classic VMSize for %s", self.Properties.HardwareProfile.Size) 415 return 0 416 } 417 418 func (self *SClassicInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) { 419 return nil, cloudprovider.ErrNotSupported 420 } 421 422 func (self *SClassicInstance) StartVM(ctx context.Context) error { 423 if err := self.host.zone.region.StartVM(self.ID); err != nil { 424 return err 425 } 426 return cloudprovider.WaitStatus(self, api.VM_RUNNING, 10*time.Second, 300*time.Second) 427 } 428 429 func (self *SClassicInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { 430 err := self.host.zone.region.StopClassicVM(self.ID, opts.IsForce) 431 if err != nil { 432 return err 433 } 434 return cloudprovider.WaitStatus(self, api.VM_READY, 10*time.Second, 300*time.Second) 435 } 436 437 func (self *SRegion) StopClassicVM(instanceId string, isForce bool) error { 438 _, err := self.perform(instanceId, "shutdown", nil) 439 return err 440 } 441 442 func (self *SClassicInstance) GetIEIP() (cloudprovider.ICloudEIP, error) { 443 if self.Properties.NetworkProfile.ReservedIps != nil && len(*self.Properties.NetworkProfile.ReservedIps) > 0 { 444 for _, reserveIp := range *self.Properties.NetworkProfile.ReservedIps { 445 eip, err := self.host.zone.region.GetClassicEip(reserveIp.ID) 446 if err == nil { 447 eip.instanceId = self.ID 448 if eip.Properties.AttachedTo != nil && eip.Properties.AttachedTo.ID != self.ID { 449 //一般是此实例deallocate, eip被绑到其他机器上了. 450 return nil, nil 451 } 452 return eip, nil 453 } 454 log.Errorf("failed find eip %s for classic instance %s", reserveIp.Name, self.Name) 455 } 456 } 457 if self.Properties.InstanceView != nil && len(self.Properties.InstanceView.PublicIpAddresses) > 0 { 458 eip := SClassicEipAddress{ 459 region: self.host.zone.region, 460 ID: self.ID, 461 instanceId: self.ID, 462 Name: self.Properties.InstanceView.PublicIpAddresses[0], 463 Properties: ClassicEipProperties{ 464 IpAddress: self.Properties.InstanceView.PublicIpAddresses[0], 465 }, 466 } 467 return &eip, nil 468 } 469 return nil, nil 470 } 471 472 type assignSecurityGroup struct { 473 ID string `json:"id,omitempty"` 474 Name string `json:"name,omitempty"` 475 Properties assignProperties `json:"properties,omitempty"` 476 Type string `json:"type,omitempty"` 477 } 478 479 type assignProperties struct { 480 NetworkSecurityGroup SubResource `json:"networkSecurityGroup,omitempty"` 481 } 482 483 func (self *SClassicInstance) SetSecurityGroups(secgroupIds []string) error { 484 return cloudprovider.ErrNotSupported 485 } 486 487 func (self *SClassicInstance) AssignSecurityGroup(secgroupId string) error { 488 if self.Properties.NetworkProfile.NetworkSecurityGroup != nil { 489 if self.Properties.NetworkProfile.NetworkSecurityGroup.ID == secgroupId { 490 return nil 491 } 492 self.host.zone.region.del(fmt.Sprintf("%s/associatedNetworkSecurityGroups/%s", self.ID, self.Properties.NetworkProfile.NetworkSecurityGroup.Name)) 493 } 494 495 secgroup, err := self.host.zone.region.GetClassicSecurityGroupDetails(secgroupId) 496 if err != nil { 497 return err 498 } 499 data := assignSecurityGroup{ 500 ID: fmt.Sprintf("%s/associatedNetworkSecurityGroups/%s", self.ID, secgroup.Name), 501 Name: secgroup.Name, 502 Properties: assignProperties{ 503 NetworkSecurityGroup: SubResource{ 504 ID: secgroup.ID, 505 Name: secgroup.Name, 506 }, 507 }, 508 } 509 return self.host.zone.region.update(jsonutils.Marshal(data), nil) 510 } 511 512 func (self *SClassicInstance) GetBillingType() string { 513 return billing_api.BILLING_TYPE_POSTPAID 514 } 515 516 func (self *SClassicInstance) GetCreatedAt() time.Time { 517 return time.Time{} 518 } 519 520 func (self *SClassicInstance) GetExpiredAt() time.Time { 521 return time.Time{} 522 } 523 524 func (self *SClassicInstance) UpdateUserData(userData string) error { 525 return cloudprovider.ErrNotSupported 526 } 527 528 func (self *SClassicInstance) Renew(bc billing.SBillingCycle) error { 529 return cloudprovider.ErrNotSupported 530 } 531 532 func (self *SClassicInstance) GetProjectId() string { 533 return getResourceGroup(self.ID) 534 } 535 536 func (self *SClassicInstance) GetError() error { 537 return nil 538 }