yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/esxi/virtualmachine.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 esxi 16 17 import ( 18 "context" 19 "fmt" 20 "reflect" 21 "sort" 22 "strings" 23 "time" 24 25 "github.com/vmware/govmomi/nfc" 26 "github.com/vmware/govmomi/object" 27 "github.com/vmware/govmomi/vim25/methods" 28 "github.com/vmware/govmomi/vim25/mo" 29 "github.com/vmware/govmomi/vim25/soap" 30 "github.com/vmware/govmomi/vim25/types" 31 32 "yunion.io/x/jsonutils" 33 "yunion.io/x/log" 34 "yunion.io/x/pkg/errors" 35 "yunion.io/x/pkg/util/netutils" 36 "yunion.io/x/pkg/util/reflectutils" 37 "yunion.io/x/pkg/util/regutils" 38 "yunion.io/x/pkg/utils" 39 40 billing_api "yunion.io/x/cloudmux/pkg/apis/billing" 41 api "yunion.io/x/cloudmux/pkg/apis/compute" 42 "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" 43 cloudtypes "yunion.io/x/onecloud/pkg/cloudcommon/types" 44 "yunion.io/x/cloudmux/pkg/cloudprovider" 45 "yunion.io/x/onecloud/pkg/httperrors" 46 "yunion.io/x/cloudmux/pkg/multicloud" 47 "yunion.io/x/onecloud/pkg/util/billing" 48 "yunion.io/x/onecloud/pkg/util/imagetools" 49 "yunion.io/x/onecloud/pkg/util/netutils2" 50 "yunion.io/x/onecloud/pkg/util/version" 51 ) 52 53 var ( 54 vmSummaryProps = []string{"summary.runtime.powerState", "summary.config.uuid", "summary.config.memorySizeMB", "summary.config.numCpu"} 55 // vmConfigProps = []string{"config.template", "config.alternateGuestName", "config.hardware", "config.guestId", "config.guestFullName", "config.firmware", "config.version", "config.createDate"} 56 vmGuestProps = []string{"guest.net", "guest.guestState", "guest.toolsStatus", "guest.toolsRunningStatus", "guest.toolsVersion"} 57 vmLayoutExProps = []string{"layoutEx.file"} 58 ) 59 60 var VIRTUAL_MACHINE_PROPS = []string{"name", "parent", "resourcePool", "snapshot", "config"} 61 62 func init() { 63 VIRTUAL_MACHINE_PROPS = append(VIRTUAL_MACHINE_PROPS, vmSummaryProps...) 64 // VIRTUAL_MACHINE_PROPS = append(VIRTUAL_MACHINE_PROPS, vmConfigProps...) 65 VIRTUAL_MACHINE_PROPS = append(VIRTUAL_MACHINE_PROPS, vmGuestProps...) 66 } 67 68 type SVirtualMachine struct { 69 multicloud.SInstanceBase 70 multicloud.STagBase 71 SManagedObject 72 73 vnics []SVirtualNIC 74 vdisks []SVirtualDisk 75 vga SVirtualVGA 76 cdroms []SVirtualCdrom 77 devs map[int32]SVirtualDevice 78 ihost cloudprovider.ICloudHost 79 snapshots []SVirtualMachineSnapshot 80 81 guestIps map[string]string 82 83 osInfo *imagetools.ImageInfo 84 } 85 86 type VMFetcher interface { 87 FetchNoTemplateVMs() ([]*SVirtualMachine, error) 88 FetchTemplateVMs() ([]*SVirtualMachine, error) 89 FetchFakeTempateVMs(string) ([]*SVirtualMachine, error) 90 } 91 92 type byDiskType []SVirtualDisk 93 94 func (d byDiskType) Len() int { return len(d) } 95 func (d byDiskType) Swap(i, j int) { d[i], d[j] = d[j], d[i] } 96 func (d byDiskType) Less(i, j int) bool { 97 if d[i].GetDiskType() == api.DISK_TYPE_SYS { 98 return true 99 } 100 return false 101 } 102 103 func NewVirtualMachine(manager *SESXiClient, vm *mo.VirtualMachine, dc *SDatacenter) *SVirtualMachine { 104 svm := &SVirtualMachine{SManagedObject: newManagedObject(manager, vm, dc)} 105 err := svm.fetchHardwareInfo() 106 if err != nil { 107 log.Errorf("NewVirtualMachine: %v", err) 108 return nil 109 } 110 return svm 111 } 112 113 func (self *SVirtualMachine) GetSecurityGroupIds() ([]string, error) { 114 return []string{}, cloudprovider.ErrNotSupported 115 } 116 117 func (self *SVirtualMachine) GetSysTags() map[string]string { 118 meta := map[string]string{} 119 meta["datacenter"] = self.GetDatacenterPathString() 120 rp, _ := self.getResourcePool() 121 if rp != nil { 122 rpPath := rp.GetPath() 123 rpOffset := -1 124 for i := range rpPath { 125 if rpPath[i] == "Resources" { 126 if i > 0 { 127 meta["cluster"] = rpPath[i-1] 128 rpOffset = i 129 } 130 } else if rpOffset >= 0 && i > rpOffset { 131 meta[fmt.Sprintf("pool%d", i-rpOffset-1)] = rpPath[i] 132 } 133 } 134 } 135 return meta 136 } 137 138 func (self *SVirtualMachine) getVirtualMachine() *mo.VirtualMachine { 139 return self.object.(*mo.VirtualMachine) 140 } 141 142 func (self *SVirtualMachine) GetGlobalId() string { 143 return self.getUuid() 144 } 145 146 func (self *SVirtualMachine) GetHostname() string { 147 return self.GetName() 148 } 149 150 func (self *SVirtualMachine) GetStatus() string { 151 // err := self.CheckFileInfo(context.Background()) 152 // if err != nil { 153 // return api.VM_UNKNOWN 154 // } 155 vm := object.NewVirtualMachine(self.manager.client.Client, self.getVirtualMachine().Self) 156 state, err := vm.PowerState(self.manager.context) 157 if err != nil { 158 return api.VM_UNKNOWN 159 } 160 switch state { 161 case types.VirtualMachinePowerStatePoweredOff: 162 return api.VM_READY 163 case types.VirtualMachinePowerStatePoweredOn: 164 return api.VM_RUNNING 165 case types.VirtualMachinePowerStateSuspended: 166 return api.VM_SUSPEND 167 default: 168 return api.VM_UNKNOWN 169 } 170 } 171 172 func (self *SVirtualMachine) Refresh() error { 173 base := self.SManagedObject 174 var moObj mo.VirtualMachine 175 err := self.manager.reference2Object(self.object.Reference(), VIRTUAL_MACHINE_PROPS, &moObj) 176 if err != nil { 177 return err 178 } 179 base.object = &moObj 180 *self = SVirtualMachine{} 181 self.SManagedObject = base 182 self.fetchHardwareInfo() 183 return nil 184 } 185 186 func (self *SVirtualMachine) IsEmulated() bool { 187 return false 188 } 189 190 func (self *SVirtualMachine) GetInstanceType() string { 191 return "" 192 } 193 194 func (self *SVirtualMachine) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error { 195 return cloudprovider.ErrNotImplemented 196 } 197 198 func (self *SVirtualMachine) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) { 199 return "", cloudprovider.ErrNotImplemented 200 } 201 202 func (self *SVirtualMachine) DoRebuildRoot(ctx context.Context, imagePath string, uuid string) error { 203 if len(self.vdisks) == 0 { 204 return errors.ErrNotFound 205 } 206 return self.rebuildDisk(ctx, &self.vdisks[0], imagePath) 207 } 208 209 func (self *SVirtualMachine) rebuildDisk(ctx context.Context, disk *SVirtualDisk, imagePath string) error { 210 uuid := disk.GetId() 211 sizeMb := disk.GetDiskSizeMB() 212 diskKey := disk.getKey() 213 ctlKey := disk.getControllerKey() 214 unitNumber := *disk.dev.GetVirtualDevice().UnitNumber 215 216 err := self.doDetachAndDeleteDisk(ctx, disk) 217 if err != nil { 218 return err 219 } 220 return self.createDiskInternal(ctx, SDiskConfig{ 221 SizeMb: int64(sizeMb), 222 Uuid: uuid, 223 ControllerKey: ctlKey, 224 UnitNumber: unitNumber, 225 Key: diskKey, 226 ImagePath: imagePath, 227 IsRoot: len(imagePath) > 0, 228 }, false) 229 } 230 231 func (self *SVirtualMachine) UpdateVM(ctx context.Context, name string) error { 232 return cloudprovider.ErrNotImplemented 233 } 234 235 // TODO: detach disk to a separate directory, so as to keep disk independent of VM 236 237 func (self *SVirtualMachine) DetachDisk(ctx context.Context, diskId string) error { 238 vdisk, err := self.GetIDiskById(diskId) 239 if err != nil { 240 return err 241 } 242 return self.doDetachDisk(ctx, vdisk.(*SVirtualDisk), false) 243 } 244 245 func (self *SVirtualMachine) AttachDisk(ctx context.Context, diskId string) error { 246 return cloudprovider.ErrNotImplemented 247 } 248 249 func (self *SVirtualMachine) getUuid() string { 250 return self.getVirtualMachine().Summary.Config.Uuid 251 } 252 253 func (self *SVirtualMachine) GetIHost() cloudprovider.ICloudHost { 254 if self.ihost == nil { 255 self.ihost = self.getIHost() 256 } 257 return self.ihost 258 } 259 260 func (self *SVirtualMachine) getIHost() cloudprovider.ICloudHost { 261 vm := self.getVmObj() 262 263 hostsys, err := vm.HostSystem(self.manager.context) 264 if err != nil { 265 log.Errorf("fail to find host system for vm %s", err) 266 return nil 267 } 268 ihost, err := self.manager.FindHostByMoId(moRefId(hostsys.Reference())) 269 if err != nil { 270 log.Errorf("fail to find host %s for vm %s???", hostsys.Name(), self.GetName()) 271 return nil 272 } 273 return ihost 274 } 275 276 func (self *SVirtualMachine) GetIDisks() ([]cloudprovider.ICloudDisk, error) { 277 idisks := make([]cloudprovider.ICloudDisk, len(self.vdisks)) 278 for i := 0; i < len(self.vdisks); i += 1 { 279 idisks[i] = &(self.vdisks[i]) 280 } 281 return idisks, nil 282 } 283 284 func (self *SVirtualMachine) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) { 285 for i := 0; i < len(self.vdisks); i += 1 { 286 if self.vdisks[i].MatchId(idStr) { 287 return &self.vdisks[i], nil 288 } 289 } 290 return nil, cloudprovider.ErrNotFound 291 } 292 293 func (self *SVirtualMachine) GetINics() ([]cloudprovider.ICloudNic, error) { 294 inics := make([]cloudprovider.ICloudNic, len(self.vnics)) 295 for i := 0; i < len(self.vnics); i += 1 { 296 inics[i] = &(self.vnics[i]) 297 } 298 return inics, nil 299 } 300 301 func (self *SVirtualMachine) GetIEIP() (cloudprovider.ICloudEIP, error) { 302 return nil, nil 303 } 304 305 func (self *SVirtualMachine) GetVcpuCount() int { 306 return int(self.getVirtualMachine().Summary.Config.NumCpu) 307 } 308 309 func (self *SVirtualMachine) GetVmemSizeMB() int { 310 return int(self.getVirtualMachine().Summary.Config.MemorySizeMB) 311 } 312 313 func (self *SVirtualMachine) GetBootOrder() string { 314 return "cdn" 315 } 316 317 func (self *SVirtualMachine) GetVga() string { 318 return "vga" 319 } 320 321 func (self *SVirtualMachine) GetVdi() string { 322 return "vmrc" 323 } 324 325 func (self *SVirtualMachine) GetGuestFamily() string { 326 moVM := self.getVirtualMachine() 327 return moVM.Config.AlternateGuestName 328 } 329 330 func (self *SVirtualMachine) GetGuestId() string { 331 moVM := self.getVirtualMachine() 332 return moVM.Config.GuestId 333 } 334 335 func (self *SVirtualMachine) GetGuestFullName() string { 336 moVM := self.getVirtualMachine() 337 return moVM.Config.GuestFullName 338 } 339 340 func (self *SVirtualMachine) GetGuestState() string { 341 moVM := self.getVirtualMachine() 342 return moVM.Guest.GuestState 343 } 344 345 func (self *SVirtualMachine) GetGuestToolsStatus() string { 346 moVM := self.getVirtualMachine() 347 return string(moVM.Guest.ToolsStatus) 348 } 349 350 func (self *SVirtualMachine) isToolsOk() bool { 351 switch self.getVirtualMachine().Guest.ToolsStatus { 352 case types.VirtualMachineToolsStatusToolsNotInstalled: 353 return false 354 case types.VirtualMachineToolsStatusToolsNotRunning: 355 return false 356 } 357 return true 358 } 359 360 func (self *SVirtualMachine) GetGuestToolsRunningStatus() string { 361 moVM := self.getVirtualMachine() 362 return string(moVM.Guest.ToolsRunningStatus) 363 } 364 365 func (vm *SVirtualMachine) getNormalizedOsInfo() *imagetools.ImageInfo { 366 if vm.osInfo == nil { 367 if osInfo, ok := GuestOsInfo[vm.GetGuestId()]; ok { 368 osInfo := imagetools.NormalizeImageInfo("", string(osInfo.OsArch), string(osInfo.OsType), osInfo.OsDistribution, osInfo.OsVersion) 369 vm.osInfo = &osInfo 370 } else { 371 osInfo := imagetools.NormalizeImageInfo("", "", "", "", "") 372 vm.osInfo = &osInfo 373 } 374 } 375 return vm.osInfo 376 } 377 378 func (vm *SVirtualMachine) GetOsType() cloudprovider.TOsType { 379 return cloudprovider.TOsType(vm.getNormalizedOsInfo().OsType) 380 } 381 382 func (vm *SVirtualMachine) GetFullOsName() string { 383 return vm.getNormalizedOsInfo().GetFullOsName() 384 } 385 386 func (vm *SVirtualMachine) GetOsDist() string { 387 return vm.getNormalizedOsInfo().OsDistro 388 } 389 390 func (vm *SVirtualMachine) GetOsVersion() string { 391 return vm.getNormalizedOsInfo().OsVersion 392 } 393 394 func (vm *SVirtualMachine) GetOsLang() string { 395 return vm.getNormalizedOsInfo().OsLang 396 } 397 398 func (vm *SVirtualMachine) GetOsArch() string { 399 return vm.getNormalizedOsInfo().OsArch 400 } 401 402 func (vm *SVirtualMachine) GetBios() cloudprovider.TBiosType { 403 return cloudprovider.ToBiosType(vm.getBios()) 404 } 405 406 func (vm *SVirtualMachine) getBios() string { 407 // self.obj.config.firmware 408 switch vm.getVirtualMachine().Config.Firmware { 409 case "efi": 410 return "UEFI" 411 case "bios": 412 return "BIOS" 413 default: 414 return "BIOS" 415 } 416 } 417 418 func (self *SVirtualMachine) GetMachine() string { 419 return "pc" 420 } 421 422 func (self *SVirtualMachine) GetHypervisor() string { 423 return api.HYPERVISOR_ESXI 424 } 425 426 func (self *SVirtualMachine) getVmObj() *object.VirtualMachine { 427 return object.NewVirtualMachine(self.manager.client.Client, self.getVirtualMachine().Self) 428 } 429 430 // ideopotent start 431 func (self *SVirtualMachine) StartVM(ctx context.Context) error { 432 if self.GetStatus() == api.VM_RUNNING { 433 return nil 434 } 435 return self.startVM(ctx) 436 } 437 438 func (self *SVirtualMachine) startVM(ctx context.Context) error { 439 ihost := self.GetIHost() 440 if ihost == nil { 441 return errors.Wrap(httperrors.ErrInvalidStatus, "no valid host") 442 } 443 444 lockman.LockRawObject(ctx, "host", ihost.GetGlobalId()) 445 defer lockman.ReleaseRawObject(ctx, "host", ihost.GetGlobalId()) 446 447 err := self.makeNicsStartConnected(ctx) 448 if err != nil { 449 log.Errorf("self.makeNicsStartConnected %s", err) 450 return err 451 } 452 453 vm := self.getVmObj() 454 455 task, err := vm.PowerOn(ctx) 456 if err != nil { 457 log.Errorf("vm.PowerOn %s", err) 458 return err 459 } 460 err = task.Wait(ctx) 461 if err != nil { 462 log.Errorf("task.Wait %s", err) 463 return err 464 } 465 return nil 466 } 467 468 func (self *SVirtualMachine) makeNicsStartConnected(ctx context.Context) error { 469 spec := types.VirtualMachineConfigSpec{} 470 spec.DeviceChange = make([]types.BaseVirtualDeviceConfigSpec, len(self.vnics)) 471 for i := 0; i < len(self.vnics); i += 1 { 472 spec.DeviceChange[i] = makeNicStartConnected(&self.vnics[i]) 473 } 474 475 vm := self.getVmObj() 476 477 task, err := vm.Reconfigure(ctx, spec) 478 if err != nil { 479 return err 480 } 481 return task.Wait(ctx) 482 } 483 484 func makeNicStartConnected(nic *SVirtualNIC) *types.VirtualDeviceConfigSpec { 485 editSpec := types.VirtualDeviceConfigSpec{} 486 editSpec.Operation = types.VirtualDeviceConfigSpecOperationEdit 487 editSpec.FileOperation = "" 488 editSpec.Device = nic.dev 489 editSpec.Device.GetVirtualDevice().Connectable.StartConnected = true 490 return &editSpec 491 } 492 493 func (self *SVirtualMachine) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { 494 if self.GetStatus() == api.VM_READY { 495 return nil 496 } 497 if !opts.IsForce && self.isToolsOk() { 498 return self.shutdownVM(ctx) 499 } else { 500 return self.poweroffVM(ctx) 501 } 502 } 503 504 func (self *SVirtualMachine) SuspendVM(ctx context.Context) error { 505 vm := self.getVmObj() 506 task, err := vm.Suspend(ctx) 507 if err != nil { 508 return err 509 } 510 return task.Wait(ctx) 511 } 512 513 func (self *SVirtualMachine) ResumeVM(ctx context.Context) error { 514 if self.GetStatus() == api.VM_RUNNING { 515 return nil 516 } 517 vm := self.getVmObj() 518 519 task, err := vm.PowerOn(ctx) 520 if err != nil { 521 return errors.Wrap(err, "VM.PowerOn") 522 } 523 err = task.Wait(ctx) 524 if err != nil { 525 return errors.Wrap(err, "Task.Wait after VM.PowerOn") 526 } 527 return nil 528 } 529 530 func (self *SVirtualMachine) poweroffVM(ctx context.Context) error { 531 vm := self.getVmObj() 532 533 task, err := vm.PowerOff(ctx) 534 if err != nil { 535 return err 536 } 537 return task.Wait(ctx) 538 } 539 540 func (self *SVirtualMachine) shutdownVM(ctx context.Context) error { 541 vm := self.getVmObj() 542 543 err := vm.ShutdownGuest(ctx) 544 if err != nil { 545 return err 546 } 547 return err 548 } 549 550 func (self *SVirtualMachine) doDestroy(ctx context.Context) error { 551 vm := self.getVmObj() 552 task, err := vm.Destroy(ctx) 553 if err != nil { 554 return errors.Wrap(err, "unable to destroy vm") 555 } 556 return task.Wait(ctx) 557 } 558 559 func (self *SVirtualMachine) doDelete(ctx context.Context) error { 560 // detach all disks first 561 for i := range self.vdisks { 562 err := self.doDetachAndDeleteDisk(ctx, &self.vdisks[i]) 563 if err != nil { 564 return errors.Wrap(err, "doDetachAndDeteteDisk") 565 } 566 } 567 568 return self.doDestroy(ctx) 569 } 570 571 func (self *SVirtualMachine) doUnregister(ctx context.Context) error { 572 vm := self.getVmObj() 573 574 err := vm.Unregister(ctx) 575 if err != nil { 576 log.Errorf("vm.Unregister(ctx) fail %s", err) 577 return err 578 } 579 return nil 580 } 581 582 func (self *SVirtualMachine) DeleteVM(ctx context.Context) error { 583 err := self.CheckFileInfo(ctx) 584 if err != nil { 585 return self.doUnregister(ctx) 586 } 587 return self.doDestroy(ctx) 588 } 589 590 func (self *SVirtualMachine) doDetachAndDeleteDisk(ctx context.Context, vdisk *SVirtualDisk) error { 591 return self.doDetachDisk(ctx, vdisk, true) 592 } 593 594 func (self *SVirtualMachine) doDetachDisk(ctx context.Context, vdisk *SVirtualDisk, remove bool) error { 595 removeSpec := types.VirtualDeviceConfigSpec{} 596 removeSpec.Operation = types.VirtualDeviceConfigSpecOperationRemove 597 removeSpec.Device = vdisk.dev 598 599 spec := types.VirtualMachineConfigSpec{} 600 spec.DeviceChange = []types.BaseVirtualDeviceConfigSpec{&removeSpec} 601 602 vm := self.getVmObj() 603 604 task, err := vm.Reconfigure(ctx, spec) 605 if err != nil { 606 log.Errorf("vm.Reconfigure fail %s", err) 607 return err 608 } 609 610 err = task.Wait(ctx) 611 if err != nil { 612 log.Errorf("task.Wait(ctx) fail %s", err) 613 return err 614 } 615 616 if !remove { 617 return nil 618 } 619 return vdisk.Delete(ctx) 620 } 621 622 func (self *SVirtualMachine) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) { 623 hostVer := self.GetIHost().GetVersion() 624 if version.GE(hostVer, "6.5") { 625 info, err := self.acquireWebmksTicket("webmks") 626 if err == nil { 627 return info, nil 628 } 629 } 630 return self.acquireVmrcUrl() 631 } 632 633 func (self *SVirtualMachine) GetVmrcInfo() (*cloudprovider.ServerVncOutput, error) { 634 return self.acquireVmrcUrl() 635 } 636 637 func (self *SVirtualMachine) GetWebmksInfo() (*cloudprovider.ServerVncOutput, error) { 638 return self.acquireWebmksTicket("webmks") 639 } 640 641 func (self *SVirtualMachine) acquireWebmksTicket(ticketType string) (*cloudprovider.ServerVncOutput, error) { 642 vm := object.NewVirtualMachine(self.manager.client.Client, self.getVirtualMachine().Self) 643 ticket, err := vm.AcquireTicket(self.manager.context, ticketType) 644 if err != nil { 645 return nil, err 646 } 647 648 host := ticket.Host 649 if len(host) == 0 { 650 host = self.manager.host 651 } 652 port := ticket.Port 653 if port == 0 { 654 port = int32(self.manager.port) 655 } 656 if port == 0 { 657 port = 443 658 } 659 ret := &cloudprovider.ServerVncOutput{ 660 Url: fmt.Sprintf("wss://%s:%d/ticket/%s", host, port, ticket.Ticket), 661 Protocol: "wmks", 662 Hypervisor: api.HYPERVISOR_ESXI, 663 } 664 return ret, nil 665 } 666 667 func (self *SVirtualMachine) acquireVmrcUrl() (*cloudprovider.ServerVncOutput, error) { 668 ticket, err := self.manager.acquireCloneTicket() 669 if err != nil { 670 return nil, err 671 } 672 port := self.manager.port 673 if port == 0 { 674 port = 443 675 } 676 ret := &cloudprovider.ServerVncOutput{ 677 Url: fmt.Sprintf("vmrc://clone:%s@%s:%d/?moid=%s", ticket, self.manager.host, port, self.GetId()), 678 Protocol: "vmrc", 679 } 680 return ret, nil 681 } 682 683 func (self *SVirtualMachine) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error { 684 return self.doChangeConfig(ctx, int32(config.Cpu), int64(config.MemoryMB), "", "") 685 } 686 687 func (self *SVirtualMachine) GetVersion() string { 688 return self.getVirtualMachine().Config.Version 689 } 690 691 func (self *SVirtualMachine) doChangeConfig(ctx context.Context, ncpu int32, vmemMB int64, guestId string, version string) error { 692 changed := false 693 configSpec := types.VirtualMachineConfigSpec{} 694 if int(ncpu) != self.GetVcpuCount() { 695 configSpec.NumCPUs = ncpu 696 changed = true 697 } 698 if int(vmemMB) != self.GetVmemSizeMB() { 699 configSpec.MemoryMB = vmemMB 700 changed = true 701 } 702 if len(guestId) > 0 && guestId != self.GetGuestId() { 703 configSpec.GuestId = guestId 704 changed = true 705 } 706 if len(version) > 0 && version != self.GetVersion() { 707 configSpec.Version = version 708 changed = true 709 } 710 if !changed { 711 return nil 712 } 713 714 vm := self.getVmObj() 715 716 task, err := vm.Reconfigure(ctx, configSpec) 717 if err != nil { 718 return err 719 } 720 err = task.Wait(ctx) 721 if err != nil { 722 return err 723 } 724 return self.Refresh() 725 } 726 727 func (self *SVirtualMachine) AssignSecurityGroup(secgroupId string) error { 728 return cloudprovider.ErrNotImplemented 729 } 730 731 func (self *SVirtualMachine) SetSecurityGroups(secgroupIds []string) error { 732 return cloudprovider.ErrNotImplemented 733 } 734 735 func (self *SVirtualMachine) GetBillingType() string { 736 return billing_api.BILLING_TYPE_POSTPAID 737 } 738 739 func (self *SVirtualMachine) GetCreatedAt() time.Time { 740 moVM := self.getVirtualMachine() 741 ctm := moVM.Config.CreateDate 742 if ctm != nil { 743 return *ctm 744 } else { 745 return time.Time{} 746 } 747 } 748 749 func (self *SVirtualMachine) GetExpiredAt() time.Time { 750 return time.Time{} 751 } 752 753 func (self *SVirtualMachine) UpdateUserData(userData string) error { 754 return nil 755 } 756 757 func (self *SVirtualMachine) fetchHardwareInfo() error { 758 self.vnics = make([]SVirtualNIC, 0) 759 self.vdisks = make([]SVirtualDisk, 0) 760 self.cdroms = make([]SVirtualCdrom, 0) 761 self.devs = make(map[int32]SVirtualDevice) 762 763 moVM := self.getVirtualMachine() 764 765 // MAX_TRIES := 3 766 // for tried := 0; tried < MAX_TRIES && (moVM == nil || moVM.Config == nil || moVM.Config.Hardware.Device == nil); tried += 1 { 767 // time.Sleep(time.Second) 768 // } 769 770 if moVM == nil || moVM.Config == nil || moVM.Config.Hardware.Device == nil { 771 return fmt.Errorf("invalid vm") 772 } 773 774 // sort devices via their Key 775 devices := moVM.Config.Hardware.Device 776 sort.Slice(devices, func(i, j int) bool { 777 return devices[i].GetVirtualDevice().Key < devices[j].GetVirtualDevice().Key 778 }) 779 for i := 0; i < len(devices); i += 1 { 780 dev := devices[i] 781 devType := reflect.Indirect(reflect.ValueOf(dev)).Type() 782 783 etherType := reflect.TypeOf((*types.VirtualEthernetCard)(nil)).Elem() 784 diskType := reflect.TypeOf((*types.VirtualDisk)(nil)).Elem() 785 vgaType := reflect.TypeOf((*types.VirtualMachineVideoCard)(nil)).Elem() 786 cdromType := reflect.TypeOf((*types.VirtualCdrom)(nil)).Elem() 787 788 if reflectutils.StructContains(devType, etherType) { 789 self.vnics = append(self.vnics, NewVirtualNIC(self, dev, len(self.vnics))) 790 } else if reflectutils.StructContains(devType, diskType) { 791 self.vdisks = append(self.vdisks, NewVirtualDisk(self, dev, len(self.vdisks))) 792 } else if reflectutils.StructContains(devType, vgaType) { 793 self.vga = NewVirtualVGA(self, dev, 0) 794 } else if reflectutils.StructContains(devType, cdromType) { 795 self.cdroms = append(self.cdroms, NewVirtualCdrom(self, dev, len(self.cdroms))) 796 } 797 vdev := NewVirtualDevice(self, dev, 0) 798 self.devs[vdev.getKey()] = vdev 799 } 800 self.rigorous() 801 sort.Sort(byDiskType(self.vdisks)) 802 return nil 803 } 804 805 func (self *SVirtualMachine) rigorous() { 806 hasRoot := false 807 for i := range self.vdisks { 808 if self.vdisks[i].IsRoot { 809 hasRoot = true 810 break 811 } 812 } 813 if !hasRoot && len(self.vdisks) > 0 { 814 self.vdisks[0].IsRoot = true 815 } 816 } 817 818 func (self *SVirtualMachine) getVdev(key int32) SVirtualDevice { 819 return self.devs[key] 820 } 821 822 func (self *SVirtualMachine) fetchGuestIps() map[string]string { 823 guestIps := make(map[string]string) 824 moVM := self.getVirtualMachine() 825 for _, net := range moVM.Guest.Net { 826 mac := netutils.FormatMacAddr(net.MacAddress) 827 for _, ip := range net.IpAddress { 828 if regutils.MatchIP4Addr(ip) { 829 if !vmIPV4Filter.Contains(ip) { 830 continue 831 } 832 guestIps[mac] = ip 833 break 834 } 835 } 836 } 837 return guestIps 838 } 839 840 func (self *SVirtualMachine) getGuestIps() map[string]string { 841 if self.guestIps == nil { 842 self.guestIps = self.fetchGuestIps() 843 } 844 return self.guestIps 845 } 846 847 func (self *SVirtualMachine) GetIps() []string { 848 ips := make([]string, 0) 849 for _, ip := range self.getGuestIps() { 850 ips = append(ips, ip) 851 } 852 return ips 853 } 854 855 func (self *SVirtualMachine) GetVGADevice() string { 856 return fmt.Sprintf("%s", self.vga.String()) 857 } 858 859 var ( 860 driverTable = map[string][]string{ 861 "sata": {"ahci"}, 862 "scsi": {"parascsi", "lsilogic", "lsilogicsas", "buslogic"}, 863 "pvscsi": {"parascsi"}, 864 "ide": {"ide"}, 865 } 866 ) 867 868 func (self *SVirtualMachine) getDevsByDriver(driver string) []SVirtualDevice { 869 devs := make([]SVirtualDevice, 0) 870 for _, drv := range self.devs { 871 if strings.HasSuffix(drv.GetDriver(), fmt.Sprintf("%scontroller", driver)) { 872 devs = append(devs, drv) 873 } 874 } 875 return devs 876 } 877 878 func minDevKey(devs []SVirtualDevice) int32 { 879 var minKey int32 = -1 880 for i := 0; i < len(devs); i += 1 { 881 if minKey < 0 || minKey > devs[i].getKey() { 882 minKey = devs[i].getKey() 883 } 884 } 885 return minKey 886 } 887 888 func minDiskKey(devs []SVirtualDisk) int32 { 889 var minKey int32 = -1 890 for i := 0; i < len(devs); i += 1 { 891 if minKey < 0 || minKey > devs[i].getKey() { 892 minKey = devs[i].getKey() 893 } 894 } 895 return minKey 896 } 897 898 func (self *SVirtualMachine) FindController(ctx context.Context, driver string) ([]SVirtualDevice, error) { 899 aliasDrivers, ok := driverTable[driver] 900 if !ok { 901 return nil, fmt.Errorf("Unsupported disk driver %s", driver) 902 } 903 var devs []SVirtualDevice 904 for _, alias := range aliasDrivers { 905 devs = self.getDevsByDriver(alias) 906 if len(devs) > 0 { 907 break 908 } 909 } 910 return devs, nil 911 } 912 913 func (self *SVirtualMachine) FindDiskByDriver(drivers ...string) []SVirtualDisk { 914 disks := make([]SVirtualDisk, 0) 915 for i := range self.vdisks { 916 if utils.IsInStringArray(self.vdisks[i].GetDriver(), drivers) { 917 disks = append(disks, self.vdisks[i]) 918 } 919 } 920 return disks 921 } 922 923 func (self *SVirtualMachine) devNumWithCtrlKey(ctrlKey int32) int { 924 n := 0 925 for _, dev := range self.devs { 926 if dev.getControllerKey() == ctrlKey { 927 n++ 928 } 929 } 930 return n 931 } 932 933 func (self *SVirtualMachine) getLayoutEx() *types.VirtualMachineFileLayoutEx { 934 vm := self.getVirtualMachine() 935 if vm.LayoutEx != nil { 936 return vm.LayoutEx 937 } 938 var nvm mo.VirtualMachine 939 err := self.manager.reference2Object(vm.Self, vmLayoutExProps, &nvm) 940 if err != nil { 941 log.Errorf("unable to fetch LayoutEx.File from vc: %v", err) 942 } 943 vm.LayoutEx = nvm.LayoutEx 944 return vm.LayoutEx 945 } 946 947 func (self *SVirtualMachine) CreateDisk(ctx context.Context, opts *cloudprovider.GuestDiskCreateOptions) (string, error) { 948 if opts.Driver == "pvscsi" { 949 opts.Driver = "scsi" 950 } 951 var ds *SDatastore 952 var err error 953 if opts.StorageId != "" { 954 ihost := self.getIHost() 955 if ihost == nil { 956 return "", fmt.Errorf("unable to get host of virtualmachine %s", self.GetName()) 957 } 958 ds, err = ihost.(*SHost).FindDataStoreById(opts.StorageId) 959 if err != nil { 960 return "", errors.Wrapf(err, "unable to find datastore %s", opts.StorageId) 961 } 962 } 963 devs, err := self.FindController(ctx, opts.Driver) 964 if err != nil { 965 return "", err 966 } 967 if len(devs) == 0 { 968 return "", self.createDriverAndDisk(ctx, ds, opts.SizeMb, opts.UUID, opts.Driver) 969 } 970 numDevBelowCtrl := make([]int, len(devs)) 971 for i := range numDevBelowCtrl { 972 numDevBelowCtrl[i] = self.devNumWithCtrlKey(devs[i].getKey()) 973 } 974 975 // find the min one 976 ctrlKey := devs[0].getKey() 977 unitNumber := numDevBelowCtrl[0] 978 for i := 1; i < len(numDevBelowCtrl); i++ { 979 if numDevBelowCtrl[i] >= unitNumber { 980 continue 981 } 982 ctrlKey = devs[i].getKey() 983 unitNumber = numDevBelowCtrl[i] 984 } 985 diskKey := self.FindMinDiffKey(2000) 986 987 // By default, the virtual SCSI controller is assigned to virtual device node (z:7), 988 // so that device node is unavailable for hard disks or other devices. 989 if unitNumber >= 7 && opts.Driver == "scsi" { 990 unitNumber++ 991 } 992 993 return "", self.createDiskInternal(ctx, SDiskConfig{ 994 SizeMb: int64(opts.SizeMb), 995 Uuid: opts.UUID, 996 UnitNumber: int32(unitNumber), 997 ControllerKey: ctrlKey, 998 Key: diskKey, 999 Datastore: ds, 1000 }, true) 1001 } 1002 1003 // createDriverAndDisk will create a driver and disk associated with the driver 1004 func (self *SVirtualMachine) createDriverAndDisk(ctx context.Context, ds *SDatastore, sizeMb int, uuid string, driver string) error { 1005 if driver != "scsi" && driver != "pvscsi" { 1006 return fmt.Errorf("Driver %s is not supported", driver) 1007 } 1008 1009 deviceChange := make([]types.BaseVirtualDeviceConfigSpec, 0, 2) 1010 1011 // find a suitable key for scsi or pvscsi driver 1012 scsiKey := self.FindMinDiffKey(1000) 1013 deviceChange = append(deviceChange, addDevSpec(NewSCSIDev(scsiKey, 100, driver))) 1014 1015 // find a suitable key for disk 1016 diskKey := self.FindMinDiffKey(2000) 1017 1018 if diskKey == scsiKey { 1019 // unarrivelable 1020 log.Errorf("there is no suitable key between 1000 and 2000???!") 1021 } 1022 1023 return self.createDiskWithDeviceChange(ctx, deviceChange, 1024 SDiskConfig{ 1025 SizeMb: int64(sizeMb), 1026 Uuid: uuid, 1027 ControllerKey: scsiKey, 1028 UnitNumber: 0, 1029 Key: scsiKey, 1030 ImagePath: "", 1031 IsRoot: false, 1032 Datastore: ds, 1033 }, true) 1034 } 1035 1036 func (self *SVirtualMachine) copyRootDisk(ctx context.Context, imagePath string) (string, error) { 1037 layoutEx := self.getLayoutEx() 1038 if layoutEx == nil || len(layoutEx.File) == 0 { 1039 return "", fmt.Errorf("invalid LayoutEx") 1040 } 1041 file := layoutEx.File[0].Name 1042 // find stroage 1043 host := self.GetIHost() 1044 storages, err := host.GetIStorages() 1045 if err != nil { 1046 return "", errors.Wrap(err, "host.GetIStorages") 1047 } 1048 var datastore *SDatastore 1049 for i := range storages { 1050 ds := storages[i].(*SDatastore) 1051 if ds.HasFile(file) { 1052 datastore = ds 1053 break 1054 } 1055 } 1056 if datastore == nil { 1057 return "", fmt.Errorf("can't find storage associated with vm %q", self.GetName()) 1058 } 1059 path := datastore.cleanPath(file) 1060 vmDir := strings.Split(path, "/")[0] 1061 // TODO find a non-conflicting path 1062 newImagePath := datastore.getPathString(fmt.Sprintf("%s/%s.vmdk", vmDir, vmDir)) 1063 1064 fm := datastore.getDatastoreObj().NewFileManager(datastore.datacenter.getObjectDatacenter(), true) 1065 err = fm.Copy(ctx, imagePath, newImagePath) 1066 if err != nil { 1067 return "", errors.Wrap(err, "unable to copy system disk") 1068 } 1069 return newImagePath, nil 1070 } 1071 1072 func (self *SVirtualMachine) createDiskWithDeviceChange(ctx context.Context, deviceChange []types.BaseVirtualDeviceConfigSpec, config SDiskConfig, check bool) error { 1073 var err error 1074 // copy disk 1075 if len(config.ImagePath) > 0 { 1076 config.IsRoot = true 1077 config.ImagePath, err = self.copyRootDisk(ctx, config.ImagePath) 1078 if err != nil { 1079 return errors.Wrap(err, "unable to copyRootDisk") 1080 } 1081 } 1082 1083 devSpec := NewDiskDev(int64(config.SizeMb), config) 1084 spec := addDevSpec(devSpec) 1085 if len(config.ImagePath) == 0 { 1086 spec.FileOperation = types.VirtualDeviceConfigSpecFileOperationCreate 1087 } 1088 configSpec := types.VirtualMachineConfigSpec{} 1089 configSpec.DeviceChange = append(deviceChange, spec) 1090 1091 vmObj := self.getVmObj() 1092 1093 task, err := vmObj.Reconfigure(ctx, configSpec) 1094 if err != nil { 1095 return err 1096 } 1097 err = task.Wait(ctx) 1098 if err != nil { 1099 return err 1100 } 1101 if !check { 1102 return nil 1103 } 1104 oldDiskCnt := len(self.vdisks) 1105 maxTries := 60 1106 for tried := 0; tried < maxTries; tried += 1 { 1107 time.Sleep(time.Second) 1108 self.Refresh() 1109 if len(self.vdisks) > oldDiskCnt { 1110 return nil 1111 } 1112 } 1113 return cloudprovider.ErrTimeout 1114 } 1115 1116 func (self *SVirtualMachine) createDiskInternal(ctx context.Context, config SDiskConfig, check bool) error { 1117 1118 return self.createDiskWithDeviceChange(ctx, nil, config, check) 1119 } 1120 1121 func (self *SVirtualMachine) Renew(bc billing.SBillingCycle) error { 1122 return cloudprovider.ErrNotSupported 1123 } 1124 1125 func (self *SVirtualMachine) GetProjectId() string { 1126 pool, err := self.getResourcePool() 1127 if err != nil { 1128 return "" 1129 } 1130 if pool != nil { 1131 return pool.GetId() 1132 } 1133 return "" 1134 } 1135 1136 func (self *SVirtualMachine) GetError() error { 1137 return nil 1138 } 1139 1140 func (self *SVirtualMachine) getResourcePool() (*SResourcePool, error) { 1141 vm := self.getVirtualMachine() 1142 morp := mo.ResourcePool{} 1143 if vm.ResourcePool == nil { 1144 return nil, errors.Error("nil resource pool") 1145 } 1146 err := self.manager.reference2Object(*vm.ResourcePool, RESOURCEPOOL_PROPS, &morp) 1147 if err != nil { 1148 return nil, errors.Wrap(err, "self.manager.reference2Object") 1149 } 1150 rp := NewResourcePool(self.manager, &morp, self.datacenter) 1151 return rp, nil 1152 } 1153 1154 func (self *SVirtualMachine) CheckFileInfo(ctx context.Context) error { 1155 layoutEx := self.getLayoutEx() 1156 if layoutEx != nil && len(layoutEx.File) > 0 { 1157 file := layoutEx.File[0] 1158 host := self.GetIHost() 1159 storages, err := host.GetIStorages() 1160 if err != nil { 1161 return errors.Wrap(err, "host.GetIStorages") 1162 } 1163 for i := range storages { 1164 ds := storages[i].(*SDatastore) 1165 if ds.HasFile(file.Name) { 1166 _, err := ds.CheckFile(ctx, file.Name) 1167 if err != nil { 1168 return errors.Wrap(err, "ds.CheckFile") 1169 } 1170 break 1171 } 1172 } 1173 } 1174 return nil 1175 } 1176 1177 func (self *SVirtualMachine) DoRename(ctx context.Context, name string) error { 1178 task, err := self.getVmObj().Rename(ctx, name) 1179 if err != nil { 1180 return errors.Wrap(err, "object.VirtualMachine.Rename") 1181 } 1182 return task.Wait(ctx) 1183 } 1184 1185 func (self *SVirtualMachine) GetMoid() string { 1186 return self.getVirtualMachine().Self.Value 1187 } 1188 1189 func (self *SVirtualMachine) GetToolsVersion() string { 1190 return self.getVirtualMachine().Guest.ToolsVersion 1191 } 1192 1193 func (self *SVirtualMachine) DoCustomize(ctx context.Context, params jsonutils.JSONObject) error { 1194 spec := new(types.CustomizationSpec) 1195 1196 ipSettings := new(types.CustomizationGlobalIPSettings) 1197 domain := "local" 1198 if params.Contains("domain") { 1199 domain, _ = params.GetString("domain") 1200 } 1201 ipSettings.DnsSuffixList = []string{domain} 1202 1203 // deal nics 1204 nics, _ := params.GetArray("nics") 1205 serverNics := make([]cloudtypes.SServerNic, len(nics)) 1206 for i := range nics { 1207 var nicType cloudtypes.SServerNic 1208 nics[i].Unmarshal(&nicType) 1209 serverNics[i] = nicType 1210 } 1211 1212 // find dnsServerList 1213 for i := range serverNics { 1214 dnsList := netutils2.GetNicDns(&serverNics[i]) 1215 if len(dnsList) != 0 { 1216 ipSettings.DnsServerList = dnsList 1217 } 1218 } 1219 spec.GlobalIPSettings = *ipSettings 1220 1221 maps := make([]types.CustomizationAdapterMapping, 0, len(nics)) 1222 for _, nic := range serverNics { 1223 conf := types.CustomizationAdapterMapping{} 1224 conf.MacAddress = nic.Mac 1225 if len(conf.MacAddress) == 0 { 1226 conf.MacAddress = "9e:46:27:21:a2:b2" 1227 } 1228 1229 conf.Adapter = types.CustomizationIPSettings{} 1230 fixedIp := new(types.CustomizationFixedIp) 1231 fixedIp.IpAddress = nic.Ip 1232 if len(fixedIp.IpAddress) == 0 { 1233 fixedIp.IpAddress = "10.168.26.23" 1234 } 1235 conf.Adapter.Ip = fixedIp 1236 maskLen := nic.Masklen 1237 if maskLen == 0 { 1238 maskLen = 24 1239 } 1240 mask := netutils2.Netlen2Mask(maskLen) 1241 conf.Adapter.SubnetMask = mask 1242 1243 if len(nic.Gateway) != 0 { 1244 conf.Adapter.Gateway = []string{nic.Gateway} 1245 } 1246 dnsList := netutils2.GetNicDns(&nic) 1247 if len(dnsList) != 0 { 1248 conf.Adapter.DnsServerList = dnsList 1249 dns := nic.Domain 1250 if len(dns) == 0 { 1251 dns = "local" 1252 } 1253 conf.Adapter.DnsDomain = dns 1254 } 1255 maps = append(maps, conf) 1256 } 1257 spec.NicSettingMap = maps 1258 1259 var ( 1260 osName string 1261 name = "yunionhost" 1262 ) 1263 if params.Contains("os_name") { 1264 osName, _ = params.GetString("os_name") 1265 } 1266 if params.Contains("name") { 1267 name, _ = params.GetString("name") 1268 } 1269 if osName == "Linux" { 1270 linuxPrep := types.CustomizationLinuxPrep{ 1271 HostName: &types.CustomizationFixedName{Name: name}, 1272 Domain: domain, 1273 TimeZone: "Asia/Shanghai", 1274 } 1275 spec.Identity = &linuxPrep 1276 } else if osName == "Windows" { 1277 sysPrep := types.CustomizationSysprep{ 1278 GuiUnattended: types.CustomizationGuiUnattended{ 1279 TimeZone: 210, 1280 AutoLogon: false, 1281 }, 1282 UserData: types.CustomizationUserData{ 1283 FullName: "Administrator", 1284 OrgName: "Yunion", 1285 ProductId: "", 1286 ComputerName: &types.CustomizationFixedName{ 1287 Name: name, 1288 }, 1289 }, 1290 Identification: types.CustomizationIdentification{}, 1291 } 1292 spec.Identity = &sysPrep 1293 } 1294 log.Infof("customize spec: %#v", spec) 1295 task, err := self.getVmObj().Customize(ctx, *spec) 1296 if err != nil { 1297 return errors.Wrap(err, "object.VirtualMachine.Customize") 1298 } 1299 return task.Wait(ctx) 1300 } 1301 1302 func (self *SVirtualMachine) ExportTemplate(ctx context.Context, idx int, diskPath string) error { 1303 lease, err := self.getVmObj().Export(ctx) 1304 if err != nil { 1305 return errors.Wrap(err, "esxi.SVirtualMachine.DoExportTemplate") 1306 } 1307 info, err := lease.Wait(ctx, nil) 1308 if err != nil { 1309 return errors.Wrap(err, "lease.Wait") 1310 } 1311 1312 u := lease.StartUpdater(ctx, info) 1313 defer u.Done() 1314 1315 if idx >= len(info.Items) { 1316 return errors.Error(fmt.Sprintf("No such Device whose index is %d", idx)) 1317 } 1318 1319 lr := newLeaseLogger("download vmdk", 5) 1320 lr.Log() 1321 defer lr.End() 1322 1323 // filter vmdk item 1324 vmdkItems := make([]nfc.FileItem, 0, len(info.Items)/2) 1325 for i := range info.Items { 1326 if strings.HasSuffix(info.Items[i].Path, ".vmdk") { 1327 vmdkItems = append(vmdkItems, info.Items[i]) 1328 } else { 1329 log.Infof("item.Path does not end in '.vmdk': %#v", info.Items[i]) 1330 } 1331 } 1332 1333 log.Debugf("download to %s start...", diskPath) 1334 err = lease.DownloadFile(ctx, diskPath, vmdkItems[idx], soap.Download{Progress: lr}) 1335 if err != nil { 1336 return errors.Wrap(err, "lease.DownloadFile") 1337 } 1338 1339 err = lease.Complete(ctx) 1340 if err != nil { 1341 return errors.Wrap(err, "lease.Complete") 1342 } 1343 log.Debugf("download to %s finish", diskPath) 1344 return nil 1345 } 1346 1347 func (self *SVirtualMachine) GetSerialOutput(port int) (string, error) { 1348 return "", cloudprovider.ErrNotImplemented 1349 } 1350 1351 func (self *SVirtualMachine) ConvertPublicIpToEip() error { 1352 return cloudprovider.ErrNotSupported 1353 } 1354 1355 func (self *SVirtualMachine) IsAutoRenew() bool { 1356 return false 1357 } 1358 1359 func (self *SVirtualMachine) SetAutoRenew(bc billing.SBillingCycle) error { 1360 return cloudprovider.ErrNotSupported 1361 } 1362 1363 func (self *SVirtualMachine) FindMinDiffKey(limit int32) int32 { 1364 if self.devs == nil { 1365 self.fetchHardwareInfo() 1366 } 1367 devKeys := make([]int32, 0, len(self.devs)) 1368 for key := range self.devs { 1369 devKeys = append(devKeys, key) 1370 } 1371 sort.Slice(devKeys, func(i int, j int) bool { 1372 return devKeys[i] < devKeys[j] 1373 }) 1374 for _, key := range devKeys { 1375 switch { 1376 case key < limit: 1377 case key == limit: 1378 limit += 1 1379 case key > limit: 1380 return limit 1381 } 1382 } 1383 return limit 1384 } 1385 1386 func (self *SVirtualMachine) relocate(hostId string) error { 1387 var targetHs *mo.HostSystem 1388 if hostId == "" { 1389 return errors.Wrap(fmt.Errorf("require hostId"), "relocate") 1390 } 1391 ihost, err := self.manager.GetIHostById(hostId) 1392 if err != nil { 1393 return errors.Wrap(err, "self.manager.GetIHostById(hostId)") 1394 } 1395 targetHs = ihost.(*SHost).object.(*mo.HostSystem) 1396 if len(targetHs.Datastore) < 1 { 1397 return errors.Wrap(fmt.Errorf("target host has no datastore"), "relocate") 1398 } 1399 ctx := self.manager.context 1400 config := types.VirtualMachineRelocateSpec{} 1401 hrs := targetHs.Reference() 1402 config.Host = &hrs 1403 config.Datastore = &targetHs.Datastore[0] 1404 task, err := self.getVmObj().Relocate(ctx, config, types.VirtualMachineMovePriorityDefaultPriority) 1405 if err != nil { 1406 log.Errorf("vm.Migrate %s", err) 1407 return errors.Wrap(err, "SVirtualMachine Migrate") 1408 } 1409 err = task.Wait(ctx) 1410 if err != nil { 1411 log.Errorf("task.Wait %s", err) 1412 return errors.Wrap(err, "task.wait") 1413 } 1414 return nil 1415 } 1416 1417 func (self *SVirtualMachine) MigrateVM(hostId string) error { 1418 return self.relocate(hostId) 1419 } 1420 1421 func (self *SVirtualMachine) LiveMigrateVM(hostId string) error { 1422 return self.relocate(hostId) 1423 } 1424 1425 func (self *SVirtualMachine) GetIHostId() string { 1426 ctx := self.manager.context 1427 hs, err := self.getVmObj().HostSystem(ctx) 1428 if err != nil { 1429 log.Errorf("get HostSystem %s", err) 1430 return "" 1431 } 1432 var moHost mo.HostSystem 1433 err = self.manager.reference2Object(hs.Reference(), HOST_SYSTEM_PROPS, &moHost) 1434 if err != nil { 1435 log.Errorf("hostsystem reference2Object %s", err) 1436 return "" 1437 } 1438 shost := NewHost(self.manager, &moHost, nil) 1439 return shost.GetGlobalId() 1440 } 1441 1442 func (self *SVirtualMachine) IsTemplate() bool { 1443 movm := self.getVirtualMachine() 1444 if tempalteNameRegex != nil && tempalteNameRegex.MatchString(self.GetName()) && movm.Summary.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOff { 1445 return true 1446 } 1447 return movm.Config != nil && movm.Config.Template 1448 } 1449 1450 func (self *SVirtualMachine) fetchSnapshots() { 1451 movm := self.getVirtualMachine() 1452 if movm.Snapshot == nil { 1453 return 1454 } 1455 self.snapshots = self.extractSnapshots(movm.Snapshot.RootSnapshotList, make([]SVirtualMachineSnapshot, 0, len(movm.Snapshot.RootSnapshotList))) 1456 } 1457 1458 func (self *SVirtualMachine) extractSnapshots(tree []types.VirtualMachineSnapshotTree, snapshots []SVirtualMachineSnapshot) []SVirtualMachineSnapshot { 1459 for i := range tree { 1460 snapshots = append(snapshots, SVirtualMachineSnapshot{ 1461 snapshotTree: tree[i], 1462 vm: self, 1463 }) 1464 snapshots = self.extractSnapshots(tree[i].ChildSnapshotList, snapshots) 1465 } 1466 return snapshots 1467 } 1468 1469 func (self *SVirtualMachine) GetInstanceSnapshots() ([]cloudprovider.ICloudInstanceSnapshot, error) { 1470 if self.snapshots == nil { 1471 self.fetchSnapshots() 1472 } 1473 ret := make([]cloudprovider.ICloudInstanceSnapshot, 0, len(self.snapshots)) 1474 for i := range self.snapshots { 1475 ret = append(ret, &self.snapshots[i]) 1476 } 1477 return ret, nil 1478 } 1479 1480 func (self *SVirtualMachine) GetInstanceSnapshot(idStr string) (cloudprovider.ICloudInstanceSnapshot, error) { 1481 if self.snapshots == nil { 1482 self.fetchSnapshots() 1483 } 1484 for i := range self.snapshots { 1485 if self.snapshots[i].GetGlobalId() == idStr { 1486 // copyone 1487 sp := self.snapshots[i] 1488 return &sp, nil 1489 } 1490 } 1491 return nil, errors.ErrNotFound 1492 } 1493 1494 func (self *SVirtualMachine) CreateInstanceSnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudInstanceSnapshot, error) { 1495 ovm := self.getVmObj() 1496 task, err := ovm.CreateSnapshot(ctx, name, desc, false, false) 1497 if err != nil { 1498 return nil, errors.Wrap(err, "CreateSnapshot") 1499 } 1500 info, err := task.WaitForResult(ctx, nil) 1501 if err != nil { 1502 return nil, errors.Wrap(err, "task.Wait") 1503 } 1504 sp := info.Result.(types.ManagedObjectReference) 1505 err = self.Refresh() 1506 if err != nil { 1507 return nil, errors.Wrap(err, "create successfully") 1508 } 1509 self.fetchSnapshots() 1510 for i := range self.snapshots { 1511 if self.snapshots[i].snapshotTree.Snapshot == sp { 1512 // copyone 1513 sp := self.snapshots[i] 1514 return &sp, nil 1515 } 1516 } 1517 return nil, errors.Wrap(errors.ErrNotFound, "create successfully") 1518 } 1519 1520 func (self *SVirtualMachine) ResetToInstanceSnapshot(ctx context.Context, idStr string) error { 1521 cloudIsp, err := self.GetInstanceSnapshot(idStr) 1522 if err != nil { 1523 return errors.Wrap(err, "GetInstanceSnapshot") 1524 } 1525 isp := cloudIsp.(*SVirtualMachineSnapshot) 1526 req := types.RevertToSnapshot_Task{ 1527 This: isp.snapshotTree.Snapshot.Reference(), 1528 } 1529 res, err := methods.RevertToSnapshot_Task(ctx, self.manager.client.Client, &req) 1530 if err != nil { 1531 return errors.Wrap(err, "RevertToSnapshot_Task") 1532 } 1533 return object.NewTask(self.manager.client.Client, res.Returnval).Wait(ctx) 1534 }