yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/esxi/manager.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 "bytes" 19 "context" 20 "fmt" 21 "net/http" 22 "net/http/httputil" 23 "net/url" 24 "reflect" 25 "strings" 26 "sync" 27 28 xj "github.com/basgys/goxml2json" 29 "github.com/fatih/color" 30 "github.com/vmware/govmomi" 31 "github.com/vmware/govmomi/object" 32 "github.com/vmware/govmomi/property" 33 "github.com/vmware/govmomi/session" 34 "github.com/vmware/govmomi/view" 35 "github.com/vmware/govmomi/vim25" 36 "github.com/vmware/govmomi/vim25/methods" 37 "github.com/vmware/govmomi/vim25/mo" 38 "github.com/vmware/govmomi/vim25/soap" 39 "github.com/vmware/govmomi/vim25/types" 40 "moul.io/http2curl/v2" 41 42 "yunion.io/x/jsonutils" 43 "yunion.io/x/log" 44 "yunion.io/x/pkg/errors" 45 "yunion.io/x/pkg/utils" 46 47 api "yunion.io/x/cloudmux/pkg/apis/compute" 48 "yunion.io/x/cloudmux/pkg/cloudprovider" 49 "yunion.io/x/cloudmux/pkg/multicloud" 50 "yunion.io/x/cloudmux/pkg/multicloud/esxi/vcenter" 51 "yunion.io/x/onecloud/pkg/util/httputils" 52 ) 53 54 const ( 55 CLOUD_PROVIDER_VMWARE = api.CLOUD_PROVIDER_VMWARE 56 ) 57 58 var ( 59 defaultDc mo.Datacenter 60 61 defaultDcId = "esxi-default-datacenter" 62 ) 63 64 func init() { 65 defaultDc.ManagedEntity.Name = "Datacenter" 66 defaultDc.ManagedEntity.ExtensibleManagedObject.Self.Type = "Datacenter" 67 defaultDc.ManagedEntity.ExtensibleManagedObject.Self.Value = defaultDcId 68 } 69 70 type ESXiClientConfig struct { 71 cpcfg cloudprovider.ProviderConfig 72 73 host string 74 port int 75 account string 76 password string 77 78 managed bool 79 debug bool 80 format string 81 } 82 83 func NewESXiClientConfig(host string, port int, account, password string) *ESXiClientConfig { 84 cfg := &ESXiClientConfig{ 85 host: host, 86 port: port, 87 account: account, 88 password: password, 89 } 90 return cfg 91 } 92 93 func (cfg *ESXiClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *ESXiClientConfig { 94 cfg.cpcfg = cpcfg 95 return cfg 96 } 97 98 func (cfg *ESXiClientConfig) Debug(debug bool) *ESXiClientConfig { 99 cfg.debug = debug 100 return cfg 101 } 102 103 func (cfg *ESXiClientConfig) Format(format string) *ESXiClientConfig { 104 cfg.format = format 105 return cfg 106 } 107 108 func (cfg *ESXiClientConfig) Managed(managed bool) *ESXiClientConfig { 109 cfg.managed = managed 110 return cfg 111 } 112 113 type SESXiClient struct { 114 *ESXiClientConfig 115 116 cloudprovider.SFakeOnPremiseRegion 117 multicloud.SRegion 118 multicloud.SNoObjectStorageRegion 119 120 client *govmomi.Client 121 context context.Context 122 123 datacenters []*SDatacenter 124 networkQueryMap *sync.Map 125 } 126 127 func NewESXiClient(cfg *ESXiClientConfig) (*SESXiClient, error) { 128 cfg.Managed(true) 129 return NewESXiClient2(cfg) 130 } 131 132 func NewESXiClient2(cfg *ESXiClientConfig) (*SESXiClient, error) { 133 cli := &SESXiClient{ 134 ESXiClientConfig: cfg, 135 context: context.Background(), 136 } 137 138 err := cli.connect() 139 if err != nil { 140 return nil, err 141 } 142 143 if !cli.IsVCenter() { 144 err := cli.checkHostManagedByVCenter() 145 if err != nil { 146 if cfg.managed { 147 cli.disconnect() 148 return nil, err 149 } else { 150 log.Warningf("%s", err) 151 } 152 } 153 } 154 return cli, nil 155 } 156 157 func NewESXiClientFromJson(ctx context.Context, input jsonutils.JSONObject) (*SESXiClient, *vcenter.SVCenterAccessInfo, error) { 158 accessInfo := new(vcenter.SVCenterAccessInfo) 159 err := input.Unmarshal(accessInfo) 160 if err != nil { 161 return nil, nil, errors.Wrapf(err, "unmarshal SVCenterAccessInfo: %s", input) 162 } 163 c, err := NewESXiClientFromAccessInfo(ctx, accessInfo) 164 return c, accessInfo, err 165 } 166 167 func NewESXiClientFromAccessInfo(ctx context.Context, accessInfo *vcenter.SVCenterAccessInfo) (*SESXiClient, error) { 168 if len(accessInfo.VcenterId) > 0 { 169 tmp, err := utils.DescryptAESBase64(accessInfo.VcenterId, accessInfo.Password) 170 if err == nil { 171 accessInfo.Password = tmp 172 } 173 } 174 client, err := NewESXiClient( 175 NewESXiClientConfig( 176 accessInfo.Host, 177 accessInfo.Port, 178 accessInfo.Account, 179 accessInfo.Password, 180 ).Managed(true), 181 ) 182 if err != nil { 183 return nil, err 184 } 185 if ctx == nil { 186 ctx = context.Background() 187 } 188 client.context = ctx 189 return client, nil 190 } 191 192 func (cli *SESXiClient) getUrl() string { 193 if cli.port == 443 || cli.port == 0 { 194 return fmt.Sprintf("https://%s", cli.host) 195 } else { 196 return fmt.Sprintf("https://%s:%d", cli.host, cli.port) 197 } 198 } 199 200 func (cli *SESXiClient) url() string { 201 return fmt.Sprintf("%s/sdk", cli.getUrl()) 202 } 203 204 var ( 205 red = color.New(color.FgRed, color.Bold).PrintlnFunc() 206 green = color.New(color.FgGreen, color.Bold).PrintlnFunc() 207 yellow = color.New(color.FgYellow, color.Bold).PrintlnFunc() 208 cyan = color.New(color.FgHiCyan, color.Bold).PrintlnFunc() 209 ) 210 211 func (cli *SESXiClient) connect() error { 212 u, err := url.Parse(cli.url()) 213 if err != nil { 214 return fmt.Errorf("Illegal url %s: %s", cli.url(), err) 215 } 216 217 var govmcli *govmomi.Client 218 { 219 insecure := true 220 soapCli := soap.NewClient(u, insecure) 221 httpClient := &soapCli.Client 222 transport := httputils.GetAdaptiveTransport(true) 223 transport.Proxy = cli.cpcfg.ProxyFunc 224 httpClient.Transport = cloudprovider.GetCheckTransport(transport, func(req *http.Request) (func(resp *http.Response), error) { 225 if cli.debug { 226 dump, _ := httputil.DumpRequestOut(req, false) 227 yellow(string(dump)) 228 // 忽略掉上传文件的请求,避免大量日志输出 229 if req.Header.Get("Content-Type") != "application/octet-stream" { 230 curlCmd, _ := http2curl.GetCurlCommand(req) 231 cyan("CURL:", curlCmd, "\n") 232 } 233 } 234 respCheck := func(resp *http.Response) { 235 if cli.debug { 236 dump, _ := httputil.DumpResponse(resp, true) 237 body := string(dump) 238 if cli.format == "json" { 239 obj, err := xj.Convert(bytes.NewReader(dump)) 240 if err == nil { 241 body = string(obj.String()) 242 } 243 } 244 if resp.StatusCode < 300 { 245 green(body) 246 } else if resp.StatusCode < 400 { 247 yellow(body) 248 } else { 249 red(body) 250 } 251 } 252 } 253 return respCheck, nil 254 }) 255 vimCli, err := vim25.NewClient(cli.context, soapCli) 256 if err != nil { 257 return err 258 } 259 govmcli = &govmomi.Client{ 260 Client: vimCli, 261 SessionManager: session.NewManager(vimCli), 262 } 263 } 264 265 userinfo := url.UserPassword(cli.account, cli.password) 266 267 err = govmcli.Login(cli.context, userinfo) 268 if err != nil { 269 return err 270 } 271 272 cli.client = govmcli 273 274 defaultDc.ManagedEntity.Parent = &cli.client.ServiceContent.RootFolder 275 276 return nil 277 } 278 279 func (cli *SESXiClient) disconnect() error { 280 if cli.client != nil { 281 return cli.client.Logout(cli.context) 282 } 283 return nil 284 } 285 286 func (cli *SESXiClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { 287 err := cli.connect() 288 if err != nil { 289 return nil, err 290 } 291 subAccount := cloudprovider.SSubAccount{ 292 Account: cli.account, 293 Name: cli.cpcfg.Name, 294 HealthStatus: api.CLOUD_PROVIDER_HEALTH_NORMAL, 295 } 296 return []cloudprovider.SSubAccount{subAccount}, nil 297 } 298 299 func (cli *SESXiClient) GetAccountId() string { 300 return fmt.Sprintf("%s@%s:%d", cli.account, cli.host, cli.port) 301 } 302 303 func (cli *SESXiClient) GetI18n() cloudprovider.SModelI18nTable { 304 table := cloudprovider.SModelI18nTable{} 305 table["name"] = cloudprovider.NewSModelI18nEntry(cli.GetName()).CN(cli.GetName()) 306 return table 307 } 308 309 func (cli *SESXiClient) GetVersion() string { 310 return cli.client.ServiceContent.About.Version 311 } 312 313 func (cli *SESXiClient) About() jsonutils.JSONObject { 314 about := jsonutils.Marshal(&cli.client.ServiceContent.About) 315 aboutDict := about.(*jsonutils.JSONDict) 316 aboutDict.Add(jsonutils.NewString(cli.getEndpointType()), "endpoint_type") 317 return aboutDict 318 } 319 320 func (cli *SESXiClient) getEndpointType() string { 321 if cli.IsVCenter() { 322 return "VCenter" 323 } else { 324 return "ESXi" 325 } 326 } 327 328 func (cli *SESXiClient) GetUUID() string { 329 about := cli.client.ServiceContent.About 330 return about.InstanceUuid 331 } 332 333 func (cli *SESXiClient) fetchDatacentersFromHosts() error { 334 var hosts []mo.HostSystem 335 err := cli.scanAllMObjects(HOST_SYSTEM_PROPS, &hosts) 336 if err != nil { 337 return errors.Wrap(err, "cli.scanAllMObjects host") 338 } 339 dcList := make([]*SDatacenter, 0) 340 for i := 0; i < len(hosts); i += 1 { 341 me := newManagedObject(cli, &hosts[i], nil) 342 dcme := me.findInParents("Datacenter") 343 if dcme == nil { 344 return cloudprovider.ErrNotFound 345 } 346 _, err := findDatacenterByMoId(dcList, dcme.Self.Value) 347 if err == nil { 348 continue 349 } 350 var moDc mo.Datacenter 351 err = cli.reference2Object(dcme.Self, DATACENTER_PROPS, &moDc) 352 if err != nil { 353 return errors.Wrap(err, "cli.reference2Object") 354 } 355 dc, err := cli.newDatacenterFromMo(&moDc) 356 if err != nil { 357 return errors.Wrap(err, "cli.newDatacenterFromMo") 358 } 359 dcList = append(dcList, dc) 360 } 361 cli.datacenters = dcList 362 return nil 363 } 364 365 func (cli *SESXiClient) fetchFakeDatacenter() error { 366 dc, err := cli.newDatacenterFromMo(&defaultDc) 367 if err != nil { 368 return errors.Wrap(err, "newDatacenterFromMo") 369 } 370 cli.datacenters = []*SDatacenter{dc} 371 return nil 372 } 373 374 func (cli *SESXiClient) fetchDatacenters() error { 375 var dcs []mo.Datacenter 376 err := cli.scanAllMObjects(DATACENTER_PROPS, &dcs) 377 if err != nil { 378 // log.Debugf("cli.scanAllMObjects datacenter fail %s, try cli.fetchDatacentersFromHosts", err) 379 // err := cli.fetchDatacentersFromHosts() 380 // if err != nil { 381 log.Debugf("cli.fetchDatacentersFromHosts fail %s, try cli.fetchFakeDatacenter", err) 382 return cli.fetchFakeDatacenter() 383 // } 384 } 385 cli.datacenters = make([]*SDatacenter, len(dcs)) 386 for i := 0; i < len(dcs); i += 1 { 387 dc, err := cli.newDatacenterFromMo(&dcs[i]) 388 if err != nil { 389 return errors.Wrap(err, "cli.newDatacenterFromMo") 390 } 391 cli.datacenters[i] = dc 392 } 393 return nil 394 } 395 396 func (cli *SESXiClient) newDatacenterFromMo(mo *mo.Datacenter) (*SDatacenter, error) { 397 dc := newDatacenter(cli, mo) 398 err := dc.scanHosts() 399 if err != nil { 400 return nil, errors.Wrap(err, "dc.scanHosts") 401 } 402 // err = dc.scanDatastores() 403 // if err != nil { 404 // return nil, errors.Wrap(err, "dc.scanDatastores") 405 // } 406 return dc, nil 407 } 408 409 func (cli *SESXiClient) scanAllMObjects(props []string, dst interface{}) error { 410 return cli.scanMObjects(cli.client.ServiceContent.RootFolder, props, dst) 411 } 412 413 func (cli *SESXiClient) SearchVM(id string) (*SVirtualMachine, error) { 414 filter := property.Filter{} 415 filter["summary.config.uuid"] = id 416 var movms []mo.VirtualMachine 417 err := cli.scanMObjectsWithFilter(cli.client.ServiceContent.RootFolder, VIRTUAL_MACHINE_PROPS, &movms, filter) 418 if err != nil { 419 return nil, err 420 } 421 if len(movms) == 0 { 422 return nil, errors.ErrNotFound 423 } 424 vm := NewVirtualMachine(cli, &movms[0], nil) 425 dc, err := vm.fetchDatacenter() 426 if err != nil { 427 return nil, errors.Wrap(err, "fetchDatacenter") 428 } 429 vm.datacenter = dc 430 return vm, nil 431 } 432 433 func (cli *SESXiClient) SearchTemplateVM(id string) (*SVirtualMachine, error) { 434 filter := property.Filter{} 435 uuid := toTemplateUuid(id) 436 filter["summary.config.uuid"] = uuid 437 var movms []mo.VirtualMachine 438 err := cli.scanMObjectsWithFilter(cli.client.ServiceContent.RootFolder, VIRTUAL_MACHINE_PROPS, &movms, filter) 439 if err != nil { 440 return nil, err 441 } 442 if len(movms) == 0 { 443 return nil, errors.ErrNotFound 444 } 445 vm := NewVirtualMachine(cli, &movms[0], nil) 446 if !vm.IsTemplate() { 447 return nil, errors.ErrNotFound 448 } 449 dc, err := vm.fetchDatacenter() 450 if err != nil { 451 return nil, errors.Wrap(err, "fetchDatacenter") 452 } 453 vm.datacenter = dc 454 return vm, nil 455 } 456 457 func (cli *SESXiClient) scanMObjectsWithFilter(folder types.ManagedObjectReference, props []string, dst interface{}, filter property.Filter) error { 458 dstValue := reflect.Indirect(reflect.ValueOf(dst)) 459 dstType := dstValue.Type() 460 dstEleType := dstType.Elem() 461 462 resType := dstEleType.Name() 463 m := view.NewManager(cli.client.Client) 464 465 v, err := m.CreateContainerView(cli.context, folder, []string{resType}, true) 466 if err != nil { 467 return errors.Wrapf(err, "m.CreateContainerView %s", resType) 468 } 469 470 defer v.Destroy(cli.context) 471 472 err = v.RetrieveWithFilter(cli.context, []string{resType}, props, dst, filter) 473 if err != nil { 474 // hack 475 if strings.Contains(err.Error(), "object references is empty") { 476 return nil 477 } 478 return errors.Wrapf(err, "v.RetrieveWithFilter %s", resType) 479 } 480 481 return nil 482 } 483 484 func (cli *SESXiClient) scanMObjects(folder types.ManagedObjectReference, props []string, dst interface{}) error { 485 dstValue := reflect.Indirect(reflect.ValueOf(dst)) 486 dstType := dstValue.Type() 487 dstEleType := dstType.Elem() 488 489 resType := dstEleType.Name() 490 m := view.NewManager(cli.client.Client) 491 492 v, err := m.CreateContainerView(cli.context, folder, []string{resType}, true) 493 if err != nil { 494 return errors.Wrapf(err, "m.CreateContainerView %s", resType) 495 } 496 497 defer v.Destroy(cli.context) 498 499 err = v.Retrieve(cli.context, []string{resType}, props, dst) 500 if err != nil { 501 return errors.Wrapf(err, "v.Retrieve %s", resType) 502 } 503 504 return nil 505 } 506 507 func (cli *SESXiClient) references2Objects(refs []types.ManagedObjectReference, props []string, dst interface{}) error { 508 pc := property.DefaultCollector(cli.client.Client) 509 err := pc.Retrieve(cli.context, refs, props, dst) 510 if err != nil { 511 log.Errorf("pc.Retrieve fail %s", err) 512 return err 513 } 514 return nil 515 } 516 517 func (cli *SESXiClient) reference2Object(ref types.ManagedObjectReference, props []string, dst interface{}) error { 518 pc := property.DefaultCollector(cli.client.Client) 519 err := pc.RetrieveOne(cli.context, ref, props, dst) 520 if err != nil { 521 return errors.Wrap(err, "pc.RetrieveOne") 522 } 523 return nil 524 } 525 526 func (cli *SESXiClient) GetDatacenters() ([]*SDatacenter, error) { 527 if cli.datacenters == nil { 528 err := cli.fetchDatacenters() 529 if err != nil { 530 return nil, err 531 } 532 } 533 return cli.datacenters, nil 534 } 535 536 func (cli *SESXiClient) FindDatacenterByMoId(dcId string) (*SDatacenter, error) { 537 dcs, err := cli.GetDatacenters() 538 if err != nil { 539 return nil, err 540 } 541 return findDatacenterByMoId(dcs, dcId) 542 } 543 544 func findDatacenterByMoId(dcs []*SDatacenter, dcId string) (*SDatacenter, error) { 545 for i := 0; i < len(dcs); i += 1 { 546 if dcs[i].GetId() == dcId { 547 return dcs[i], nil 548 } 549 // defaultDcId means no premision to get datacenter, so return fake dc 550 if dcs[i].GetId() == defaultDcId { 551 return dcs[i], nil 552 } 553 } 554 return nil, cloudprovider.ErrNotFound 555 } 556 557 func (cli *SESXiClient) GetIProjects() ([]cloudprovider.ICloudProject, error) { 558 dcs, err := cli.GetDatacenters() 559 if err != nil { 560 return nil, errors.Wrap(err, "GetDatacenters") 561 } 562 ret := []cloudprovider.ICloudProject{} 563 for i := 0; i < len(dcs); i++ { 564 iprojects, err := dcs[i].GetResourcePools() 565 if err != nil { 566 return nil, errors.Wrap(err, "GetResourcePools") 567 } 568 ret = append(ret, iprojects...) 569 } 570 return ret, nil 571 } 572 573 func (cli *SESXiClient) FindHostByMoId(moId string) (cloudprovider.ICloudHost, error) { 574 dcs, err := cli.GetDatacenters() 575 if err != nil { 576 return nil, err 577 } 578 for i := 0; i < len(dcs); i += 1 { 579 ihost, err := dcs[i].GetIHostByMoId(moId) 580 if err == nil { 581 return ihost, nil 582 } 583 } 584 return nil, cloudprovider.ErrNotFound 585 } 586 587 func (cli *SESXiClient) getPrivateId(idStr string) string { 588 if len(cli.cpcfg.Id) > 0 && strings.HasPrefix(idStr, cli.cpcfg.Id) { 589 idStr = idStr[len(cli.cpcfg.Id)+1:] 590 } 591 return idStr 592 } 593 594 func (cli *SESXiClient) checkHostManagedByVCenter() error { 595 host, err := cli.FindHostByIp(cli.host) 596 if err != nil { 597 if errors.Cause(err) == errors.ErrNotFound { 598 // host might be behind a NAT 599 return nil 600 } 601 return errors.Wrap(err, "cli.FindHostByIp") 602 } 603 if host.IsManagedByVCenter() { 604 return fmt.Errorf("ESXi host is managed by vcenter %s, please connect to vcenter instead for full management functions!", host.GetManagementServerIp()) 605 } 606 return nil 607 } 608 609 func (cli *SESXiClient) IsHostIpExists(hostIp string) (bool, error) { 610 searchIndex := object.NewSearchIndex(cli.client.Client) 611 612 hostRef, err := searchIndex.FindByIp(cli.context, nil, cli.getPrivateId(hostIp), false) 613 if err != nil { 614 log.Errorf("searchIndex.FindByIp fail %s", err) 615 return false, err 616 } 617 if hostRef == nil { 618 return false, nil 619 } 620 return true, nil 621 } 622 623 func (cli *SESXiClient) FindHostByIp(hostIp string) (*SHost, error) { 624 searchIndex := object.NewSearchIndex(cli.client.Client) 625 626 hostRef, err := searchIndex.FindByIp(cli.context, nil, cli.getPrivateId(hostIp), false) 627 if err != nil { 628 log.Errorf("searchIndex.FindByIp fail %s", err) 629 return nil, errors.Wrap(err, "searchIndex.FindByIp") 630 } 631 632 if hostRef == nil { 633 return nil, errors.Wrapf(errors.ErrNotFound, "cannot find %s", cli.getPrivateId(hostIp)) 634 } 635 636 var host mo.HostSystem 637 err = cli.reference2Object(hostRef.Reference(), HOST_SYSTEM_PROPS, &host) 638 if err != nil { 639 log.Errorf("reference2Object fail %s", err) 640 return nil, errors.Wrap(err, "cli.reference2Object") 641 } 642 643 h := NewHost(cli, &host, nil) 644 if h == nil { 645 return nil, errors.Wrap(errors.ErrInvalidStatus, "empty host mo") 646 } 647 return h, nil 648 } 649 650 func (cli *SESXiClient) acquireCloneTicket() (string, error) { 651 manager := session.NewManager(cli.client.Client) 652 return manager.AcquireCloneTicket(cli.context) 653 } 654 655 func (cli *SESXiClient) IsVCenter() bool { 656 return cli.client.Client.IsVC() 657 } 658 659 func (cli *SESXiClient) IsValid() bool { 660 return cli.client.Client.Valid() 661 } 662 663 func (cli *SESXiClient) GetCapabilities() []string { 664 caps := []string{ 665 cloudprovider.CLOUD_CAPABILITY_PROJECT, 666 cloudprovider.CLOUD_CAPABILITY_COMPUTE, 667 // cloudprovider.CLOUD_CAPABILITY_NETWORK, 668 // cloudprovider.CLOUD_CAPABILITY_LOADBALANCER, 669 // cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE, 670 // cloudprovider.CLOUD_CAPABILITY_RDS, 671 // cloudprovider.CLOUD_CAPABILITY_CACHE, 672 // cloudprovider.CLOUD_CAPABILITY_EVENT, 673 } 674 return caps 675 } 676 677 func (cli *SESXiClient) FindVMByPrivateID(idstr string) (*SVirtualMachine, error) { 678 searchIndex := object.NewSearchIndex(cli.client.Client) 679 instanceUuid := true 680 vmRef, err := searchIndex.FindByUuid(cli.context, nil, idstr, true, &instanceUuid) 681 if err != nil { 682 return nil, errors.Wrap(err, "searchIndex.FindByUuid fail") 683 } 684 if vmRef == nil { 685 return nil, fmt.Errorf("cannot find %s", idstr) 686 } 687 var vm mo.VirtualMachine 688 err = cli.reference2Object(vmRef.Reference(), VIRTUAL_MACHINE_PROPS, &vm) 689 if err != nil { 690 return nil, errors.Wrap(err, "reference2Object fail") 691 } 692 693 ret := NewVirtualMachine(cli, &vm, nil) 694 if ret == nil { 695 return nil, errors.Error("invalid vm") 696 } 697 return ret, nil 698 } 699 700 func (cli *SESXiClient) DoExtendDiskOnline(_vm *SVirtualMachine, _disk *SVirtualDisk, newSizeMb int64) error { 701 disk := _disk.getVirtualDisk() 702 disk.CapacityInKB = newSizeMb * 1024 703 devSepc := types.VirtualDeviceConfigSpec{Operation: types.VirtualDeviceConfigSpecOperationEdit, Device: disk} 704 spec := types.VirtualMachineConfigSpec{DeviceChange: []types.BaseVirtualDeviceConfigSpec{&devSepc}} 705 vm := object.NewVirtualMachine(cli.client.Client, _vm.getVirtualMachine().Reference()) 706 task, err := vm.Reconfigure(cli.context, spec) 707 if err != nil { 708 return errors.Wrapf(err, "vm reconfigure failed") 709 } 710 return task.Wait(cli.context) 711 } 712 713 func (cli *SESXiClient) ExtendDisk(url string, newSizeMb int64) error { 714 param := types.ExtendVirtualDisk_Task{ 715 This: *cli.client.Client.ServiceContent.VirtualDiskManager, 716 Name: url, 717 NewCapacityKb: newSizeMb * 1024, 718 } 719 response, err := methods.ExtendVirtualDisk_Task(cli.context, cli.client, ¶m) 720 if err != nil { 721 return errors.Wrapf(err, "extend virtualdisk task failed") 722 } 723 log.Debugf("extend virtual disk task response: %s", response.Returnval.String()) 724 return nil 725 } 726 727 func (cli *SESXiClient) CopyDisk(ctx context.Context, src, dst string, isForce bool) error { 728 dm := object.NewVirtualDiskManager(cli.client.Client) 729 task, err := dm.CopyVirtualDisk(ctx, src, nil, dst, nil, nil, isForce) 730 if err != nil { 731 return errors.Wrap(err, "CopyVirtualDisk") 732 } 733 return task.Wait(ctx) 734 } 735 736 func (cli *SESXiClient) MoveDisk(ctx context.Context, src, dst string, isForce bool) error { 737 dm := object.NewVirtualDiskManager(cli.client.Client) 738 task, err := dm.MoveVirtualDisk(ctx, src, nil, dst, nil, isForce) 739 if err != nil { 740 return errors.Wrap(err, "MoveVirtualDisk") 741 } 742 return task.Wait(ctx) 743 }