| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package guestdrivers
- import (
- "context"
- "fmt"
- "math"
- "strings"
- "sync"
- "time"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/gotypes"
- "yunion.io/x/pkg/util/billing"
- "yunion.io/x/pkg/util/pinyinutils"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/sqlchemy"
- billing_api "yunion.io/x/onecloud/pkg/apis/billing"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- image_api "yunion.io/x/onecloud/pkg/apis/image"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
- "yunion.io/x/onecloud/pkg/cloudcommon/validators"
- "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/mcclient/auth"
- img "yunion.io/x/onecloud/pkg/mcclient/modules/image"
- "yunion.io/x/onecloud/pkg/util/logclient"
- )
- type SManagedVirtualizedGuestDriver struct {
- SVirtualizedGuestDriver
- }
- func (d SManagedVirtualizedGuestDriver) DoScheduleCPUFilter() bool { return false }
- func (d SManagedVirtualizedGuestDriver) DoScheduleSKUFilter() bool { return true }
- func (d SManagedVirtualizedGuestDriver) DoScheduleMemoryFilter() bool { return false }
- func (d SManagedVirtualizedGuestDriver) DoScheduleStorageFilter() bool { return false }
- func (d SManagedVirtualizedGuestDriver) DoScheduleCloudproviderTagFilter() bool { return true }
- func (drv *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
- driver, err := guest.GetDriver()
- if err != nil {
- return nil, err
- }
- config := cloudprovider.SManagedVMCreateConfig{
- IsNeedInjectPasswordByCloudInit: driver.IsNeedInjectPasswordByCloudInit(),
- UserDataType: driver.GetUserDataType(),
- WindowsUserDataType: driver.GetWindowsUserDataType(),
- IsWindowsUserDataTypeNeedEncode: driver.IsWindowsUserDataTypeNeedEncode(),
- }
- config.Name = guest.Name
- config.NameEn = pinyinutils.Text2Pinyin(guest.Name)
- config.Hostname = guest.Hostname
- config.Cpu = int(guest.VcpuCount)
- config.MemoryMB = guest.VmemSize
- config.UserData = guest.GetUserData(ctx, userCred)
- config.Description = guest.Description
- config.EnableMonitorAgent = options.Options.EnableMonitorAgent
- if params != nil {
- params.Unmarshal(&config.SPublicIpInfo)
- }
- config.InstanceType = guest.InstanceType
- if len(guest.KeypairId) > 0 {
- keypair := guest.GetKeypair()
- if keypair != nil {
- config.PublicKey = keypair.PublicKey
- config.KeypairName = keypair.Name
- }
- }
- nics, _ := guest.GetNetworks("")
- if len(nics) > 0 {
- net, err := nics[0].GetNetwork()
- if err != nil {
- return nil, errors.Wrapf(err, "GetNetwork")
- }
- config.ExternalNetworkId = net.ExternalId
- vpc, err := net.GetVpc()
- if err == nil {
- config.ExternalVpcId = vpc.ExternalId
- }
- config.IpAddr = nics[0].IpAddr
- }
- provider := host.GetCloudprovider()
- config.ProjectId, err = provider.SyncProject(ctx, userCred, guest.ProjectId)
- if err != nil {
- if errors.Cause(err) != cloudprovider.ErrNotSupported && errors.Cause(err) != cloudprovider.ErrNotImplemented {
- logclient.AddSimpleActionLog(guest, logclient.ACT_SYNC_CLOUD_PROJECT, err, userCred, false)
- }
- }
- disks, err := guest.GetDisks()
- if err != nil {
- return nil, errors.Wrapf(err, "GetDisks")
- }
- config.DataDisks = []cloudprovider.SDiskInfo{}
- for i := 0; i < len(disks); i += 1 {
- disk := disks[i]
- storage, _ := disk.GetStorage()
- if i == 0 {
- config.SysDisk.Name = disk.Name
- config.SysDisk.StorageExternalId = storage.ExternalId
- config.SysDisk.StorageType = storage.StorageType
- config.SysDisk.SizeGB = int(math.Ceil(float64(disk.DiskSize) / 1024))
- config.SysDisk.Iops = disk.Iops
- config.SysDisk.Throughput = disk.Throughput
- if gds, err := disk.GetGuestDisk(); err == nil {
- config.SysDisk.Driver = gds.Driver
- config.SysDisk.CacheMode = gds.CacheMode
- }
- cache := storage.GetStoragecache()
- imageId := disk.GetTemplateId()
- //避免因同步过来的instance没有对应的imagecache信息,重置密码时引发空指针访问
- if len(imageId) == 0 {
- if cdrom := guest.GetCdrom(); cdrom != nil {
- imageId = cdrom.ImageId
- }
- }
- if scimg := models.StoragecachedimageManager.GetStoragecachedimage(cache.Id, imageId); scimg != nil {
- config.ExternalImageId = scimg.ExternalId
- img := scimg.GetCachedimage()
- config.OsDistribution, _ = img.Info.GetString("properties", "os_distribution")
- config.OsVersion, _ = img.Info.GetString("properties", "os_version")
- config.OsType, _ = img.Info.GetString("properties", "os_type")
- config.ImageType = img.ImageType
- }
- } else {
- dataDisk := cloudprovider.SDiskInfo{
- SizeGB: disk.DiskSize / 1024,
- StorageType: storage.StorageType,
- StorageExternalId: storage.ExternalId,
- Iops: disk.Iops,
- Throughput: disk.Throughput,
- Name: disk.Name,
- }
- if gds, err := disk.GetGuestDisk(); err == nil {
- dataDisk.Driver = gds.Driver
- dataDisk.CacheMode = gds.CacheMode
- }
- config.DataDisks = append(config.DataDisks, dataDisk)
- }
- }
- if len(config.ExternalImageId) == 0 {
- image, err := func() (*models.SCachedimage, error) {
- cdrom := guest.GetCdrom()
- if cdrom == nil {
- return nil, fmt.Errorf("empty cdrom")
- }
- return cdrom.GetImage()
- }()
- if err != nil {
- log.Errorf("get cachdimage error: %v", err)
- } else {
- config.ExternalImageId = image.ExternalId
- config.OsDistribution, _ = image.Info.GetString("properties", "os_distribution")
- config.OsVersion, _ = image.Info.GetString("properties", "os_version")
- config.OsType, _ = image.Info.GetString("properties", "os_type")
- config.ImageType = image.ImageType
- }
- }
- // 避免因同步包年包月实例billing_cycle失败,导致重置虚拟机密码异常
- if guest.BillingType == billing_api.BILLING_TYPE_PREPAID && len(guest.BillingCycle) > 0 {
- bc, err := billing.ParseBillingCycle(guest.BillingCycle)
- if err != nil {
- return nil, errors.Wrapf(err, "ParseBillingCycle(%s)", guest.BillingCycle)
- }
- if bc.IsValid() {
- bc.AutoRenew = guest.AutoRenew
- config.BillingCycle = &bc
- }
- }
- return jsonutils.Marshal(&config), nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestSaveImage(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, task taskman.ITask) error {
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- iVm, err := guest.GetIVM(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "guest.GetIVM")
- }
- input := &api.DiskSaveInput{}
- err = task.GetParams().Unmarshal(input)
- if err != nil {
- return nil, errors.Wrapf(err, "Unmarshal")
- }
- opts := &cloudprovider.SaveImageOptions{
- Name: input.Name,
- }
- image, err := iVm.SaveImage(opts)
- if err != nil {
- return nil, errors.Wrapf(err, "iVm.SaveImage")
- }
- err = cloudprovider.WaitStatus(image, cloudprovider.IMAGE_STATUS_ACTIVE, time.Second*10, time.Minute*10)
- if err != nil {
- return nil, errors.Wrapf(err, "wait image %s(%s) active current is: %s", image.GetName(), image.GetGlobalId(), image.GetStatus())
- }
- if options.Options.SaveCloudImageToGlance {
- exports, err := image.Export(&cloudprovider.SImageExportOptions{})
- if err != nil {
- if errors.Cause(err) != cloudprovider.ErrNotImplemented && errors.Cause(err) != cloudprovider.ErrNotSupported {
- logclient.AddSimpleActionLog(guest, logclient.ACT_SAVE_IMAGE, errors.Wrapf(err, "Export"), userCred, false)
- }
- }
- osProfile := guest.GetOSProfile()
- s := auth.GetSession(ctx, userCred, options.Options.Region)
- var wg sync.WaitGroup
- for _, export := range exports {
- wg.Add(1)
- go func(export cloudprovider.SImageExportInfo) {
- defer wg.Done()
- params := map[string]string{
- "name": export.Name,
- "os_type": osProfile.OSType,
- "copy_from": export.DownloadUrl,
- "compress_format": export.CompressFormat,
- }
- localImage, err := img.Images.Create(s, jsonutils.Marshal(params))
- if err != nil {
- log.Errorf("create image error: %v", err)
- return
- }
- imageId, err := localImage.GetString("id")
- if err != nil {
- return
- }
- info := &image_api.ImageDetails{}
- for {
- localImage, err = img.Images.Get(s, imageId, jsonutils.Marshal(map[string]string{"scope": "system"}))
- if err != nil {
- break
- }
- localImage.Unmarshal(info)
- log.Debugf("save image %s(%s) status: %s", info.Name, info.Id, info.Status)
- if utils.IsInStringArray(info.Status, []string{
- image_api.IMAGE_STATUS_ACTIVE,
- image_api.IMAGE_STATUS_KILLED,
- image_api.IMAGE_STATUS_SAVE_FAIL,
- }) {
- break
- }
- time.Sleep(time.Second * 20)
- }
- }(export)
- }
- wg.Wait()
- }
- host, err := guest.GetHost()
- if err != nil {
- return nil, errors.Wrapf(err, "GetHost")
- }
- region, err := host.GetRegion()
- if err != nil {
- return nil, errors.Wrapf(err, "GetRegion")
- }
- iRegion, err := host.GetIRegion(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "host.GetIRegion")
- }
- caches, err := region.GetStoragecaches()
- if err != nil {
- return nil, errors.Wrapf(err, "region.GetStoragecaches")
- }
- for i := range caches {
- if caches[i].ManagerId == host.ManagerId {
- iStoragecache, err := iRegion.GetIStoragecacheById(caches[i].ExternalId)
- if err != nil {
- return nil, errors.Wrapf(err, "iRegion.GetIStoragecacheById(%s)", caches[i].ExternalId)
- }
- result := caches[i].SyncCloudImages(ctx, userCred, iStoragecache, region, true)
- log.Infof("sync cloud image for storagecache %s result: %s", caches[i].Name, result.Result())
- }
- }
- return nil, nil
- })
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestGuestCreateAllDisks(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
- diskCat := guest.CategorizeDisks()
- var imageId string
- if diskCat.Root != nil {
- imageId = diskCat.Root.GetTemplateId()
- }
- if len(imageId) == 0 {
- task.ScheduleRun(nil)
- return nil
- }
- storage, _ := diskCat.Root.GetStorage()
- if storage == nil {
- return fmt.Errorf("no valid storage")
- }
- storageCache := storage.GetStoragecache()
- if storageCache == nil {
- return fmt.Errorf("no valid storage cache")
- }
- input := api.CacheImageInput{
- ImageId: imageId,
- Format: diskCat.Root.DiskFormat,
- ParentTaskId: task.GetTaskId(),
- ServerId: guest.Id,
- }
- return storageCache.StartImageCacheTask(ctx, task.GetUserCred(), input)
- }
- func (drv *SManagedVirtualizedGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput) (*api.ServerCreateInput, error) {
- if input.Cdrom != "" {
- return nil, httperrors.NewInputParameterError("%s not support cdrom params", input.Hypervisor)
- }
- var vpc *models.SVpc = nil
- for _, network := range input.Networks {
- netObj, err := validators.ValidateModel(ctx, userCred, models.NetworkManager, &network.Network)
- if err == nil {
- net := netObj.(*models.SNetwork)
- vpc, err = net.GetVpc()
- if err != nil {
- return nil, errors.Wrapf(err, "GetVpc")
- }
- }
- }
- for i := range input.Secgroups {
- if input.Secgroups[i] == api.SECGROUP_DEFAULT_ID {
- continue
- }
- if gotypes.IsNil(vpc) {
- return nil, httperrors.NewMissingParameterError("nets")
- }
- secObj, err := validators.ValidateModel(ctx, userCred, models.SecurityGroupManager, &input.Secgroups[i])
- if err != nil {
- return nil, err
- }
- secgroup := secObj.(*models.SSecurityGroup)
- err = vpc.CheckSecurityGroupConsistent(secgroup)
- if err != nil {
- return nil, err
- }
- }
- return input, nil
- }
- func (drv *SManagedVirtualizedGuestDriver) ValidateCreateEip(ctx context.Context, userCred mcclient.TokenCredential, input api.ServerCreateEipInput) error {
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestDetachDisk(ctx context.Context, guest *models.SGuest, disk *models.SDisk, task taskman.ITask) error {
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- iVM, err := guest.GetIVM(ctx)
- if err != nil {
- //若guest被删除,忽略错误,否则会无限删除guest失败(有挂载的云盘)
- if errors.Cause(err) == cloudprovider.ErrNotFound {
- return nil, nil
- }
- return nil, errors.Wrapf(err, "guest.GetIVM")
- }
- if len(disk.ExternalId) == 0 {
- return nil, nil
- }
- _, err = disk.GetIDisk(ctx)
- if errors.Cause(err) == cloudprovider.ErrNotFound {
- //忽略云上磁盘已经被删除错误
- return nil, nil
- }
- err = iVM.DetachDisk(ctx, disk.ExternalId)
- if err != nil {
- return nil, errors.Wrapf(err, "iVM.DetachDisk")
- }
- err = cloudprovider.Wait(time.Second*5, time.Minute*3, func() (bool, error) {
- err := iVM.Refresh()
- if err != nil {
- return false, errors.Wrapf(err, "iVM.Refresh")
- }
- iDisks, err := iVM.GetIDisks()
- if err != nil {
- return false, errors.Wrapf(err, "RequestDetachDisk.iVM.GetIDisks")
- }
- exist := false
- for i := 0; i < len(iDisks); i++ {
- if iDisks[i].GetGlobalId() == disk.ExternalId {
- exist = true
- }
- }
- if !exist {
- return true, nil
- }
- return false, nil
- })
- if err != nil {
- return nil, errors.Wrapf(err, "RequestDetachDisk.Wait")
- }
- return nil, nil
- })
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestAttachDisk(ctx context.Context, guest *models.SGuest, disk *models.SDisk, task taskman.ITask) error {
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- iVM, err := guest.GetIVM(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "guest.GetIVM")
- }
- if len(disk.ExternalId) == 0 {
- return nil, fmt.Errorf("disk %s(%s) is not a managed resource", disk.Name, disk.Id)
- }
- err = iVM.AttachDisk(ctx, disk.ExternalId)
- if err != nil {
- return nil, errors.Wrapf(err, "iVM.AttachDisk")
- }
- err = cloudprovider.Wait(time.Second*10, time.Minute*6, func() (bool, error) {
- err := iVM.Refresh()
- if err != nil {
- return false, errors.Wrapf(err, "iVM.Refresh")
- }
- iDisks, err := iVM.GetIDisks()
- if err != nil {
- return false, errors.Wrapf(err, "RequestAttachDisk.iVM.GetIDisks")
- }
- for i := 0; i < len(iDisks); i++ {
- if iDisks[i].GetGlobalId() == disk.ExternalId {
- err := cloudprovider.WaitStatus(iDisks[i], api.DISK_READY, 5*time.Second, 60*time.Second)
- if err != nil {
- return false, errors.Wrapf(err, "RequestAttachDisk.iVM.WaitStatus")
- }
- if device := iDisks[i].GetDeviceName(); len(device) > 0 {
- db.Update(disk, func() error {
- disk.Device = device
- return nil
- })
- }
- return true, nil
- }
- }
- return false, nil
- })
- if err != nil {
- return nil, errors.Wrapf(err, "RequestAttachDisk.Wait")
- }
- return nil, nil
- })
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestStartOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) error {
- ivm, err := guest.GetIVM(ctx)
- if err != nil {
- return errors.Wrapf(err, "GetIVM")
- }
- result := jsonutils.NewDict()
- if ivm.GetStatus() != api.VM_RUNNING {
- if guest.BillingType == billing_api.BILLING_TYPE_POSTPAID && guest.ShutdownMode != api.VM_SHUTDOWN_MODE_STOP_CHARGING && jsonutils.QueryBoolean(task.GetParams(), "auto_prepaid", false) {
- err = ivm.ChangeBillingType(string(billing_api.BILLING_TYPE_PREPAID))
- if err != nil && errors.Cause(err) != cloudprovider.ErrNotImplemented {
- logclient.AddSimpleActionLog(guest, logclient.ACT_CHANGE_BILLING_TYPE, errors.Wrapf(err, string(billing_api.BILLING_TYPE_PREPAID)), userCred, false)
- }
- }
- err := ivm.StartVM(ctx)
- if err != nil {
- return errors.Wrapf(err, "StartVM")
- }
- err = cloudprovider.WaitStatus(ivm, api.VM_RUNNING, time.Second*5, time.Minute*10)
- if err != nil {
- return errors.Wrapf(err, "Wait vm running")
- }
- if guest.BillingType == billing_api.BILLING_TYPE_POSTPAID && guest.ShutdownMode == api.VM_SHUTDOWN_MODE_STOP_CHARGING && jsonutils.QueryBoolean(task.GetParams(), "auto_prepaid", false) {
- err := ivm.ChangeBillingType(string(billing_api.BILLING_TYPE_PREPAID))
- if err != nil && errors.Cause(err) != cloudprovider.ErrNotImplemented {
- logclient.AddSimpleActionLog(guest, logclient.ACT_CHANGE_BILLING_TYPE, errors.Wrapf(err, string(billing_api.BILLING_TYPE_PREPAID)), userCred, false)
- }
- }
- // 虚拟机开机,公网ip自动生成
- guest.SyncAllWithCloudVM(ctx, userCred, host, ivm, true)
- return task.ScheduleRun(result)
- }
- return guest.SetStatus(ctx, userCred, api.VM_RUNNING, "StartOnHost")
- }
- func (drv *SManagedVirtualizedGuestDriver) 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 {
- return errors.Wrapf(err, "GetDeployConfigOnHost")
- }
- log.Debugf("RequestDeployGuestOnHost: %s", config)
- desc := cloudprovider.SManagedVMCreateConfig{}
- desc.Description = guest.Description
- // 账号必须在desc.GetConfig()之前设置,避免默认用户不能正常注入
- osInfo := struct {
- OsType string
- OsDistribution string
- ImageType string
- }{}
- config.Unmarshal(&osInfo, "desc")
- driver, err := guest.GetDriver()
- if err != nil {
- return err
- }
- desc.Account = driver.GetDefaultAccount(osInfo.OsType, osInfo.OsDistribution, osInfo.ImageType)
- err = desc.GetConfig(config)
- if err != nil {
- return errors.Wrapf(err, "desc.GetConfig")
- }
- desc.Tags, _ = guest.GetAllUserMetadata()
- desc.UserData, err = desc.GetUserData()
- if err != nil {
- return errors.Wrapf(err, "GetUserData")
- }
- action, err := config.GetString("action")
- if err != nil {
- return err
- }
- ihost, err := host.GetIHost(ctx)
- if err != nil {
- return err
- }
- region, err := host.GetRegion()
- if err != nil {
- return errors.Wrapf(err, "GetRegion")
- }
- switch action {
- case "create":
- if len(desc.InstanceType) == 0 && region != nil && utils.IsInStringArray(region.Provider, api.PUBLIC_CLOUD_PROVIDERS) {
- sku, err := models.ServerSkuManager.GetMatchedSku(region.GetId(), int64(desc.Cpu), int64(desc.MemoryMB))
- if err != nil {
- return errors.Wrap(err, "ManagedVirtualizedGuestDriver.RequestDeployGuestOnHost.GetMatchedSku")
- }
- if sku == nil {
- return errors.Wrap(errors.ErrNotFound, "ManagedVirtualizedGuestDriver.RequestDeployGuestOnHost.GetMatchedSku")
- }
- desc.InstanceType = sku.Name
- }
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- return driver.RemoteDeployGuestForCreate(ctx, task.GetUserCred(), guest, host, desc)
- })
- case "deploy":
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- return driver.RemoteDeployGuestForDeploy(ctx, guest, ihost, task, desc)
- })
- case "rebuild":
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- return driver.RemoteDeployGuestForRebuildRoot(ctx, guest, ihost, task, desc)
- })
- default:
- log.Errorf("RequestDeployGuestOnHost: Action %s not supported", action)
- return fmt.Errorf("Action %s not supported", action)
- }
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) GetGuestInitialStateAfterCreate() string {
- return api.VM_READY
- }
- func (drv *SManagedVirtualizedGuestDriver) GetGuestInitialStateAfterRebuild() string {
- return api.VM_READY
- }
- func (drv *SManagedVirtualizedGuestDriver) RemoteDeployGuestForCreate(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error) {
- ihost, err := host.GetIHost(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "RemoteDeployGuestForCreate.GetIHost")
- }
- secgroups, err := guest.GetSecgroups()
- if err != nil {
- return nil, errors.Wrap(err, "GetSecgroups")
- }
- desc.ExternalSecgroupIds = []string{}
- for _, secgroup := range secgroups {
- if len(secgroup.ExternalId) > 0 {
- desc.ExternalSecgroupIds = append(desc.ExternalSecgroupIds, secgroup.ExternalId)
- }
- }
- devs, err := guest.GetIsolatedDevices()
- if err != nil {
- return nil, errors.Wrapf(err, "GetIsolatedDevices")
- }
- desc.IsolateDevices = []cloudprovider.SIsolateDevice{}
- for _, dev := range devs {
- desc.IsolateDevices = append(desc.IsolateDevices, cloudprovider.SIsolateDevice{
- Id: dev.ExternalId,
- Name: dev.Name,
- })
- }
- desc.KsyunPostpaidChargeType = options.Options.KsyunPostpaidChargeType
- var iVM cloudprovider.ICloudVM = nil
- iVM, err = func() (cloudprovider.ICloudVM, error) {
- lockman.LockObject(ctx, guest)
- defer lockman.ReleaseObject(ctx, guest)
- tryCnt := 0
- iVM, err = func() (cloudprovider.ICloudVM, error) {
- iVM, err = ihost.CreateVM(&desc)
- if err == nil || !options.Options.EnableAutoSwitchServerSku {
- return iVM, err
- }
- if errors.Cause(err) != cloudprovider.ErrInvalidSku {
- return iVM, err
- }
- skus, e := models.ServerSkuManager.GetSkus(host.GetProviderName(), guest.VcpuCount, guest.VmemSize)
- if e != nil {
- return iVM, errors.Wrapf(e, "GetSkus")
- }
- oldSku := desc.InstanceType
- for i := range skus {
- if skus[i].Name != oldSku && len(skus[i].Name) > 0 {
- desc.InstanceType = skus[i].Name
- log.Infof("try switch server sku from %s to %s for create %s", oldSku, desc.InstanceType, guest.Name)
- iVM, err = ihost.CreateVM(&desc)
- if err == nil {
- db.Update(guest, func() error {
- guest.InstanceType = desc.InstanceType
- return nil
- })
- return iVM, nil
- }
- log.Errorf("use sku %s error: %v", desc.InstanceType, err)
- tryCnt++
- }
- }
- return iVM, err
- }()
- if err != nil {
- return nil, errors.Wrapf(err, "After try %d skus", tryCnt)
- }
- db.SetExternalId(guest, userCred, iVM.GetGlobalId())
- return iVM, nil
- }()
- if err != nil {
- return nil, err
- }
- driver, err := guest.GetDriver()
- if err != nil {
- return nil, err
- }
- // iVM 实际所在的ihost 可能和 调度选择的host不是同一个,此处根据iVM实际所在host,重新同步
- ihost, err = driver.RemoteDeployGuestSyncHost(ctx, userCred, guest, host, iVM)
- if err != nil {
- return nil, errors.Wrap(err, "RemoteDeployGuestSyncHost")
- }
- vmId := iVM.GetGlobalId()
- initialState := driver.GetGuestInitialStateAfterCreate()
- log.Debugf("VMcreated %s, wait status %s ...", vmId, initialState)
- err = cloudprovider.WaitStatusWithInstanceErrorCheck(iVM, initialState, time.Second*5, time.Second*1800, func() error {
- return iVM.GetError()
- })
- if err != nil {
- return nil, err
- }
- log.Debugf("VMcreated %s, and status is running", vmId)
- iVM, err = ihost.GetIVMById(vmId)
- if err != nil {
- return nil, errors.Wrapf(err, "GetIVMById(%s)", vmId)
- }
- if driver.GetMaxSecurityGroupCount() > 0 {
- err = iVM.SetSecurityGroups(desc.ExternalSecgroupIds)
- if err != nil {
- return nil, errors.Wrapf(err, "SetSecurityGroups")
- }
- }
- ret, expect, eipSync := 0, len(desc.DataDisks)+1, false
- err = cloudprovider.RetryUntil(func() (bool, error) {
- // 虚拟机启动后才分配静态公网IP,否则取不到public ip
- if desc.PublicIpBw > 0 && initialState == api.VM_RUNNING {
- eip, _ := iVM.GetIEIP()
- if eip == nil {
- iVM.Refresh()
- return false, nil
- }
- // 同步静态公网ip
- if !eipSync {
- provider := host.GetCloudprovider()
- guest.SyncVMEip(ctx, userCred, provider, eip, provider.GetOwnerId())
- eipSync = true
- }
- }
- idisks, err := iVM.GetIDisks()
- if err != nil {
- return false, errors.Wrap(err, "iVM.GetIDisks")
- }
- ret = len(idisks)
- log.Debugf("wait vm disk ready, expect %d disks, return %d disks", expect, ret)
- if ret >= expect { // 有可能自定义镜像里面也有磁盘,会导致返回的磁盘多于创建时的磁盘
- return true, nil
- }
- err = iVM.Refresh()
- if err != nil {
- log.Warningf("refresh vm %s error: %v", guest.Name, err)
- }
- return false, nil
- }, 10)
- if err != nil {
- return nil, errors.Wrapf(err, "GuestDriver.RemoteDeployGuestForCreate.RetryUntil expect %d disks return %d disks", expect, ret)
- }
- // 回填IP
- if len(desc.IpAddr) == 0 {
- nics, _ := iVM.GetINics()
- gns, _ := guest.GetNetworks("")
- if len(nics) > 0 && len(gns) > 0 {
- db.Update(&gns[0], func() error {
- gns[0].IpAddr = nics[0].GetIP()
- gns[0].MacAddr = nics[0].GetMAC()
- gns[0].Driver = nics[0].GetDriver()
- return nil
- })
- }
- }
- driver.RemoteActionAfterGuestCreated(ctx, userCred, guest, host, iVM, &desc)
- data := fetchIVMinfo(desc, iVM, guest.Id, desc.Account, desc.Password, desc.PublicKey, "create")
- return data, nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RemoteDeployGuestSyncHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, iVM cloudprovider.ICloudVM) (cloudprovider.ICloudHost, error) {
- if hostId := iVM.GetIHostId(); len(hostId) > 0 {
- nh, err := db.FetchByExternalIdAndManagerId(models.HostManager, hostId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
- return q.Equals("manager_id", host.ManagerId)
- })
- if err != nil {
- log.Warningf("failed to found new hostId(%s) for ivm %s(%s) error: %v", hostId, guest.Name, guest.Id, err)
- } else if nh.GetId() != guest.HostId {
- guest.OnScheduleToHost(ctx, userCred, nh.GetId())
- host = nh.(*models.SHost)
- }
- }
- return host.GetIHost(ctx)
- }
- func (drv *SManagedVirtualizedGuestDriver) RemoteDeployGuestForDeploy(ctx context.Context, guest *models.SGuest, ihost cloudprovider.ICloudHost, task taskman.ITask, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error) {
- iVM, err := ihost.GetIVMById(guest.GetExternalId())
- if err != nil || iVM == nil {
- log.Errorf("cannot find vm %s", err)
- return nil, fmt.Errorf("cannot find vm")
- }
- params := task.GetParams()
- log.Debugf("Deploy VM params %s", params.String())
- opts := &cloudprovider.SInstanceDeployOptions{
- Username: desc.Account,
- PublicKey: desc.PublicKey,
- KeypairName: desc.KeypairName,
- Password: desc.Password,
- UserData: desc.UserData,
- }
- opts.DeleteKeypair = jsonutils.QueryBoolean(params, "__delete_keypair__", false)
- if len(desc.UserData) > 0 {
- err := iVM.UpdateUserData(desc.UserData)
- if err != nil {
- log.Errorf("update userdata fail %s", err)
- }
- }
- err = func() error {
- lockman.LockObject(ctx, guest)
- defer lockman.ReleaseObject(ctx, guest)
- // 避免DeployVM函数里面执行顺序不一致导致与预期结果不符
- if opts.DeleteKeypair {
- opts.Password, opts.PublicKey = "", ""
- }
- if len(desc.PublicKey) > 0 {
- opts.Password = ""
- }
- e := iVM.DeployVM(ctx, opts)
- if e != nil {
- return e
- }
- if len(desc.Password) == 0 {
- //可以从秘钥解密旧密码
- desc.Password = guest.GetOldPassword(ctx, task.GetUserCred())
- }
- return nil
- }()
- if err != nil {
- return nil, err
- }
- data := fetchIVMinfo(desc, iVM, guest.Id, desc.Account, desc.Password, desc.PublicKey, "deploy")
- return data, nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RemoteDeployGuestForRebuildRoot(ctx context.Context, guest *models.SGuest, ihost cloudprovider.ICloudHost, task taskman.ITask, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error) {
- iVM, err := ihost.GetIVMById(guest.GetExternalId())
- if err != nil {
- return nil, errors.Wrapf(err, "ihost.GetIVMById(%s)", guest.GetExternalId())
- }
- if len(desc.UserData) > 0 {
- err := iVM.UpdateUserData(desc.UserData)
- if err != nil {
- log.Errorf("update userdata fail %s", err)
- }
- cloudprovider.WaitMultiStatus(iVM, []string{api.VM_READY, api.VM_RUNNING}, time.Second*5, time.Minute*3)
- }
- diskId, err := func() (string, error) {
- lockman.LockObject(ctx, guest)
- defer lockman.ReleaseObject(ctx, guest)
- conf := cloudprovider.SManagedVMRebuildRootConfig{
- Account: desc.Account,
- ImageId: desc.ExternalImageId,
- Password: desc.Password,
- PublicKey: desc.PublicKey,
- KeypairName: desc.KeypairName,
- SysSizeGB: desc.SysDisk.SizeGB,
- OsType: desc.OsType,
- UserData: desc.UserData,
- }
- return iVM.RebuildRoot(ctx, &conf)
- }()
- if err != nil {
- return nil, err
- }
- driver, err := guest.GetDriver()
- if err != nil {
- return nil, err
- }
- initialState := driver.GetGuestInitialStateAfterRebuild()
- log.Debugf("VMrebuildRoot %s new diskID %s, wait status %s ...", iVM.GetGlobalId(), diskId, initialState)
- err = cloudprovider.WaitStatus(iVM, initialState, 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 {
- return nil, errors.Wrapf(cloudprovider.ErrTimeout, "inconsistent disk number %d < %d, wait timeout, must be something wrong on remote", len(idisks), len(desc.DataDisks)+1)
- }
- 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 {
- break
- }
- if waited > maxWaitSecs {
- return nil, fmt.Errorf("inconsistent sys disk id after rebuild root")
- }
- log.Debugf("current system disk id inconsistent %s != %s, try after 5 seconds", idisks[0].GetGlobalId(), diskId)
- time.Sleep(time.Second * 5)
- waited += 5
- }
- }
- data := fetchIVMinfo(desc, iVM, guest.Id, desc.Account, desc.Password, desc.PublicKey, "rebuild")
- return data, nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestUndeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- ihost, err := host.GetIHost(ctx)
- if err != nil {
- //私有云宿主机有可能下线,会导致虚拟机无限删除失败
- if errors.Cause(err) == cloudprovider.ErrNotFound {
- return nil, nil
- }
- return nil, errors.Wrapf(err, "host.GetIHost")
- }
- // 创建失败时external id为空。此时直接返回即可。不需要再调用公有云api
- if len(guest.ExternalId) == 0 {
- return nil, nil
- }
- ivm, err := ihost.GetIVMById(guest.ExternalId)
- if err != nil {
- if errors.Cause(err) == cloudprovider.ErrNotFound {
- return nil, nil
- }
- return nil, errors.Wrapf(err, "ihost.GetIVMById(%s)", guest.ExternalId)
- }
- err = ivm.DeleteVM(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "ivm.DeleteVM")
- }
- cloudprovider.WaitDeleted(ivm, time.Second*10, time.Minute*3)
- driver, err := guest.GetDriver()
- if err != nil {
- return nil, err
- }
- if driver.IsNeedCleanDisksAfterUndeploy() {
- disks, err := guest.GetDisks()
- if err != nil {
- return nil, errors.Wrapf(err, "GetDisks")
- }
- for i := range disks {
- disk := disks[i]
- storage, _ := disk.GetStorage()
- if !utils.IsInStringArray(storage.StorageType, api.STORAGE_LOCAL_TYPES) && disk.DiskType != api.DISK_TYPE_SYS {
- idisk, err := disk.GetIDisk(ctx)
- if err != nil {
- if errors.Cause(err) == cloudprovider.ErrNotFound {
- continue
- }
- return nil, errors.Wrapf(err, "disk.GetIDisk")
- }
- if idisk.GetStatus() == api.DISK_DEALLOC {
- continue
- }
- err = idisk.Delete(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "idisk.Delete")
- }
- }
- }
- }
- return nil, nil
- })
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestStopOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask, syncStatus bool) error {
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- ivm, err := guest.GetIVM(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "guest.GetIVM")
- }
- if ivm.GetStatus() != api.VM_READY {
- opts := &cloudprovider.ServerStopOptions{}
- task.GetParams().Unmarshal(opts)
- // 包年包月实例关机不收费,先转按量付费再关机
- if opts.StopCharging && guest.BillingType == billing_api.BILLING_TYPE_PREPAID {
- err = ivm.ChangeBillingType(string(billing_api.BILLING_TYPE_POSTPAID))
- if err != nil && errors.Cause(err) != cloudprovider.ErrNotImplemented {
- logclient.AddSimpleActionLog(guest, logclient.ACT_CHANGE_BILLING_TYPE, errors.Wrapf(err, string(billing_api.BILLING_TYPE_POSTPAID)), task.GetUserCred(), false)
- }
- }
- err = ivm.StopVM(ctx, opts)
- if err != nil {
- return nil, errors.Wrapf(err, "ivm.StopVM")
- }
- err = cloudprovider.WaitStatus(ivm, api.VM_READY, time.Second*3, time.Minute*5)
- if err != nil {
- return nil, errors.Wrapf(err, "wait server stop after 5 miniutes")
- }
- }
- // 公有云关机,公网ip会释放
- guest.SyncAllWithCloudVM(ctx, task.GetUserCred(), host, ivm, syncStatus)
- return nil, nil
- })
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestChangeBillingType(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- ivm, err := guest.GetIVM(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "guest.GetIVM")
- }
- var billType billing_api.TBillingType
- switch guest.BillingType {
- case billing_api.BILLING_TYPE_POSTPAID:
- billType = billing_api.BILLING_TYPE_PREPAID
- case billing_api.BILLING_TYPE_PREPAID:
- billType = billing_api.BILLING_TYPE_POSTPAID
- }
- err = ivm.ChangeBillingType(string(billType))
- if err != nil {
- return nil, errors.Wrapf(err, "ChangeBillingType")
- }
- err = cloudprovider.Wait(time.Second*5, time.Minute*3, func() (bool, error) {
- err = ivm.Refresh()
- if err != nil {
- return false, err
- }
- if ivm.GetBillingType() != string(billType) {
- return false, nil
- }
- return true, nil
- })
- if err != nil {
- return nil, errors.Wrapf(err, "Wait vm billing type changed")
- }
- _, err = db.Update(guest, func() error {
- guest.BillingType = billing_api.TBillingType(ivm.GetBillingType())
- guest.Status = ivm.GetStatus()
- guest.ExpiredAt = time.Time{}
- guest.AutoRenew = false
- if guest.BillingType == billing_api.BILLING_TYPE_PREPAID {
- guest.AutoRenew = ivm.IsAutoRenew()
- guest.ExpiredAt = ivm.GetExpiredAt()
- }
- return nil
- })
- return nil, err
- })
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestSyncstatusOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) error {
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- ihost, err := host.GetIHost(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "host.GetIHost")
- }
- ivm, err := ihost.GetIVMById(guest.ExternalId)
- if err != nil {
- log.Errorf("fail to find ivm by id %s", err)
- return nil, errors.Wrap(err, "ihost.GetIVMById")
- }
- err = guest.SyncAllWithCloudVM(ctx, userCred, host, ivm, true)
- if err != nil {
- return nil, errors.Wrap(err, "guest.SyncAllWithCloudVM")
- }
- status := GetCloudVMStatus(ivm)
- body := jsonutils.NewDict()
- body.Add(jsonutils.NewString(status), "status")
- return body, nil
- })
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) GetGuestVncInfo(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
- ihost, err := host.GetIHost(ctx)
- if err != nil {
- return nil, err
- }
- iVM, err := ihost.GetIVMById(guest.ExternalId)
- if err != nil {
- log.Errorf("cannot find vm %s %s", iVM, err)
- return nil, err
- }
- return iVM.GetVNCInfo(input)
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestRebuildRootDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
- subtask, err := taskman.TaskManager.NewTask(ctx, "ManagedGuestRebuildRootTask", guest, task.GetUserCred(), task.GetParams(), task.GetTaskId(), "", nil)
- if err != nil {
- return err
- }
- subtask.ScheduleRun(nil)
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) DoGuestCreateDisksTask(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
- subtask, err := taskman.TaskManager.NewTask(ctx, "ManagedGuestCreateDiskTask", guest, task.GetUserCred(), task.GetParams(), task.GetTaskId(), "", nil)
- if err != nil {
- return err
- }
- subtask.ScheduleRun(nil)
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestChangeVmConfig(ctx context.Context, guest *models.SGuest, task taskman.ITask, instanceType string, vcpuCount, cpuSockets, vmemSize int64) error {
- host, err := guest.GetHost()
- if err != nil {
- return errors.Wrapf(err, "GetHost")
- }
- ihost, err := host.GetIHost(ctx)
- if err != nil {
- return err
- }
- iVM, err := ihost.GetIVMById(guest.GetExternalId())
- if err != nil {
- return err
- }
- if len(instanceType) == 0 {
- region, err := host.GetRegion()
- if err != nil {
- return err
- }
- sku, err := models.ServerSkuManager.GetMatchedSku(region.GetId(), vcpuCount, vmemSize)
- if err != nil {
- return errors.Wrapf(err, "GetMatchedSku %s %dC%dM", region.GetId(), vcpuCount, vmemSize)
- }
- instanceType = sku.Name
- }
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- config := &cloudprovider.SManagedVMChangeConfig{
- Cpu: int(vcpuCount),
- CpuSocket: int(cpuSockets),
- MemoryMB: int(vmemSize),
- InstanceType: instanceType,
- }
- err := iVM.ChangeConfig(ctx, config)
- if err != nil {
- return nil, errors.Wrap(err, "ChangeConfig")
- }
- err = cloudprovider.WaitCreated(time.Second*5, time.Minute*5, func() bool {
- err := iVM.Refresh()
- if err != nil {
- return false
- }
- status := iVM.GetStatus()
- if status == api.VM_READY || status == api.VM_RUNNING {
- iInstanceType := iVM.GetInstanceType()
- if len(instanceType) > 0 && len(iInstanceType) > 0 && instanceType == iInstanceType {
- return true
- } else {
- // aws 目前取不到内存。返回值永远为0
- if iVM.GetVcpuCount() == int(vcpuCount) && (iVM.GetVmemSizeMB() == int(vmemSize) || iVM.GetVmemSizeMB() == 0) {
- return true
- }
- }
- }
- return false
- })
- if err != nil {
- return nil, errors.Wrap(err, "wait config change")
- }
- instanceType = iVM.GetInstanceType()
- if len(instanceType) > 0 {
- _, err = db.Update(guest, func() error {
- guest.InstanceType = instanceType
- return nil
- })
- if err != nil {
- return nil, errors.Wrap(err, "Update")
- }
- }
- return nil, nil
- })
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error {
- uuid, _ := data.GetString("uuid")
- if len(uuid) > 0 {
- db.SetExternalId(guest, task.GetUserCred(), uuid)
- }
- recycle := false
- if guest.IsPrepaidRecycle() {
- recycle = true
- }
- if data.Contains("disks") {
- diskInfo := make([]SDiskInfo, 0)
- err := data.Unmarshal(&diskInfo, "disks")
- if err != nil {
- return err
- }
- disks, _ := guest.GetGuestDisks()
- if len(disks) != len(diskInfo) {
- // 公有云镜像可能包含数据盘, 若忽略设置磁盘的external id, 会导致部分磁盘状态异常
- log.Warningf("inconsistent disk number: guest have %d disks, data contains %d disks", len(disks), len(diskInfo))
- }
- for i := 0; i < len(diskInfo) && i < len(disks); i += 1 {
- disk := disks[i].GetDisk()
- _, err = db.Update(disk, func() error {
- disk.DiskSize = diskInfo[i].Size
- disk.ExternalId = diskInfo[i].Uuid
- disk.DiskType = diskInfo[i].DiskType
- disk.Status = api.DISK_READY
- disk.Device = diskInfo[i].Device
- disk.FsFormat = diskInfo[i].FsFromat
- if diskInfo[i].AutoDelete {
- disk.AutoDelete = true
- }
- // disk.TemplateId = diskInfo[i].TemplateId
- disk.AccessPath = diskInfo[i].Path
- if !recycle {
- if len(diskInfo[i].BillingType) > 0 {
- disk.BillingType = diskInfo[i].BillingType
- disk.ExpiredAt = diskInfo[i].ExpiredAt
- }
- }
- if len(diskInfo[i].StorageExternalId) > 0 {
- storage, err := db.FetchByExternalIdAndManagerId(models.StorageManager, diskInfo[i].StorageExternalId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
- host, _ := guest.GetHost()
- if host != nil {
- return q.Equals("manager_id", host.ManagerId)
- }
- return q
- })
- if err != nil {
- log.Warningf("failed to found storage by externalId %s error: %v", diskInfo[i].StorageExternalId, err)
- } else if disk.StorageId != storage.GetId() {
- disk.StorageId = storage.GetId()
- }
- }
- if len(diskInfo[i].Metadata) > 0 {
- for key, value := range diskInfo[i].Metadata {
- if err := disk.SetMetadata(ctx, key, value, task.GetUserCred()); err != nil {
- log.Errorf("set disk %s mata %s => %s error: %v", disk.Name, key, value, err)
- }
- }
- }
- return nil
- })
- if err != nil {
- msg := fmt.Sprintf("save disk info failed %s", err)
- log.Errorf("%s", msg)
- break
- }
- db.OpsLog.LogEvent(disk, db.ACT_ALLOCATE, disk.GetShortDesc(ctx), task.GetUserCred())
- guestdisk := guest.GetGuestDisk(disk.Id)
- _, err = db.Update(guestdisk, func() error {
- guestdisk.Driver = diskInfo[i].Driver
- guestdisk.CacheMode = diskInfo[i].CacheMode
- return nil
- })
- if err != nil {
- msg := fmt.Sprintf("save disk info failed %s", err)
- log.Errorf("%s", msg)
- break
- }
- }
- }
- if metaData, _ := data.Get("metadata"); metaData != nil {
- meta := make(map[string]string, 0)
- if err := metaData.Unmarshal(meta); err != nil {
- log.Errorf("Get guest %s metadata error: %v", guest.Name, err)
- } else {
- for key, value := range meta {
- if err := guest.SetMetadata(ctx, key, value, task.GetUserCred()); err != nil {
- log.Errorf("set guest %s mata %s => %s error: %v", guest.Name, key, value, err)
- }
- }
- }
- }
- exp, err := data.GetTime("expired_at")
- if err == nil && !guest.IsPrepaidRecycle() {
- models.SaveRenewInfo(ctx, task.GetUserCred(), guest, nil, &exp, "")
- }
- driver, _ := guest.GetDriver()
- if driver != nil && driver.IsSupportSetAutoRenew() {
- autoRenew, _ := data.Bool("auto_renew")
- guest.SetAutoRenew(autoRenew)
- }
- guest.SaveDeployInfo(ctx, task.GetUserCred(), data)
- iVM, err := guest.GetIVM(ctx)
- if err != nil {
- return errors.Wrap(err, "guest.GetIVM")
- }
- guest.SyncOsInfo(ctx, task.GetUserCred(), iVM)
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestSyncSecgroupsOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
- secgroups, err := guest.GetSecgroups()
- if err != nil {
- return errors.Wrapf(err, "GetSecgroups")
- }
- iVM, err := guest.GetIVM(ctx)
- if err != nil {
- return err
- }
- externalIds := []string{}
- for _, secgroup := range secgroups {
- if len(secgroup.ExternalId) > 0 {
- externalIds = append(externalIds, secgroup.ExternalId)
- }
- }
- return iVM.SetSecurityGroups(externalIds)
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- if jsonutils.QueryBoolean(task.GetParams(), "fw_only", false) {
- driver, err := guest.GetDriver()
- if err != nil {
- return nil, err
- }
- err = driver.RequestSyncSecgroupsOnHost(ctx, guest, host, task)
- if err != nil {
- return nil, err
- }
- }
- return nil, nil
- })
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestRenewInstance(ctx context.Context, guest *models.SGuest, bc billing.SBillingCycle) (time.Time, error) {
- iVM, err := guest.GetIVM(ctx)
- if err != nil {
- return time.Time{}, err
- }
- oldExpired := iVM.GetExpiredAt()
- err = iVM.Renew(bc)
- if err != nil {
- return time.Time{}, err
- }
- //避免有些云续费后过期时间刷新比较慢问题
- cloudprovider.WaitCreated(15*time.Second, 5*time.Minute, func() bool {
- err := iVM.Refresh()
- if err != nil {
- log.Errorf("failed refresh instance %s error: %v", guest.Name, err)
- }
- newExipred := iVM.GetExpiredAt()
- if newExipred.After(oldExpired) {
- return true
- }
- return false
- })
- return iVM.GetExpiredAt(), nil
- }
- func (drv *SManagedVirtualizedGuestDriver) IsSupportEip() bool {
- return true
- }
- func chooseHostStorage(
- drv models.IGuestDriver,
- host *models.SHost,
- backend string,
- storageIds []string,
- ) *models.SStorage {
- if len(storageIds) != 0 {
- return models.StorageManager.FetchStorageById(storageIds[0])
- }
- storages := host.GetAttachedEnabledHostStorages(nil)
- for i := 0; i < len(storages); i += 1 {
- if storages[i].StorageType == backend {
- return &storages[i]
- }
- }
- for _, stype := range drv.GetStorageTypes() {
- for i := 0; i < len(storages); i += 1 {
- if storages[i].StorageType == stype {
- return &storages[i]
- }
- }
- }
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) IsSupportCdrom(guest *models.SGuest) (bool, error) {
- return false, nil
- }
- func (drv *SManagedVirtualizedGuestDriver) IsSupportFloppy(guest *models.SGuest) (bool, error) {
- return false, nil
- }
- func GetCloudVMStatus(vm cloudprovider.ICloudVM) string {
- status := vm.GetStatus()
- switch status {
- case api.VM_RUNNING:
- status = cloudprovider.CloudVMStatusRunning
- case api.VM_READY:
- status = cloudprovider.CloudVMStatusStopped
- case api.VM_STARTING:
- status = cloudprovider.CloudVMStatusStopped
- case api.VM_STOPPING:
- status = cloudprovider.CloudVMStatusStopping
- case api.VM_CHANGE_FLAVOR:
- status = cloudprovider.CloudVMStatusChangeFlavor
- case api.VM_DEPLOYING:
- status = cloudprovider.CloudVMStatusDeploying
- case api.VM_SUSPEND:
- status = cloudprovider.CloudVMStatusSuspend
- case api.VM_MIGRATING, api.VM_START_MIGRATE:
- default:
- status = cloudprovider.CloudVMStatusOther
- }
- return status
- }
- func (self *SManagedVirtualizedGuestDriver) RequestMigrate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, input api.GuestMigrateInput, task taskman.ITask) error {
- return self.requestMigrate(ctx, guest, userCred, api.GuestLiveMigrateInput{PreferHostId: input.PreferHostId}, task, false)
- }
- func (self *SManagedVirtualizedGuestDriver) RequestLiveMigrate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, input api.GuestLiveMigrateInput, task taskman.ITask) error {
- return self.requestMigrate(ctx, guest, userCred, input, task, true)
- }
- func (self *SManagedVirtualizedGuestDriver) requestMigrate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, input api.GuestLiveMigrateInput, task taskman.ITask, isLive bool) error {
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- iVM, err := guest.GetIVM(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "guest.GetIVM")
- }
- iHost, err := models.HostManager.FetchById(input.PreferHostId)
- if err != nil {
- return nil, errors.Wrapf(err, "FetchById(%s)", input.PreferHostId)
- }
- host := iHost.(*models.SHost)
- hostExternalId := host.ExternalId
- if isLive {
- err = iVM.LiveMigrateVM(hostExternalId)
- } else {
- err = iVM.MigrateVM(hostExternalId)
- }
- if err != nil {
- return nil, errors.Wrapf(err, "Migrate (%s)", hostExternalId)
- }
- err = cloudprovider.Wait(time.Second*10, time.Hour*1, func() (bool, error) {
- err = iVM.Refresh()
- if err != nil {
- return false, err
- }
- vmStatus := iVM.GetStatus()
- log.Debugf("vm %s migrate status: %s", guest.Name, vmStatus)
- if vmStatus == api.VM_UNKNOWN || strings.Contains(vmStatus, "fail") {
- return false, errors.Wrapf(cloudprovider.ErrInvalidStatus, "%s", vmStatus)
- }
- if !utils.IsInStringArray(vmStatus, []string{api.VM_RUNNING, api.VM_READY}) {
- return false, nil
- }
- hostId := iVM.GetIHostId()
- log.Debugf("guest %s migrate from %s -> %s", guest.Name, guest.HostId, host.Id)
- if len(hostId) > 0 && hostId == hostExternalId {
- return true, nil
- }
- return false, nil
- })
- if err != nil {
- return nil, errors.Wrapf(err, "wait host change")
- }
- iHost, err = db.FetchByExternalIdAndManagerId(models.HostManager, hostExternalId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
- if host, _ := guest.GetHost(); host != nil {
- return q.Equals("manager_id", host.ManagerId)
- }
- return q
- })
- if err != nil {
- return nil, errors.Wrapf(err, "fetch host %s", hostExternalId)
- }
- host = iHost.(*models.SHost)
- _, err = db.Update(guest, func() error {
- guest.HostId = host.GetId()
- return nil
- })
- if err != nil {
- return nil, errors.Wrap(err, "update hostId")
- }
- provider := host.GetCloudprovider()
- driver, err := provider.GetProvider(ctx)
- if err != nil {
- return nil, err
- }
- models.SyncVMPeripherals(ctx, userCred, guest, iVM, host, provider, driver)
- return nil, nil
- })
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestConvertPublicipToEip(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, task taskman.ITask) error {
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- iVM, err := guest.GetIVM(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "guest.GetIVM")
- }
- err = iVM.ConvertPublicIpToEip()
- if err != nil {
- return nil, errors.Wrap(err, "iVM.ConvertPublicIpToEip")
- }
- publicIp, err := guest.GetPublicIp()
- if err != nil {
- return nil, errors.Wrap(err, "guest.GetPublicIp")
- }
- if publicIp == nil {
- return nil, fmt.Errorf("faild to found public ip after convert")
- }
- err = cloudprovider.Wait(time.Second*5, time.Minute*5, func() (bool, error) {
- err = iVM.Refresh()
- if err != nil {
- log.Errorf("refresh ivm error: %v", err)
- return false, nil
- }
- eip, err := iVM.GetIEIP()
- if err != nil {
- log.Errorf("iVM.GetIEIP error: %v", err)
- return false, nil
- }
- if eip.GetGlobalId() == iVM.GetGlobalId() || eip.GetGlobalId() == eip.GetIpAddr() {
- log.Errorf("wait public ip convert to eip (%s)...", eip.GetGlobalId())
- return false, nil
- }
- _, err = db.Update(publicIp, func() error {
- publicIp.ExternalId = eip.GetGlobalId()
- publicIp.IpAddr = eip.GetIpAddr()
- publicIp.Bandwidth = eip.GetBandwidth()
- publicIp.Mode = api.EIP_MODE_STANDALONE_EIP
- return nil
- })
- return true, err
- })
- if err != nil {
- return nil, errors.Wrap(err, "cloudprovider.Wait")
- }
- return nil, nil
- })
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestSetAutoRenewInstance(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, input api.GuestAutoRenewInput, task taskman.ITask) error {
- taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
- iVM, err := guest.GetIVM(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "guest.GetIVM")
- }
- bc, err := billing.ParseBillingCycle(input.Duration)
- if err != nil {
- return nil, errors.Wrapf(err, "billing.ParseBillingCycle")
- }
- bc.AutoRenew = input.AutoRenew
- err = iVM.SetAutoRenew(bc)
- if err != nil {
- return nil, errors.Wrap(err, "iVM.SetAutoRenew")
- }
- return nil, guest.SetAutoRenew(input.AutoRenew)
- })
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) RequestRemoteUpdate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, replaceTags bool) error {
- // nil ops
- iVM, err := guest.GetIVM(ctx)
- if err != nil {
- return errors.Wrap(err, "guest.GetIVM")
- }
- err = func() error {
- oldTags, err := iVM.GetTags()
- if err != nil {
- if errors.Cause(err) == cloudprovider.ErrNotSupported || errors.Cause(err) == cloudprovider.ErrNotImplemented {
- return nil
- }
- return errors.Wrap(err, "iVM.GetTags()")
- }
- tags, err := guest.GetAllUserMetadata()
- if err != nil {
- return errors.Wrapf(err, "GetAllUserMetadata")
- }
- tagsUpdateInfo := cloudprovider.TagsUpdateInfo{OldTags: oldTags, NewTags: tags}
- host, _ := guest.GetHost()
- err = cloudprovider.SetTags(ctx, iVM, host.ManagerId, tags, replaceTags)
- if err != nil {
- if errors.Cause(err) == cloudprovider.ErrNotSupported || errors.Cause(err) == cloudprovider.ErrNotImplemented {
- return nil
- }
- logclient.AddSimpleActionLog(guest, logclient.ACT_UPDATE_TAGS, err, userCred, false)
- return errors.Wrap(err, "iVM.SetTags")
- }
- logclient.AddSimpleActionLog(guest, logclient.ACT_UPDATE_TAGS, tagsUpdateInfo, userCred, true)
- // sync back cloud metadata
- iVM.Refresh()
- guest.SyncOsInfo(ctx, userCred, iVM)
- err = models.SyncVirtualResourceMetadata(ctx, userCred, guest, iVM, false)
- if err != nil {
- return errors.Wrap(err, "syncVirtualResourceMetadata")
- }
- return nil
- }()
- if err != nil {
- return err
- }
- err = iVM.UpdateVM(ctx, cloudprovider.SInstanceUpdateOptions{NAME: guest.Name, HostName: guest.Hostname, Description: guest.Description})
- if err != nil {
- if errors.Cause(err) != cloudprovider.ErrNotSupported {
- return errors.Wrap(err, "iVM.UpdateVM")
- }
- }
- return nil
- }
- func (drv *SManagedVirtualizedGuestDriver) SyncOsInfo(ctx context.Context, userCred mcclient.TokenCredential, g *models.SGuest, extVM cloudprovider.IOSInfo) error {
- // save os info
- osinfo := map[string]interface{}{}
- for k, v := range map[string]string{
- "os_full_name": extVM.GetFullOsName(),
- "os_name": string(extVM.GetOsType()),
- "os_arch": extVM.GetOsArch(),
- "os_type": string(extVM.GetOsType()),
- "os_distribution": extVM.GetOsDist(),
- "os_version": extVM.GetOsVersion(),
- "os_language": extVM.GetOsLang(),
- } {
- if len(v) == 0 {
- continue
- }
- osinfo[k] = v
- }
- if len(osinfo) > 0 {
- err := g.SetAllMetadata(ctx, osinfo, userCred)
- if err != nil {
- return errors.Wrap(err, "SetAllMetadata")
- }
- }
- return nil
- }
- func (self *SManagedVirtualizedGuestDriver) ValidateSetOSInfo(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, input *api.ServerSetOSInfoInput) error {
- return httperrors.NewNotAcceptableError("%s server doesn't allow to set OS info", guest.Hypervisor)
- }
|