yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/zstack/host.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 zstack
    16  
    17  import (
    18  	"fmt"
    19  	"net/url"
    20  	"strings"
    21  
    22  	"github.com/pkg/errors"
    23  
    24  	"yunion.io/x/jsonutils"
    25  	"yunion.io/x/log"
    26  
    27  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    28  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    29  	"yunion.io/x/cloudmux/pkg/multicloud"
    30  )
    31  
    32  type SHost struct {
    33  	multicloud.SHostBase
    34  	zone *SZone
    35  
    36  	ZStackBasic
    37  	Username                string `json:"username"`
    38  	SSHPort                 int    `json:"sshPort"`
    39  	ZoneUUID                string `json:"zoneUuid"`
    40  	ClusterUUID             string `json:"clusterUuid"`
    41  	ManagementIP            string `json:"managementIp"`
    42  	HypervisorType          string `json:"hypervisorType"`
    43  	State                   string `json:"state"`
    44  	Status                  string `json:"status"`
    45  	TotalCPUCapacity        int    `json:"totalCpuCapacity"`
    46  	AvailableCPUCapacity    int    `json:"availableCpuCapacity"`
    47  	CPUSockets              int    `json:"cpuSockets"`
    48  	TotalMemoryCapacity     int    `json:"totalMemoryCapacity"`
    49  	AvailableMemoryCapacity int    `json:"availableMemoryCapacity"`
    50  	CPUNum                  int    `json:"cpuNum"`
    51  	ZStackTime
    52  }
    53  
    54  func (region *SRegion) GetHosts(zoneId string, hostId string) ([]SHost, error) {
    55  	hosts := []SHost{}
    56  	params := url.Values{}
    57  	if len(zoneId) > 0 {
    58  		params.Add("q", "zone.uuid="+zoneId)
    59  	}
    60  	if len(hostId) > 0 {
    61  		params.Add("q", "uuid="+hostId)
    62  	}
    63  	if SkipEsxi {
    64  		params.Add("q", "hypervisorType!=ESX")
    65  	}
    66  	return hosts, region.client.listAll("hosts", params, &hosts)
    67  }
    68  
    69  func (region *SRegion) GetHost(hostId string) (*SHost, error) {
    70  	host := &SHost{}
    71  	err := region.client.getResource("hosts", hostId, host)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	zone, err := region.GetZone(host.ZoneUUID)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	host.zone = zone
    80  	return host, nil
    81  }
    82  
    83  func (host *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) {
    84  	wires, err := host.zone.region.GetWires(host.ZoneUUID, "", host.ClusterUUID)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	iwires := []cloudprovider.ICloudWire{}
    89  	for i := 0; i < len(wires); i++ {
    90  		iwires = append(iwires, &wires[i])
    91  	}
    92  	return iwires, nil
    93  }
    94  
    95  func (host *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
    96  	storages, err := host.zone.region.GetStorages(host.zone.UUID, host.ClusterUUID, "")
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	istorages := []cloudprovider.ICloudStorage{}
   101  	for i := 0; i < len(storages); i++ {
   102  		storages[i].region = host.zone.region
   103  		switch storages[i].Type {
   104  		case StorageTypeLocal:
   105  			localStorages, err := host.zone.region.getILocalStorages(storages[i].UUID, host.UUID)
   106  			if err != nil {
   107  				return nil, err
   108  			}
   109  			istorages = append(istorages, localStorages...)
   110  		case StorageTypeCeph:
   111  			istorages = append(istorages, &storages[i])
   112  		case StorageTypeVCenter:
   113  		}
   114  	}
   115  	return istorages, nil
   116  }
   117  
   118  func (host *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
   119  	return host.zone.GetIStorageById(id)
   120  }
   121  
   122  func (host *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
   123  	instances, err := host.zone.region.GetInstances(host.UUID, "", "")
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	iInstnace := []cloudprovider.ICloudVM{}
   128  	for i := 0; i < len(instances); i++ {
   129  		instances[i].host = host
   130  		iInstnace = append(iInstnace, &instances[i])
   131  	}
   132  	return iInstnace, nil
   133  }
   134  
   135  func (host *SHost) GetIVMById(instanceId string) (cloudprovider.ICloudVM, error) {
   136  	instance, err := host.zone.region.GetInstance(instanceId)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	instance.host = host
   141  	return instance, nil
   142  }
   143  
   144  func (host *SHost) GetId() string {
   145  	return host.UUID
   146  }
   147  
   148  func (host *SHost) GetName() string {
   149  	return host.Name
   150  }
   151  
   152  func (host *SHost) GetGlobalId() string {
   153  	return host.GetId()
   154  }
   155  
   156  func (host *SHost) IsEmulated() bool {
   157  	return false
   158  }
   159  
   160  func (host *SHost) GetStatus() string {
   161  	if host.Status == "Connected" {
   162  		return api.HOST_STATUS_RUNNING
   163  	}
   164  	return api.HOST_STATUS_UNKNOWN
   165  }
   166  
   167  func (host *SHost) Refresh() error {
   168  	return nil
   169  }
   170  
   171  func (host *SHost) GetHostStatus() string {
   172  	if host.Status == "Connected" {
   173  		return api.HOST_ONLINE
   174  	}
   175  	return api.HOST_OFFLINE
   176  }
   177  
   178  func (host *SHost) GetEnabled() bool {
   179  	return host.State == "Enabled"
   180  }
   181  
   182  func (host *SHost) GetAccessIp() string {
   183  	return host.ManagementIP
   184  }
   185  
   186  func (host *SHost) GetAccessMac() string {
   187  	return ""
   188  }
   189  
   190  func (host *SHost) GetSysInfo() jsonutils.JSONObject {
   191  	info := jsonutils.NewDict()
   192  	info.Add(jsonutils.NewString(CLOUD_PROVIDER_ZSTACK), "manufacture")
   193  	return info
   194  }
   195  
   196  func (host *SHost) GetSN() string {
   197  	return ""
   198  }
   199  
   200  func (host *SHost) GetReservedMemoryMb() int {
   201  	host.zone.fetchHostCmtbound()
   202  	return host.zone.reservedMemeoryMb
   203  }
   204  
   205  func (host *SHost) GetCpuCmtbound() float32 {
   206  	host.zone.fetchHostCmtbound()
   207  	return host.zone.cpuCmtbound
   208  }
   209  
   210  func (host *SHost) GetMemCmtbound() float32 {
   211  	host.zone.fetchHostCmtbound()
   212  	return host.zone.memCmtbound
   213  }
   214  
   215  func (host *SHost) GetCpuCount() int {
   216  	cpuCmtBound := host.GetCpuCmtbound()
   217  	if cpuCmtBound > 0 {
   218  		return int(float32(host.TotalCPUCapacity) / cpuCmtBound)
   219  	}
   220  	return host.TotalCPUCapacity
   221  }
   222  
   223  func (host *SHost) GetNodeCount() int8 {
   224  	return int8(host.CPUSockets)
   225  }
   226  
   227  func (host *SHost) GetCpuDesc() string {
   228  	return ""
   229  }
   230  
   231  func (host *SHost) GetCpuMhz() int {
   232  	return 0
   233  }
   234  
   235  func (host *SHost) GetMemSizeMB() int {
   236  	return host.TotalMemoryCapacity / 1024 / 1024
   237  }
   238  
   239  func (host *SHost) GetStorageSizeMB() int {
   240  	storages, err := host.zone.region.GetStorages(host.zone.UUID, host.ClusterUUID, "")
   241  	if err != nil {
   242  		return 0
   243  	}
   244  	totalStorage := 0
   245  	for _, storage := range storages {
   246  		if storage.Type == StorageTypeLocal {
   247  			localStorages, err := host.zone.region.GetLocalStorages(storage.UUID, host.UUID)
   248  			if err != nil {
   249  				return 0
   250  			}
   251  			for i := 0; i < len(localStorages); i++ {
   252  				totalStorage += int(localStorages[i].TotalCapacity)
   253  			}
   254  		}
   255  	}
   256  	return totalStorage / 1024 / 1024
   257  }
   258  
   259  func (host *SHost) GetStorageType() string {
   260  	return api.DISK_TYPE_HYBRID
   261  }
   262  
   263  func (host *SHost) GetHostType() string {
   264  	return api.HOST_TYPE_ZSTACK
   265  }
   266  
   267  func (region *SRegion) cleanDisks(diskIds []string) {
   268  	for i := 0; i < len(diskIds); i++ {
   269  		err := region.DeleteDisk(diskIds[i])
   270  		if err != nil {
   271  			log.Errorf("clean disk %s error: %v", diskIds[i], err)
   272  		}
   273  	}
   274  }
   275  
   276  func (region *SRegion) createDataDisks(disks []cloudprovider.SDiskInfo, hostId string) ([]string, error) {
   277  	diskIds := []string{}
   278  
   279  	storages, err := region.GetStorages("", "", "")
   280  	if err != nil {
   281  		return nil, errors.Wrapf(err, "createDataDisks.GetStorages")
   282  	}
   283  
   284  	localstorages := []SLocalStorage{}
   285  
   286  	for _, storage := range storages {
   287  		if storage.Type == StorageTypeLocal {
   288  			localstorage, _ := region.GetLocalStorage(storage.UUID, hostId)
   289  			if localstorage != nil {
   290  				localstorages = append(localstorages, *localstorage)
   291  			}
   292  		}
   293  	}
   294  
   295  	for i := 0; i < len(disks); i++ {
   296  		storageInfo := strings.Split(disks[i].StorageExternalId, "/")
   297  		if len(storageInfo) == 0 {
   298  			return diskIds, fmt.Errorf("invalidate storage externalId: %s", disks[i].StorageExternalId)
   299  		}
   300  		storage, err := region.GetStorage(storageInfo[0])
   301  		if err != nil {
   302  			return diskIds, errors.Wrapf(err, "createDataDisks")
   303  		}
   304  
   305  		switch storage.Type {
   306  		case StorageTypeCeph:
   307  			poolName := ""
   308  			for _, pool := range storage.Pools {
   309  				if pool.Type == CephPoolTypeData {
   310  					poolName = pool.PoolName
   311  				}
   312  			}
   313  			if len(poolName) == 0 {
   314  				return diskIds, fmt.Errorf("failed to found ceph data pool for storage %s to createDataDisk", storage.Name)
   315  			}
   316  			disk, err := region.CreateDisk(disks[i].Name, storage.UUID, "", poolName, disks[i].SizeGB, "")
   317  			if err != nil {
   318  				return diskIds, err
   319  			}
   320  			diskIds = append(diskIds, disk.UUID)
   321  		case StorageTypeLocal:
   322  			if len(localstorages) == 0 {
   323  				return nil, fmt.Errorf("No validate localstorage")
   324  			}
   325  			var disk *SDisk
   326  			var err error
   327  			for _, localstorage := range localstorages {
   328  				disk, err = region.CreateDisk(disks[i].Name, localstorage.primaryStorageID, hostId, "", disks[i].SizeGB, "")
   329  				if err != nil {
   330  					log.Warningf("createDataDisks error: %v", err)
   331  				} else {
   332  					diskIds = append(diskIds, disk.UUID)
   333  					break
   334  				}
   335  			}
   336  			if err != nil {
   337  				return diskIds, err
   338  			}
   339  		default:
   340  			return diskIds, fmt.Errorf("not support storageType %s", disks[i].StorageType)
   341  		}
   342  	}
   343  	return diskIds, nil
   344  }
   345  
   346  func (host *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
   347  	instance, err := host.zone.region._createVM(desc, host.ZoneUUID)
   348  	if err != nil {
   349  		return nil, errors.Wrapf(err, "host.zone.region._createVM")
   350  	}
   351  
   352  	diskIds, err := host.zone.region.createDataDisks(desc.DataDisks, instance.HostUUID)
   353  	if err != nil {
   354  		defer host.zone.region.cleanDisks(diskIds)
   355  		defer host.zone.region.DeleteVM(instance.UUID)
   356  		return nil, errors.Wrapf(err, "host.zone.region.createDataDisks")
   357  	}
   358  
   359  	err = host.zone.region.ResizeDisk(instance.RootVolumeUUID, int64(desc.SysDisk.SizeGB)*1024)
   360  	if err != nil {
   361  		log.Warningf("failed to resize system disk %s error: %v", instance.RootVolumeUUID, err)
   362  	}
   363  
   364  	for i := 0; i < len(diskIds); i++ {
   365  		err = host.zone.region.AttachDisk(instance.UUID, diskIds[i])
   366  		if err != nil {
   367  			log.Errorf("failed to attach disk %s into instance %s error: %v", diskIds[i], instance.Name, err)
   368  		}
   369  	}
   370  	err = host.zone.region.AssignSecurityGroup(instance.UUID, desc.ExternalSecgroupId)
   371  	if err != nil {
   372  		return nil, err
   373  	}
   374  	return host.GetIVMById(instance.UUID)
   375  }
   376  
   377  func (region *SRegion) _createVM(desc *cloudprovider.SManagedVMCreateConfig, zoneId string) (*SInstance, error) {
   378  	l3Id := strings.Split(desc.ExternalNetworkId, "/")[0]
   379  	if len(l3Id) == 0 {
   380  		return nil, fmt.Errorf("invalid networkid: %s", desc.ExternalNetworkId)
   381  	}
   382  	_, err := region.GetL3Network(l3Id)
   383  	if err != nil {
   384  		log.Errorf("failed to found l3network %s error: %v", l3Id, err)
   385  		return nil, err
   386  	}
   387  	offerings := map[string]string{}
   388  	if len(desc.InstanceType) > 0 {
   389  		offering, err := region.GetInstanceOfferingByType(desc.InstanceType)
   390  		if err != nil {
   391  			if errors.Cause(err) == cloudprovider.ErrNotFound {
   392  				offering, err = region.CreateInstanceOffering(desc.InstanceType, desc.Cpu, desc.MemoryMB, "UserVm")
   393  				if err != nil {
   394  					return nil, err
   395  				}
   396  			} else {
   397  				return nil, err
   398  			}
   399  		}
   400  		offerings[offering.Name] = offering.UUID
   401  	} else {
   402  		_offerings, err := region.GetInstanceOfferings("", "", desc.Cpu, desc.MemoryMB)
   403  		if err != nil {
   404  			return nil, err
   405  		}
   406  		for _, offering := range _offerings {
   407  			offerings[offering.Name] = offering.UUID
   408  		}
   409  		if len(offerings) == 0 {
   410  			return nil, fmt.Errorf("instance type %dC%dMB not avaiable", desc.Cpu, desc.MemoryMB)
   411  		}
   412  	}
   413  	return region.CreateInstance(desc, l3Id, zoneId, offerings)
   414  }
   415  
   416  func (region *SRegion) CreateInstance(desc *cloudprovider.SManagedVMCreateConfig, l3Id, zoneId string, offerings map[string]string) (*SInstance, error) {
   417  	instance := &SInstance{}
   418  	systemTags := []string{
   419  		"createWithoutCdRom::true",
   420  		"usbRedirect::false",
   421  		"vmConsoleMode::vnc",
   422  		"cleanTraffic::false",
   423  	}
   424  	if len(desc.IpAddr) > 0 {
   425  		systemTags = append(systemTags, fmt.Sprintf("staticIp::%s::%s", l3Id, desc.IpAddr))
   426  	}
   427  	if len(desc.UserData) > 0 {
   428  		systemTags = append(systemTags, "userdata::"+desc.UserData)
   429  	}
   430  	if len(desc.PublicKey) > 0 {
   431  		systemTags = append(systemTags, "sshkey::"+desc.PublicKey)
   432  	}
   433  	var err error
   434  	for offerName, offerId := range offerings {
   435  		params := map[string]interface{}{
   436  			"params": map[string]interface{}{
   437  				"name":                 desc.NameEn,
   438  				"description":          desc.Description,
   439  				"instanceOfferingUuid": offerId,
   440  				"imageUuid":            desc.ExternalImageId,
   441  				"l3NetworkUuids": []string{
   442  					l3Id,
   443  				},
   444  				"zoneUuid":              zoneId,
   445  				"dataVolumeSystemTags":  []string{},
   446  				"rootVolumeSystemTags":  []string{},
   447  				"vmMachineType":         "",
   448  				"tagUuids":              []string{},
   449  				"defaultL3NetworkUuid":  l3Id,
   450  				"dataDiskOfferingUuids": []string{},
   451  				"systemTags":            systemTags,
   452  				"vmNicConfig":           []string{},
   453  			},
   454  		}
   455  
   456  		log.Debugf("Try instanceOffering : %s", offerName)
   457  		err = region.client.create("vm-instances", jsonutils.Marshal(params), instance)
   458  		if err == nil {
   459  			return instance, nil
   460  		}
   461  		log.Errorf("create %s instance failed error: %v", offerName, err)
   462  	}
   463  	if err != nil {
   464  		return nil, err
   465  	}
   466  	return nil, fmt.Errorf("instance type %dC%dMB not avaiable", desc.Cpu, desc.MemoryMB)
   467  }
   468  
   469  func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
   470  	return nil, cloudprovider.ErrNotSupported
   471  }
   472  
   473  func (host *SHost) GetIsMaintenance() bool {
   474  	return false
   475  }
   476  
   477  func (host *SHost) GetVersion() string {
   478  	return ""
   479  }