mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
324 lines
9.2 KiB
Go
324 lines
9.2 KiB
Go
package guestdrivers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
|
"yunion.io/x/onecloud/pkg/cloudprovider"
|
|
"yunion.io/x/onecloud/pkg/compute/models"
|
|
"yunion.io/x/onecloud/pkg/compute/options"
|
|
"yunion.io/x/onecloud/pkg/httperrors"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
"yunion.io/x/onecloud/pkg/util/billing"
|
|
"yunion.io/x/pkg/util/compare"
|
|
)
|
|
|
|
type SOpenStackGuestDriver struct {
|
|
SManagedVirtualizedGuestDriver
|
|
}
|
|
|
|
func init() {
|
|
driver := SOpenStackGuestDriver{}
|
|
models.RegisterGuestDriver(&driver)
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) GetHypervisor() string {
|
|
return models.HYPERVISOR_OPENSTACK
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) IsSupportEip() bool {
|
|
return false
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) GetDefaultSysDiskBackend() string {
|
|
return models.STORAGE_OPENSTACK_ISCSI
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) GetMinimalSysDiskSizeGb() int {
|
|
return options.Options.DefaultDiskSizeMB / 1024
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) ChooseHostStorage(host *models.SHost, backend string) *models.SStorage {
|
|
storages := host.GetAttachedStorages("")
|
|
for i := 0; i < len(storages); i++ {
|
|
if storages[i].StorageType == backend {
|
|
return &storages[i]
|
|
}
|
|
}
|
|
for _, stype := range []string{models.STORAGE_OPENSTACK_ISCSI} {
|
|
for i := 0; i < len(storages); i++ {
|
|
if storages[i].StorageType == stype {
|
|
return &storages[i]
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
|
return []string{models.VM_READY, models.VM_RUNNING}, nil
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) GetAttachDiskStatus() ([]string, error) {
|
|
return []string{models.VM_READY, models.VM_RUNNING}, nil
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) GetRebuildRootStatus() ([]string, error) {
|
|
return []string{models.VM_READY, models.VM_RUNNING, models.VM_REBUILD_ROOT_FAIL}, nil
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) GetChangeConfigStatus() ([]string, error) {
|
|
return []string{models.VM_READY, models.VM_RUNNING}, nil
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) GetDeployStatus() ([]string, error) {
|
|
return []string{models.VM_RUNNING}, nil
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
|
data, err := self.SManagedVirtualizedGuestDriver.ValidateCreateData(ctx, userCred, data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if data.Contains("net.0") && data.Contains("net.1") {
|
|
return nil, httperrors.NewInputParameterError("cannot support more than 1 nic")
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
|
|
config, err := guest.GetDeployConfigOnHost(ctx, task.GetUserCred(), host, task.GetParams())
|
|
if err != nil {
|
|
log.Errorf("GetDeployConfigOnHost error: %v", err)
|
|
return err
|
|
}
|
|
log.Debugf("RequestDeployGuestOnHost: %s", config)
|
|
|
|
desc := cloudprovider.SManagedVMCreateConfig{}
|
|
if err := desc.GetConfig(config); err != nil {
|
|
return err
|
|
}
|
|
|
|
action, err := config.GetString("action")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ihost, err := host.GetIHost()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if action == "create" {
|
|
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
|
|
|
iVM, createErr := ihost.CreateVM(&desc)
|
|
if createErr != nil {
|
|
return nil, createErr
|
|
}
|
|
|
|
// 避免部署失败后,不能删除openstack平台机器
|
|
guest.SetExternalId(iVM.GetGlobalId())
|
|
|
|
log.Debugf("VMcreated %s, wait status running ...", iVM.GetGlobalId())
|
|
err = cloudprovider.WaitStatus(iVM, models.VM_RUNNING, time.Second*5, time.Second*1800)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
log.Debugf("VMcreated %s, and status is running", iVM.GetGlobalId())
|
|
|
|
iVM, err = ihost.GetIVMById(iVM.GetGlobalId())
|
|
if err != nil {
|
|
log.Errorf("cannot find vm %s", err)
|
|
return nil, err
|
|
}
|
|
|
|
err = cloudprovider.RetryUntil(func() (bool, error) {
|
|
idisks, err := iVM.GetIDisks()
|
|
if err != nil {
|
|
log.Errorf("cannot find vm disks %s", err)
|
|
return false, err
|
|
}
|
|
if len(idisks) == len(desc.DataDisks)+1 {
|
|
return true, nil
|
|
} else {
|
|
return false, nil
|
|
}
|
|
}, 10)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data := fetchIVMinfo(desc, iVM, guest.Id, "root", desc.Password, action)
|
|
return data, nil
|
|
})
|
|
} else if action == "deploy" {
|
|
iVM, err := ihost.GetIVMById(guest.GetExternalId())
|
|
if err != nil || iVM == nil {
|
|
log.Errorf("cannot find vm %s", err)
|
|
return fmt.Errorf("cannot find vm")
|
|
}
|
|
|
|
params := task.GetParams()
|
|
log.Debugf("Deploy VM params %s", params.String())
|
|
|
|
deleteKeypair := jsonutils.QueryBoolean(params, "__delete_keypair__", false)
|
|
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
|
|
|
err := iVM.DeployVM(ctx, desc.Name, desc.Password, desc.PublicKey, deleteKeypair, desc.Description)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data := fetchIVMinfo(desc, iVM, guest.Id, "root", desc.Password, action)
|
|
return data, nil
|
|
})
|
|
} else if action == "rebuild" {
|
|
|
|
iVM, err := ihost.GetIVMById(guest.GetExternalId())
|
|
if err != nil || iVM == nil {
|
|
log.Errorf("cannot find vm %s", err)
|
|
return fmt.Errorf("cannot find vm")
|
|
}
|
|
|
|
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
|
|
|
diskId, err := iVM.RebuildRoot(ctx, desc.ExternalImageId, desc.Password, desc.PublicKey, desc.SysDisk.SizeGB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
log.Debugf("VMrebuildRoot %s new diskID %s, wait status ready ...", iVM.GetGlobalId(), diskId)
|
|
|
|
err = cloudprovider.WaitStatus(iVM, models.VM_READY, time.Second*5, time.Second*1800)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
log.Debugf("VMrebuildRoot %s, and status is ready", iVM.GetGlobalId())
|
|
|
|
maxWaitSecs := 300
|
|
waited := 0
|
|
|
|
for {
|
|
// hack, wait disk number consistent
|
|
idisks, err := iVM.GetIDisks()
|
|
if err != nil {
|
|
log.Errorf("fail to find VM idisks %s", err)
|
|
return nil, err
|
|
}
|
|
if len(idisks) < len(desc.DataDisks)+1 {
|
|
if waited > maxWaitSecs {
|
|
log.Errorf("inconsistent disk number, wait timeout, must be something wrong on remote")
|
|
return nil, cloudprovider.ErrTimeout
|
|
}
|
|
log.Debugf("inconsistent disk number???? %d != %d", len(idisks), len(desc.DataDisks)+1)
|
|
time.Sleep(time.Second * 5)
|
|
waited += 5
|
|
} else {
|
|
if idisks[0].GetGlobalId() != diskId {
|
|
log.Errorf("system disk id inconsistent %s != %s", idisks[0].GetGlobalId(), diskId)
|
|
return nil, fmt.Errorf("inconsistent sys disk id after rebuild root")
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
data := fetchIVMinfo(desc, iVM, guest.Id, "root", desc.Password, action)
|
|
|
|
return data, nil
|
|
})
|
|
|
|
} else {
|
|
log.Errorf("RequestDeployGuestOnHost: Action %s not supported", action)
|
|
return fmt.Errorf("Action %s not supported", action)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
|
|
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
|
ihost, err := host.GetIHost()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
iVM, err := ihost.GetIVMById(guest.ExternalId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if fwOnly, _ := task.GetParams().Bool("fw_only"); fwOnly {
|
|
iregion, err := host.GetIRegion()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
secgroups := guest.GetSecgroups()
|
|
externalIds := []string{}
|
|
for _, secgroup := range secgroups {
|
|
|
|
lockman.LockRawObject(ctx, "secgroupcache", fmt.Sprintf("%s-normal", guest.SecgrpId))
|
|
defer lockman.ReleaseRawObject(ctx, "secgroupcache", fmt.Sprintf("%s-normal", guest.SecgrpId))
|
|
|
|
secgroupCache := models.SecurityGroupCacheManager.Register(ctx, task.GetUserCred(), secgroup.Id, "normal", host.GetRegion().Id, host.ManagerId)
|
|
if secgroupCache == nil {
|
|
return nil, fmt.Errorf("failed to registor secgroupCache for secgroup: %s", secgroup.Id)
|
|
}
|
|
extID, err := iregion.SyncSecurityGroup(secgroupCache.ExternalId, "", secgroup.Name, secgroup.Description, secgroup.GetSecRules(""))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = secgroupCache.SetExternalId(extID); err != nil {
|
|
return nil, err
|
|
}
|
|
externalIds = append(externalIds, extID)
|
|
}
|
|
return nil, iVM.SetSecurityGroups(externalIds)
|
|
}
|
|
|
|
iDisks, err := iVM.GetIDisks()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
disks := make([]models.SDisk, 0)
|
|
for _, guestdisk := range guest.GetDisks() {
|
|
disk := guestdisk.GetDisk()
|
|
disks = append(disks, *disk)
|
|
}
|
|
|
|
added := make([]models.SDisk, 0)
|
|
commondb := make([]models.SDisk, 0)
|
|
commonext := make([]cloudprovider.ICloudDisk, 0)
|
|
removed := make([]cloudprovider.ICloudDisk, 0)
|
|
|
|
if err := compare.CompareSets(disks, iDisks, &added, &commondb, &commonext, &removed); err != nil {
|
|
return nil, err
|
|
}
|
|
for _, disk := range removed {
|
|
if err := iVM.DetachDisk(ctx, disk.GetId()); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
for _, disk := range added {
|
|
if err := iVM.AttachDisk(ctx, disk.ExternalId); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return nil, nil
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) AllowReconfigGuest() bool {
|
|
return true
|
|
}
|
|
|
|
func (self *SOpenStackGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle) bool {
|
|
return false
|
|
}
|