yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/cloudpods/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 cloudpods
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"time"
    21  
    22  	"yunion.io/x/jsonutils"
    23  	"yunion.io/x/pkg/errors"
    24  	"yunion.io/x/pkg/utils"
    25  
    26  	"yunion.io/x/onecloud/pkg/apis"
    27  	api "yunion.io/x/onecloud/pkg/apis/compute"
    28  	modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
    29  	"yunion.io/x/onecloud/pkg/mcclient/modules/identity"
    30  	"yunion.io/x/onecloud/pkg/mcclient/modules/logger"
    31  	"yunion.io/x/onecloud/pkg/mcclient/modules/monitor"
    32  	"yunion.io/x/onecloud/pkg/mcclient/modules/webconsole"
    33  
    34  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    35  	"yunion.io/x/cloudmux/pkg/multicloud"
    36  )
    37  
    38  type SInstance struct {
    39  	multicloud.SInstanceBase
    40  	CloudpodsTags
    41  
    42  	host *SHost
    43  	api.ServerDetails
    44  }
    45  
    46  func (self *SInstance) GetName() string {
    47  	return self.Name
    48  }
    49  
    50  func (self *SInstance) GetHostname() string {
    51  	return self.Hostname
    52  }
    53  
    54  func (self *SInstance) GetId() string {
    55  	return self.Id
    56  }
    57  
    58  func (self *SInstance) GetGlobalId() string {
    59  	return self.Id
    60  }
    61  
    62  func (self *SInstance) GetStatus() string {
    63  	return self.Status
    64  }
    65  
    66  func (self *SInstance) Refresh() error {
    67  	ins, err := self.host.zone.region.GetInstance(self.Id)
    68  	if err != nil {
    69  		return err
    70  	}
    71  	return jsonutils.Update(self, ins)
    72  }
    73  
    74  func (self *SInstance) GetCreatedAt() time.Time {
    75  	return self.CreatedAt
    76  }
    77  
    78  func (self *SInstance) GetExpiredAt() time.Time {
    79  	return self.ExpiredAt
    80  }
    81  
    82  func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
    83  	return self.host
    84  }
    85  
    86  func (self *SInstance) GetIHostId() string {
    87  	return self.HostId
    88  }
    89  
    90  func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
    91  	disks, err := self.host.zone.region.GetDisks("", self.Id)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	ret := []cloudprovider.ICloudDisk{}
    96  	for i := range disks {
    97  		disks[i].region = self.host.zone.region
    98  		ret = append(ret, &disks[i])
    99  	}
   100  	return ret, nil
   101  }
   102  
   103  func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
   104  	if len(self.Eip) > 0 {
   105  		eips, err := self.host.zone.region.GetEips(self.Id)
   106  		if err != nil {
   107  			return nil, err
   108  		}
   109  		for i := range eips {
   110  			eips[i].region = self.host.zone.region
   111  			return &eips[i], nil
   112  		}
   113  		return nil, cloudprovider.ErrNotFound
   114  	}
   115  	return nil, nil
   116  }
   117  
   118  func (self *SInstance) GetVcpuCount() int {
   119  	return self.VcpuCount
   120  }
   121  
   122  func (self *SInstance) GetVmemSizeMB() int {
   123  	return self.VmemSize
   124  }
   125  
   126  func (self *SInstance) GetBootOrder() string {
   127  	return self.BootOrder
   128  }
   129  
   130  func (self *SInstance) GetVga() string {
   131  	return self.Vga
   132  }
   133  
   134  func (self *SInstance) GetVdi() string {
   135  	return self.Vdi
   136  }
   137  
   138  func (self *SInstance) GetOsType() cloudprovider.TOsType {
   139  	return cloudprovider.TOsType(self.OsType)
   140  }
   141  
   142  func (self *SInstance) GetFullOsName() string {
   143  	return self.OsName
   144  }
   145  
   146  func (self *SInstance) GetBios() cloudprovider.TBiosType {
   147  	return cloudprovider.ToBiosType(self.Bios)
   148  }
   149  
   150  func (ins *SInstance) GetOsDist() string {
   151  	val, ok := ins.Metadata["os_distribution"]
   152  	if ok {
   153  		return val
   154  	}
   155  	return ""
   156  }
   157  
   158  func (ins *SInstance) GetOsVersion() string {
   159  	val, ok := ins.Metadata["os_version"]
   160  	if ok {
   161  		return val
   162  	}
   163  	return ""
   164  }
   165  
   166  func (ins *SInstance) GetOsLang() string {
   167  	val, ok := ins.Metadata["os_language"]
   168  	if ok {
   169  		return val
   170  	}
   171  	return ""
   172  }
   173  
   174  func (ins *SInstance) GetOsArch() string {
   175  	return ins.OsArch
   176  }
   177  
   178  func (self *SInstance) GetMachine() string {
   179  	return self.Machine
   180  }
   181  
   182  func (self *SInstance) GetInstanceType() string {
   183  	return self.InstanceType
   184  }
   185  
   186  func (self *SInstance) GetSecurityGroupIds() ([]string, error) {
   187  	ret := []string{}
   188  	for _, sec := range self.Secgroups {
   189  		ret = append(ret, sec.Id)
   190  	}
   191  	return ret, nil
   192  }
   193  
   194  func (self *SInstance) GetProjectId() string {
   195  	return self.TenantId
   196  }
   197  
   198  func (self *SInstance) AssignSecurityGroup(id string) error {
   199  	input := api.GuestAssignSecgroupInput{}
   200  	input.SecgroupId = id
   201  	_, err := self.host.zone.region.perform(&modules.Servers, self.Id, "assign-secgroup", input)
   202  	return err
   203  }
   204  
   205  func (self *SInstance) SetSecurityGroups(ids []string) error {
   206  	if self.Hypervisor == api.HYPERVISOR_ESXI {
   207  		return nil
   208  	}
   209  	input := api.GuestSetSecgroupInput{}
   210  	input.SecgroupIds = ids
   211  	_, err := self.host.zone.region.perform(&modules.Servers, self.Id, "set-secgroup", input)
   212  	return err
   213  }
   214  
   215  func (self *SInstance) GetHypervisor() string {
   216  	return api.HYPERVISOR_CLOUDPODS
   217  }
   218  
   219  func (self *SInstance) StartVM(ctx context.Context) error {
   220  	if self.Status == api.VM_RUNNING {
   221  		return nil
   222  	}
   223  	_, err := self.host.zone.region.perform(&modules.Servers, self.Id, "start", nil)
   224  	return err
   225  }
   226  
   227  func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
   228  	if self.Status == api.VM_READY {
   229  		return nil
   230  	}
   231  	input := api.ServerStopInput{}
   232  	input.IsForce = opts.IsForce
   233  	_, err := self.host.zone.region.perform(&modules.Servers, self.Id, "stop", input)
   234  	return err
   235  }
   236  
   237  func (self *SInstance) DeleteVM(ctx context.Context) error {
   238  	if self.DisableDelete != nil && *self.DisableDelete {
   239  		input := api.ServerUpdateInput{}
   240  		disableDelete := false
   241  		input.DisableDelete = &disableDelete
   242  		self.host.zone.region.cli.update(&modules.Servers, self.Id, input)
   243  	}
   244  	return self.host.zone.region.cli.delete(&modules.Servers, self.Id)
   245  }
   246  
   247  func (self *SInstance) UpdateVM(ctx context.Context, name string) error {
   248  	if self.Name != name {
   249  		input := api.ServerUpdateInput{}
   250  		input.Name = name
   251  		self.host.zone.region.cli.update(&modules.Servers, self.Id, input)
   252  		return cloudprovider.WaitMultiStatus(self, []string{api.VM_READY, api.VM_RUNNING}, time.Second*5, time.Minute*3)
   253  	}
   254  	return nil
   255  }
   256  
   257  func (self *SInstance) UpdateUserData(userData string) error {
   258  	input := api.ServerUserDataInput{}
   259  	input.UserData = userData
   260  	_, err := self.host.zone.region.perform(&modules.Servers, self.Id, "user-data", input)
   261  	return err
   262  }
   263  
   264  func (self *SInstance) RebuildRoot(ctx context.Context, opts *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
   265  	input := api.ServerRebuildRootInput{}
   266  	input.ImageId = opts.ImageId
   267  	input.Password = opts.Password
   268  	if len(opts.PublicKey) > 0 {
   269  		keypairId, err := self.host.zone.region.syncKeypair(self.Name, opts.PublicKey)
   270  		if err != nil {
   271  			return "", errors.Wrapf(err, "syncKeypair")
   272  		}
   273  		input.KeypairId = keypairId
   274  	}
   275  	_, err := self.host.zone.region.perform(&modules.Servers, self.Id, "rebuild-root", input)
   276  	if err != nil {
   277  		return "", err
   278  	}
   279  	return self.DisksInfo[0].Id, nil
   280  }
   281  
   282  func (self *SInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error {
   283  	input := api.ServerDeployInput{}
   284  	input.Password = password
   285  	if len(publicKey) > 0 {
   286  		keypairId, err := self.host.zone.region.syncKeypair(name, publicKey)
   287  		if err != nil {
   288  			return errors.Wrapf(err, "syncKeypair")
   289  		}
   290  		input.KeypairId = keypairId
   291  	}
   292  
   293  	_, err := self.host.zone.region.perform(&modules.Servers, self.Id, "deploy", input)
   294  	if err != nil {
   295  		return errors.Wrapf(err, "deploy")
   296  	}
   297  	return cloudprovider.WaitMultiStatus(self, []string{api.VM_READY, api.VM_RUNNING}, time.Second*5, time.Minute*3)
   298  }
   299  
   300  func (self *SInstance) ChangeConfig(ctx context.Context, opts *cloudprovider.SManagedVMChangeConfig) error {
   301  	input := api.ServerChangeConfigInput{}
   302  	input.VmemSize = fmt.Sprintf("%dM", opts.MemoryMB)
   303  	input.VcpuCount = opts.Cpu
   304  	input.InstanceType = opts.InstanceType
   305  	_, err := self.host.zone.region.perform(&modules.Servers, self.Id, "change-config", input)
   306  	return err
   307  }
   308  
   309  func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
   310  	s := self.host.zone.region.cli.s
   311  	resp, err := webconsole.WebConsole.DoServerConnect(s, self.Id, nil)
   312  	if err != nil {
   313  		return nil, errors.Wrapf(err, "DoServerConnect")
   314  	}
   315  	result := &cloudprovider.ServerVncOutput{
   316  		Protocol:     "cloudpods",
   317  		InstanceId:   self.Id,
   318  		InstanceName: self.Name,
   319  		Hypervisor:   api.HYPERVISOR_CLOUDPODS,
   320  	}
   321  	err = resp.Unmarshal(&result)
   322  	if err != nil {
   323  		return nil, errors.Wrapf(err, "resp.Unmarshal")
   324  	}
   325  	resp, err = identity.ServicesV3.GetSpecific(s, "common", "config", nil)
   326  	if err != nil {
   327  		return nil, errors.Wrapf(err, "GetSpecific")
   328  	}
   329  	result.ApiServer, _ = resp.GetString("config", "default", "api_server")
   330  	return result, nil
   331  }
   332  
   333  func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
   334  	input := api.ServerAttachDiskInput{}
   335  	input.DiskId = diskId
   336  	_, err := self.host.zone.region.perform(&modules.Servers, self.Id, "attachdisk", input)
   337  	return err
   338  }
   339  
   340  func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
   341  	input := api.ServerDetachDiskInput{}
   342  	input.DiskId = diskId
   343  	input.KeepDisk = true
   344  	_, err := self.host.zone.region.perform(&modules.Servers, self.Id, "detachdisk", input)
   345  	return err
   346  }
   347  
   348  func (self *SInstance) MigrateVM(hostId string) error {
   349  	input := api.GuestMigrateInput{}
   350  	input.PreferHost = hostId
   351  	_, err := self.host.zone.region.perform(&modules.Servers, self.Id, "migrate", input)
   352  	return err
   353  }
   354  
   355  func (self *SInstance) LiveMigrateVM(hostId string) error {
   356  	input := api.GuestLiveMigrateInput{}
   357  	input.PreferHost = hostId
   358  	skipCpuCheck := true
   359  	input.SkipCpuCheck = &skipCpuCheck
   360  	_, err := self.host.zone.region.perform(&modules.Servers, self.Id, "live-migrate", input)
   361  	return err
   362  }
   363  
   364  func (self *SInstance) GetError() error {
   365  	if utils.IsInStringArray(self.Status, []string{api.VM_DISK_FAILED, api.VM_SCHEDULE_FAILED, api.VM_NETWORK_FAILED}) {
   366  		return fmt.Errorf("vm create failed with status %s", self.Status)
   367  	}
   368  	if self.Status == api.VM_DEPLOY_FAILED {
   369  		params := map[string]interface{}{"obj_id": self.Id, "success": false}
   370  		actions := []apis.OpsLogDetails{}
   371  		self.host.zone.region.list(&logger.Actions, params, &actions)
   372  		if len(actions) > 0 {
   373  			return fmt.Errorf(actions[0].Notes)
   374  		}
   375  		return fmt.Errorf("vm create failed with status %s", self.Status)
   376  	}
   377  	return nil
   378  }
   379  
   380  func (self *SInstance) CreateInstanceSnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudInstanceSnapshot, error) {
   381  	return nil, cloudprovider.ErrNotImplemented
   382  }
   383  
   384  func (self *SInstance) GetInstanceSnapshot(idStr string) (cloudprovider.ICloudInstanceSnapshot, error) {
   385  	return nil, cloudprovider.ErrNotImplemented
   386  }
   387  
   388  func (self *SInstance) GetInstanceSnapshots() ([]cloudprovider.ICloudInstanceSnapshot, error) {
   389  	return nil, cloudprovider.ErrNotImplemented
   390  }
   391  
   392  func (self *SInstance) ResetToInstanceSnapshot(ctx context.Context, idStr string) error {
   393  	return cloudprovider.ErrNotImplemented
   394  }
   395  
   396  func (self *SInstance) SaveImage(opts *cloudprovider.SaveImageOptions) (cloudprovider.ICloudImage, error) {
   397  	return self.host.zone.region.SaveImage(self.Id, opts.Name, opts.Notes)
   398  }
   399  
   400  func (self *SRegion) SaveImage(id, imageName, notes string) (*SImage, error) {
   401  	input := api.ServerSaveImageInput{}
   402  	input.GenerateName = imageName
   403  	input.Notes = notes
   404  	resp, err := self.perform(&modules.Servers, id, "save-image", input)
   405  	if err != nil {
   406  		return nil, err
   407  	}
   408  	imageId, err := resp.GetString("image_id")
   409  	if err != nil {
   410  		return nil, err
   411  	}
   412  	caches, err := self.GetStoragecaches()
   413  	if err != nil {
   414  		return nil, errors.Wrapf(err, "GetStoragecaches")
   415  	}
   416  	if len(caches) == 0 {
   417  		return nil, fmt.Errorf("no storage cache found")
   418  	}
   419  	caches[0].region = self
   420  	image, err := self.GetImage(imageId)
   421  	if err != nil {
   422  		return nil, err
   423  	}
   424  	image.cache = &caches[0]
   425  	return image, nil
   426  }
   427  
   428  func (self *SInstance) AllocatePublicIpAddress() (string, error) {
   429  	return "", cloudprovider.ErrNotImplemented
   430  }
   431  
   432  func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
   433  	servers, err := self.zone.region.GetInstances(self.Id)
   434  	if err != nil {
   435  		return nil, err
   436  	}
   437  	ret := []cloudprovider.ICloudVM{}
   438  	for i := range servers {
   439  		servers[i].host = self
   440  		ret = append(ret, &servers[i])
   441  	}
   442  	return ret, nil
   443  }
   444  
   445  func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
   446  	ins, err := self.zone.region.GetInstance(id)
   447  	if err != nil {
   448  		return nil, err
   449  	}
   450  	ins.host = self
   451  	return ins, nil
   452  }
   453  
   454  func (self *SRegion) GetInstance(id string) (*SInstance, error) {
   455  	ins := &SInstance{}
   456  	return ins, self.cli.get(&modules.Servers, id, nil, ins)
   457  }
   458  
   459  func (self *SRegion) GetInstances(hostId string) ([]SInstance, error) {
   460  	params := map[string]interface{}{}
   461  	if len(hostId) > 0 {
   462  		params["host_id"] = hostId
   463  	}
   464  	ret := []SInstance{}
   465  	return ret, self.list(&modules.Servers, params, &ret)
   466  }
   467  
   468  func (self *SRegion) CreateInstance(hostId, hypervisor string, opts *cloudprovider.SManagedVMCreateConfig) (*SInstance, error) {
   469  	input := api.ServerCreateInput{
   470  		ServerConfigs: &api.ServerConfigs{},
   471  	}
   472  	input.Name = opts.Name
   473  	input.Hostname = opts.Hostname
   474  	input.Description = opts.Description
   475  	input.InstanceType = opts.InstanceType
   476  	input.VcpuCount = opts.Cpu
   477  	input.VmemSize = opts.MemoryMB
   478  	input.Password = opts.Password
   479  	input.PublicIpBw = opts.PublicIpBw
   480  	input.PublicIpChargeType = string(opts.PublicIpChargeType)
   481  	input.ProjectId = opts.ProjectId
   482  	input.Metadata = opts.Tags
   483  	input.UserData = opts.UserData
   484  	input.PreferHost = hostId
   485  	input.Hypervisor = hypervisor
   486  	if len(input.UserData) > 0 {
   487  		input.EnableCloudInit = true
   488  	}
   489  	input.Secgroups = opts.ExternalSecgroupIds
   490  	if opts.BillingCycle != nil {
   491  		input.Duration = opts.BillingCycle.String()
   492  	}
   493  	input.Disks = append(input.Disks, &api.DiskConfig{
   494  		Index:    0,
   495  		ImageId:  opts.ExternalImageId,
   496  		DiskType: api.DISK_TYPE_SYS,
   497  		SizeMb:   opts.SysDisk.SizeGB * 1024,
   498  		Backend:  opts.SysDisk.StorageType,
   499  		Storage:  opts.SysDisk.StorageExternalId,
   500  	})
   501  	for idx, disk := range opts.DataDisks {
   502  		input.Disks = append(input.Disks, &api.DiskConfig{
   503  			Index:    idx + 1,
   504  			DiskType: api.DISK_TYPE_DATA,
   505  			SizeMb:   disk.SizeGB * 1024,
   506  			Backend:  disk.StorageType,
   507  			Storage:  disk.StorageExternalId,
   508  		})
   509  	}
   510  	input.Networks = append(input.Networks, &api.NetworkConfig{
   511  		Index:   0,
   512  		Network: opts.ExternalNetworkId,
   513  		Address: opts.IpAddr,
   514  	})
   515  	ins := &SInstance{}
   516  	return ins, self.create(&modules.Servers, input, ins)
   517  }
   518  
   519  type SMetricData struct {
   520  	Id    string    `json:"id"`
   521  	Time  time.Time `json:"time"`
   522  	Value float64   `json:"value"`
   523  }
   524  
   525  func (cli *SCloudpodsClient) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   526  	brandArr := []string{"OneCloud", "VMware"}
   527  	metrics := []SMetricData{}
   528  	for i := 0; i < len(brandArr); i++ {
   529  		params := map[string]interface{}{
   530  			"metric_name": opts.MetricType,
   531  			"tags": map[string]interface{}{
   532  				"brand": brandArr[i],
   533  			},
   534  		}
   535  		onecloudObj, err := monitor.UnifiedMonitorManager.Get(cli.s, "simple-query", jsonutils.Marshal(params))
   536  		if err != nil {
   537  			return nil, err
   538  		}
   539  		tmp := []SMetricData{}
   540  		onecloudObj.Unmarshal(&tmp, "values")
   541  		metrics = append(metrics, tmp...)
   542  	}
   543  
   544  	idWithMetric := map[string][]cloudprovider.MetricValue{}
   545  	for _, v := range metrics {
   546  		if _, isExist := idWithMetric[v.Id]; !isExist {
   547  			idWithMetric[v.Id] = []cloudprovider.MetricValue{{
   548  				Timestamp: v.Time,
   549  				Value:     v.Value}}
   550  		} else {
   551  			idWithMetric[v.Id] = append(idWithMetric[v.Id], cloudprovider.MetricValue{
   552  				Timestamp: v.Time,
   553  				Value:     v.Value})
   554  		}
   555  	}
   556  
   557  	res := []cloudprovider.MetricValues{}
   558  	for _, metric := range metrics {
   559  		res = append(res, cloudprovider.MetricValues{
   560  			Id:         metric.Id,
   561  			MetricType: opts.MetricType,
   562  			Values:     idWithMetric[metric.Id],
   563  		})
   564  	}
   565  	return res, nil
   566  }