yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/ecloud/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 ecloud
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"time"
    21  
    22  	"yunion.io/x/pkg/errors"
    23  	"yunion.io/x/pkg/util/sets"
    24  
    25  	billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
    26  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    27  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    28  	"yunion.io/x/cloudmux/pkg/multicloud"
    29  	"yunion.io/x/onecloud/pkg/util/billing"
    30  )
    31  
    32  type SNovaRequest struct {
    33  	SApiRequest
    34  }
    35  
    36  func NewNovaRequest(ar *SApiRequest) *SNovaRequest {
    37  	return &SNovaRequest{
    38  		SApiRequest: *ar,
    39  	}
    40  }
    41  
    42  func (nr *SNovaRequest) GetPort() string {
    43  	if nr.RegionId == "guangzhou-2" {
    44  		return ""
    45  	}
    46  	return nr.SApiRequest.GetPort()
    47  }
    48  
    49  type SInstance struct {
    50  	multicloud.SInstanceBase
    51  	EcloudTags
    52  	multicloud.SBillingBase
    53  	SZoneRegionBase
    54  	SCreateTime
    55  
    56  	nicComplete bool
    57  	host        *SHost
    58  	image       *SImage
    59  
    60  	sysDisk   cloudprovider.ICloudDisk
    61  	dataDisks []cloudprovider.ICloudDisk
    62  
    63  	Id               string
    64  	Name             string
    65  	Vcpu             int
    66  	Vmemory          int
    67  	KeyName          string
    68  	ImageRef         string
    69  	ImageName        string
    70  	ImageOsType      string
    71  	FlavorRef        string
    72  	SystemDiskSizeGB int `json:"vdisk"`
    73  	SystemDiskId     string
    74  	ServerType       string
    75  	ServerVmType     string
    76  	EcStatus         string
    77  	BootVolumeType   string
    78  	Deleted          int
    79  	Visible          bool
    80  	Region           string
    81  	PortDetail       []SInstanceNic
    82  }
    83  
    84  func (i *SInstance) GetBillingType() string {
    85  	return billing_api.BILLING_TYPE_POSTPAID
    86  }
    87  
    88  func (i *SInstance) GetExpiredAt() time.Time {
    89  	return time.Time{}
    90  }
    91  
    92  func (i *SInstance) GetId() string {
    93  	return i.Id
    94  }
    95  
    96  func (i *SInstance) GetName() string {
    97  	return i.Name
    98  }
    99  
   100  func (i *SInstance) GetHostname() string {
   101  	return i.Name
   102  }
   103  
   104  func (i *SInstance) GetGlobalId() string {
   105  	return i.GetId()
   106  }
   107  
   108  func (i *SInstance) GetStatus() string {
   109  	switch i.EcStatus {
   110  	case "active":
   111  		return api.VM_RUNNING
   112  	case "suspended", "paused":
   113  		return api.VM_SUSPEND
   114  	case "build", "rebuild", "resize", "verify_resize", "revert_resize", "password":
   115  		return api.VM_STARTING
   116  	case "reboot", "hard_reboot":
   117  		return api.VM_STOPPING
   118  	case "stopped", "shutoff":
   119  		return api.VM_READY
   120  	case "migrating":
   121  		return api.VM_MIGRATING
   122  	case "backuping":
   123  		return api.VM_BACKUP_CREATING
   124  	default:
   125  		return api.VM_UNKNOWN
   126  	}
   127  }
   128  
   129  func (i *SInstance) Refresh() error {
   130  	// TODO
   131  	return nil
   132  }
   133  
   134  func (i *SInstance) IsEmulated() bool {
   135  	return false
   136  }
   137  
   138  func (self *SInstance) GetBootOrder() string {
   139  	return "dcn"
   140  }
   141  
   142  func (self *SInstance) GetVga() string {
   143  	return "std"
   144  }
   145  
   146  func (self *SInstance) GetVdi() string {
   147  	return "vnc"
   148  }
   149  
   150  func (i *SInstance) GetImage() (*SImage, error) {
   151  	if i.image != nil {
   152  		return i.image, nil
   153  	}
   154  	image, err := i.host.zone.region.GetImage(i.ImageRef)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	i.image = image
   159  	return i.image, nil
   160  }
   161  
   162  func (i *SInstance) GetOsType() cloudprovider.TOsType {
   163  	return cloudprovider.TOsType(i.ImageOsType)
   164  }
   165  
   166  func (i *SInstance) GetFullOsName() string {
   167  	image, err := i.GetImage()
   168  	if err != nil {
   169  		return ""
   170  	}
   171  	return image.GetFullOsName()
   172  }
   173  
   174  func (i *SInstance) GetBios() cloudprovider.TBiosType {
   175  	image, err := i.GetImage()
   176  	if err != nil {
   177  		return cloudprovider.BIOS
   178  	}
   179  	return image.GetBios()
   180  }
   181  
   182  func (i *SInstance) GetOsArch() string {
   183  	image, err := i.GetImage()
   184  	if err != nil {
   185  		return ""
   186  	}
   187  	return image.GetOsArch()
   188  }
   189  
   190  func (i *SInstance) GetOsDist() string {
   191  	image, err := i.GetImage()
   192  	if err != nil {
   193  		return ""
   194  	}
   195  	return image.GetOsDist()
   196  }
   197  
   198  func (i *SInstance) GetOsVersion() string {
   199  	image, err := i.GetImage()
   200  	if err != nil {
   201  		return ""
   202  	}
   203  	return image.GetOsVersion()
   204  }
   205  
   206  func (i *SInstance) GetOsLang() string {
   207  	image, err := i.GetImage()
   208  	if err != nil {
   209  		return ""
   210  	}
   211  	return image.GetOsLang()
   212  }
   213  
   214  func (i *SInstance) GetMachine() string {
   215  	return "pc"
   216  }
   217  
   218  func (i *SInstance) GetInstanceType() string {
   219  	return i.FlavorRef
   220  }
   221  
   222  func (in *SInstance) GetProjectId() string {
   223  	return ""
   224  }
   225  
   226  func (in *SInstance) GetIHost() cloudprovider.ICloudHost {
   227  	return in.host
   228  }
   229  
   230  func (in *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
   231  	if in.sysDisk == nil {
   232  		in.fetchSysDisk()
   233  	}
   234  	if in.dataDisks == nil {
   235  		err := in.fetchDataDisks()
   236  		if err != nil {
   237  			return nil, err
   238  		}
   239  	}
   240  	return append([]cloudprovider.ICloudDisk{in.sysDisk}, in.dataDisks...), nil
   241  }
   242  
   243  func (in *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
   244  	if !in.nicComplete {
   245  		err := in.makeNicComplete()
   246  		if err != nil {
   247  			return nil, errors.Wrap(err, "unable to make nics complete")
   248  		}
   249  		in.nicComplete = true
   250  	}
   251  	inics := make([]cloudprovider.ICloudNic, len(in.PortDetail))
   252  	for i := range in.PortDetail {
   253  		in.PortDetail[i].instance = in
   254  		inics[i] = &in.PortDetail[i]
   255  	}
   256  	return inics, nil
   257  }
   258  
   259  func (in *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
   260  	if !in.nicComplete {
   261  		err := in.makeNicComplete()
   262  		if err != nil {
   263  			return nil, errors.Wrap(err, "unable to make nics complete")
   264  		}
   265  		in.nicComplete = true
   266  	}
   267  	var eipId string
   268  	for i := range in.PortDetail {
   269  		if len(in.PortDetail[i].IpId) > 0 {
   270  			eipId = in.PortDetail[i].IpId
   271  			break
   272  		}
   273  	}
   274  	if len(eipId) == 0 {
   275  		return nil, nil
   276  	}
   277  	return in.host.zone.region.GetEipById(eipId)
   278  }
   279  
   280  func (in *SInstance) GetSecurityGroupIds() ([]string, error) {
   281  	if !in.nicComplete {
   282  		err := in.makeNicComplete()
   283  		if err != nil {
   284  			return nil, errors.Wrap(err, "unable to make nics complete")
   285  		}
   286  		in.nicComplete = true
   287  	}
   288  	ret := sets.NewString()
   289  	for i := range in.PortDetail {
   290  		for _, group := range in.PortDetail[i].SecurityGroups {
   291  			ret.Insert(group.Id)
   292  		}
   293  	}
   294  	return ret.UnsortedList(), nil
   295  }
   296  
   297  func (in *SInstance) GetVcpuCount() int {
   298  	return in.Vcpu
   299  }
   300  
   301  func (in *SInstance) GetVmemSizeMB() int {
   302  	return in.Vmemory
   303  }
   304  
   305  func (in *SInstance) AssignSecurityGroup(id string) error {
   306  	return cloudprovider.ErrNotImplemented
   307  }
   308  
   309  func (in *SInstance) SetSecurityGroups(ids []string) error {
   310  	return cloudprovider.ErrNotImplemented
   311  }
   312  
   313  func (in *SInstance) GetHypervisor() string {
   314  	return api.HYPERVISOR_ECLOUD
   315  }
   316  
   317  func (in *SInstance) StartVM(ctx context.Context) error {
   318  	return cloudprovider.ErrNotImplemented
   319  }
   320  
   321  func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
   322  	return cloudprovider.ErrNotImplemented
   323  }
   324  
   325  func (self *SInstance) DeleteVM(ctx context.Context) error {
   326  	return cloudprovider.ErrNotImplemented
   327  }
   328  
   329  func (self *SInstance) UpdateVM(ctx context.Context, name string) error {
   330  	return cloudprovider.ErrNotSupported
   331  }
   332  
   333  func (self *SInstance) UpdateUserData(userData string) error {
   334  	return cloudprovider.ErrNotSupported
   335  }
   336  
   337  func (self *SInstance) RebuildRoot(ctx context.Context, config *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
   338  	return "", cloudprovider.ErrNotImplemented
   339  }
   340  
   341  func (self *SInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error {
   342  	return cloudprovider.ErrNotImplemented
   343  }
   344  
   345  func (in *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
   346  	return errors.ErrNotImplemented
   347  }
   348  
   349  func (in *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
   350  	url, err := in.host.zone.region.GetInstanceVNCUrl(in.GetId())
   351  	if err != nil {
   352  		return nil, err
   353  	}
   354  	ret := &cloudprovider.ServerVncOutput{
   355  		Url:        url,
   356  		Protocol:   "ecloud",
   357  		InstanceId: in.GetId(),
   358  		Hypervisor: api.HYPERVISOR_ECLOUD,
   359  	}
   360  	return ret, nil
   361  }
   362  
   363  func (in *SInstance) AttachDisk(ctx context.Context, diskId string) error {
   364  	return cloudprovider.ErrNotImplemented
   365  }
   366  
   367  func (in *SInstance) DetachDisk(ctx context.Context, diskId string) error {
   368  	return cloudprovider.ErrNotImplemented
   369  }
   370  
   371  func (self *SInstance) Renew(bc billing.SBillingCycle) error {
   372  	return cloudprovider.ErrNotImplemented
   373  }
   374  
   375  func (self *SInstance) GetError() error {
   376  	return nil
   377  }
   378  
   379  func (in *SInstance) fetchSysDisk() {
   380  	storage, _ := in.host.zone.getStorageByType(api.STORAGE_ECLOUD_SYSTEM)
   381  	disk := SDisk{
   382  		storage: storage,
   383  		ManualAttr: SDiskManualAttr{
   384  			IsVirtual:  true,
   385  			TempalteId: in.ImageRef,
   386  			ServerId:   in.Id,
   387  		},
   388  		SCreateTime:     in.SCreateTime,
   389  		SZoneRegionBase: in.SZoneRegionBase,
   390  		ServerId:        []string{in.Id},
   391  		IsShare:         false,
   392  		IsDelete:        false,
   393  		SizeGB:          in.SystemDiskSizeGB,
   394  		ID:              in.SystemDiskId,
   395  		Name:            fmt.Sprintf("%s-root", in.Name),
   396  		Status:          "in-use",
   397  		Type:            api.STORAGE_ECLOUD_SYSTEM,
   398  	}
   399  	in.sysDisk = &disk
   400  	return
   401  }
   402  
   403  func (in *SInstance) fetchDataDisks() error {
   404  	request := NewNovaRequest(NewApiRequest(in.host.zone.region.ID, "/api/v2/volume/volume/mount/list",
   405  		map[string]string{"serverId": in.Id}, nil))
   406  	disks := make([]SDisk, 0, 5)
   407  	err := in.host.zone.region.client.doList(context.Background(), request, &disks)
   408  	if err != nil {
   409  		return err
   410  	}
   411  	idisks := make([]cloudprovider.ICloudDisk, len(disks))
   412  	for i := range idisks {
   413  		storageType := disks[i].Type
   414  		storage, err := in.host.zone.getStorageByType(storageType)
   415  		if err != nil {
   416  			return errors.Wrapf(err, "unable to fetch storage with stoageType %s", storageType)
   417  		}
   418  		disks[i].storage = storage
   419  		idisks[i] = &disks[i]
   420  	}
   421  	in.dataDisks = idisks
   422  	return nil
   423  }
   424  
   425  func (in *SInstance) makeNicComplete() error {
   426  	routerIds := sets.NewString()
   427  	nics := make(map[string]*SInstanceNic, len(in.PortDetail))
   428  	for i := range in.PortDetail {
   429  		nic := &in.PortDetail[i]
   430  		routerIds.Insert(nic.RouterId)
   431  		nics[nic.PortId] = nic
   432  	}
   433  	for _, routerId := range routerIds.UnsortedList() {
   434  		request := NewConsoleRequest(in.host.zone.region.ID, fmt.Sprintf("/api/vpc/%s/nic", routerId),
   435  			map[string]string{
   436  				"resourceId": in.Id,
   437  			}, nil,
   438  		)
   439  		completeNics := make([]SInstanceNic, 0, len(nics)/2)
   440  		err := in.host.zone.region.client.doList(context.Background(), request, &completeNics)
   441  		if err != nil {
   442  			return errors.Wrapf(err, "unable to get nics with instance %s in vpc %s", in.Id, routerId)
   443  		}
   444  		for i := range completeNics {
   445  			id := completeNics[i].Id
   446  			nic, ok := nics[id]
   447  			if !ok {
   448  				continue
   449  			}
   450  			nic.SInstanceNicDetail = completeNics[i].SInstanceNicDetail
   451  		}
   452  	}
   453  	return nil
   454  }
   455  
   456  func (r *SRegion) findHost(zoneRegion string) (*SHost, error) {
   457  	zone, err := r.FindZone(zoneRegion)
   458  	if err != nil {
   459  		return nil, err
   460  	}
   461  	return &SHost{
   462  		zone: zone,
   463  	}, nil
   464  }
   465  
   466  func (r *SRegion) GetInstancesWithHost(zoneRegion string) ([]SInstance, error) {
   467  	instances, err := r.GetInstances(zoneRegion)
   468  	if err != nil {
   469  		return nil, err
   470  	}
   471  	for i := range instances {
   472  		host, _ := r.findHost(instances[i].Region)
   473  		instances[i].host = host
   474  	}
   475  	return instances, nil
   476  }
   477  
   478  func (r *SRegion) GetInstances(zoneRegion string) ([]SInstance, error) {
   479  	return r.getInstances(zoneRegion, "")
   480  }
   481  
   482  func (r *SRegion) getInstances(zoneRegion string, serverId string) ([]SInstance, error) {
   483  	query := map[string]string{
   484  		"serverTypes":  "VM",
   485  		"productTypes": "NORMAL,AUTOSCALING,VO,CDN,PAAS_MASTER,PAAS_SLAVE,VCPE,EMR,LOGAUDIT",
   486  		//"productTypes": "NORMAL",
   487  		"visible": "true",
   488  	}
   489  	if len(serverId) > 0 {
   490  		query["serverId"] = serverId
   491  	}
   492  	if len(zoneRegion) > 0 {
   493  		query["region"] = zoneRegion
   494  	}
   495  	request := NewNovaRequest(NewApiRequest(r.ID, "/api/v2/server/web/with/network", query, nil))
   496  	var instances []SInstance
   497  	err := r.client.doList(context.Background(), request, &instances)
   498  	if err != nil {
   499  		return nil, err
   500  	}
   501  	return instances, nil
   502  }
   503  
   504  func (r *SRegion) GetInstanceById(id string) (*SInstance, error) {
   505  	instances, err := r.getInstances("", id)
   506  	if err != nil {
   507  		return nil, err
   508  	}
   509  	if len(instances) == 0 {
   510  		return nil, cloudprovider.ErrNotFound
   511  	}
   512  	instance := &instances[0]
   513  	host, err := r.findHost(instance.Region)
   514  	if err == nil {
   515  		instance.host = host
   516  	}
   517  	return instance, nil
   518  }
   519  
   520  func (r *SRegion) GetInstanceVNCUrl(instanceId string) (string, error) {
   521  	request := NewNovaRequest(NewApiRequest(r.ID, fmt.Sprintf("/api/server/%s/vnc", instanceId), nil, nil))
   522  	var url string
   523  	err := r.client.doGet(context.Background(), request, &url)
   524  	if err != nil {
   525  		return "", err
   526  	}
   527  	return url, nil
   528  }