| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- // 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 cloudpods
- import (
- "context"
- "fmt"
- "time"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/cloudmux/pkg/multicloud"
- "yunion.io/x/jsonutils"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/onecloud/pkg/apis"
- billing_api "yunion.io/x/onecloud/pkg/apis/billing"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
- "yunion.io/x/onecloud/pkg/mcclient/modules/identity"
- "yunion.io/x/onecloud/pkg/mcclient/modules/logger"
- "yunion.io/x/onecloud/pkg/mcclient/modules/webconsole"
- )
- type SInstance struct {
- multicloud.SInstanceBase
- CloudpodsTags
- host *SHost
- api.ServerDetails
- }
- func (vm *SInstance) GetName() string {
- return vm.Name
- }
- func (vm *SInstance) GetHostname() string {
- return vm.Hostname
- }
- func (vm *SInstance) GetId() string {
- return vm.Id
- }
- func (vm *SInstance) GetGlobalId() string {
- return vm.Id
- }
- func (vm *SInstance) GetStatus() string {
- return vm.Status
- }
- func (vm *SInstance) Refresh() error {
- ins, err := vm.host.zone.region.GetInstance(vm.Id)
- if err != nil {
- return err
- }
- vm.DisksInfo = nil
- vm.Nics = nil
- vm.Secgroups = nil
- vm.SubIPs = nil
- vm.IsolatedDevices = nil
- vm.Cdrom = nil
- vm.Floppy = nil
- return jsonutils.Update(vm, ins)
- }
- func (vm *SInstance) GetCreatedAt() time.Time {
- return vm.CreatedAt
- }
- func (vm *SInstance) GetExpiredAt() time.Time {
- return vm.ExpiredAt
- }
- func (vm *SInstance) GetIHost() cloudprovider.ICloudHost {
- return vm.host
- }
- func (vm *SInstance) GetIHostId() string {
- return vm.HostId
- }
- func (vm *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
- disks, err := vm.host.zone.region.GetDisks("", vm.Id)
- if err != nil {
- return nil, err
- }
- ret := []cloudprovider.ICloudDisk{}
- for i := range disks {
- disks[i].region = vm.host.zone.region
- ret = append(ret, &disks[i])
- }
- return ret, nil
- }
- func (vm *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
- if len(vm.Eip) > 0 {
- eips, err := vm.host.zone.region.GetEips(vm.Id)
- if err != nil {
- return nil, err
- }
- for i := range eips {
- eips[i].region = vm.host.zone.region
- return &eips[i], nil
- }
- return nil, cloudprovider.ErrNotFound
- }
- return nil, nil
- }
- func (vm *SInstance) GetVcpuCount() int {
- return vm.VcpuCount
- }
- func (vm *SInstance) GetVmemSizeMB() int {
- return vm.VmemSize
- }
- func (vm *SInstance) GetBootOrder() string {
- return vm.BootOrder
- }
- func (vm *SInstance) GetVga() string {
- return vm.Vga
- }
- func (vm *SInstance) GetVdi() string {
- return vm.Vdi
- }
- func (vm *SInstance) GetOsType() cloudprovider.TOsType {
- return cloudprovider.TOsType(vm.OsType)
- }
- func (vm *SInstance) GetFullOsName() string {
- return vm.OsName
- }
- func (vm *SInstance) GetBios() cloudprovider.TBiosType {
- return cloudprovider.ToBiosType(vm.Bios)
- }
- func (vm *SInstance) GetOsDist() string {
- val, ok := vm.Metadata["os_distribution"]
- if ok {
- return val
- }
- return ""
- }
- func (vm *SInstance) GetOsVersion() string {
- val, ok := vm.Metadata["os_version"]
- if ok {
- return val
- }
- return ""
- }
- func (vm *SInstance) GetOsLang() string {
- val, ok := vm.Metadata["os_language"]
- if ok {
- return val
- }
- return ""
- }
- func (vm *SInstance) GetOsArch() string {
- return vm.OsArch
- }
- func (vm *SInstance) GetMachine() string {
- return vm.Machine
- }
- func (vm *SInstance) GetInstanceType() string {
- return vm.InstanceType
- }
- func (vm *SInstance) GetSecurityGroupIds() ([]string, error) {
- ret := []string{}
- for _, sec := range vm.Secgroups {
- ret = append(ret, sec.Id)
- }
- return ret, nil
- }
- func (vm *SInstance) GetProjectId() string {
- return vm.TenantId
- }
- func (vm *SInstance) SetSecurityGroups(ids []string) error {
- if vm.Hypervisor == api.HYPERVISOR_ESXI {
- return nil
- }
- input := api.GuestSetSecgroupInput{}
- input.SecgroupIds = ids
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "set-secgroup", input)
- return err
- }
- func (vm *SInstance) GetHypervisor() string {
- return vm.Hypervisor
- }
- func (vm *SInstance) StartVM(ctx context.Context) error {
- if vm.Status == api.VM_RUNNING {
- return nil
- }
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "start", nil)
- return err
- }
- func (vm *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
- if vm.Status == api.VM_READY {
- return nil
- }
- input := api.ServerStopInput{}
- input.IsForce = opts.IsForce
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "stop", input)
- return err
- }
- func (vm *SInstance) DeleteVM(ctx context.Context) error {
- if vm.DisableDelete != nil && *vm.DisableDelete {
- input := api.ServerUpdateInput{}
- disableDelete := false
- input.DisableDelete = &disableDelete
- vm.host.zone.region.cli.update(&modules.Servers, vm.Id, input)
- }
- return vm.host.zone.region.cli.delete(&modules.Servers, vm.Id)
- }
- func (vm *SInstance) UpdateVM(ctx context.Context, input cloudprovider.SInstanceUpdateOptions) error {
- if vm.Name != input.NAME {
- param := api.ServerUpdateInput{}
- param.Name = input.NAME
- param.Description = input.Description
- vm.host.zone.region.cli.update(&modules.Servers, vm.Id, input)
- return cloudprovider.WaitMultiStatus(vm, []string{api.VM_READY, api.VM_RUNNING}, time.Second*5, time.Minute*3)
- }
- return nil
- }
- func (vm *SInstance) UpdateUserData(userData string) error {
- input := api.ServerUserDataInput{}
- input.UserData = userData
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "user-data", input)
- return err
- }
- func (vm *SInstance) RebuildRoot(ctx context.Context, opts *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
- input := api.ServerRebuildRootInput{}
- input.ImageId = opts.ImageId
- input.Password = opts.Password
- if len(opts.PublicKey) > 0 {
- keypairId, err := vm.host.zone.region.syncKeypair(vm.Name, opts.PublicKey)
- if err != nil {
- return "", errors.Wrapf(err, "syncKeypair")
- }
- input.KeypairId = keypairId
- }
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "rebuild-root", input)
- if err != nil {
- return "", err
- }
- return vm.DisksInfo[0].Id, nil
- }
- func (vm *SInstance) DeployVM(ctx context.Context, opts *cloudprovider.SInstanceDeployOptions) error {
- input := api.ServerDeployInput{}
- input.Password = opts.Password
- input.DeleteKeypair = opts.DeleteKeypair
- if len(opts.PublicKey) > 0 {
- keypairId, err := vm.host.zone.region.syncKeypair(vm.Name, opts.PublicKey)
- if err != nil {
- return errors.Wrapf(err, "syncKeypair")
- }
- input.KeypairId = keypairId
- }
- cloudprovider.WaitMultiStatus(vm, []string{api.VM_READY, api.VM_RUNNING}, time.Second*5, time.Minute*3)
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "deploy", input)
- if err != nil {
- return errors.Wrapf(err, "deploy")
- }
- timeout := time.Minute * 3
- if vm.Hypervisor == api.HYPERVISOR_BAREMETAL {
- timeout = time.Minute * 10
- }
- return cloudprovider.WaitMultiStatus(vm, []string{api.VM_READY, api.VM_RUNNING}, time.Second*5, timeout)
- }
- func (vm *SInstance) ChangeConfig(ctx context.Context, opts *cloudprovider.SManagedVMChangeConfig) error {
- input := api.ServerChangeConfigInput{}
- input.VmemSize = fmt.Sprintf("%dM", opts.MemoryMB)
- input.VcpuCount = &opts.Cpu
- input.InstanceType = opts.InstanceType
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "change-config", input)
- return err
- }
- func (vm *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
- return vm.host.zone.region.GetInstanceVnc(vm.Id, vm.Name)
- }
- func (region *SRegion) GetInstanceVnc(id, name string) (*cloudprovider.ServerVncOutput, error) {
- s := region.cli.s
- resp, err := webconsole.WebConsole.DoServerConnect(s, id, nil)
- if err != nil {
- return nil, errors.Wrapf(err, "DoServerConnect")
- }
- result := &cloudprovider.ServerVncOutput{
- Protocol: "cloudpods",
- InstanceId: id,
- InstanceName: name,
- Hypervisor: api.HYPERVISOR_DEFAULT,
- }
- err = resp.Unmarshal(&result)
- if err != nil {
- return nil, errors.Wrapf(err, "resp.Unmarshal")
- }
- resp, err = identity.ServicesV3.GetSpecific(s, "common", "config", nil)
- if err != nil {
- return nil, errors.Wrapf(err, "GetSpecific")
- }
- result.ApiServer, _ = resp.GetString("config", "default", "api_server")
- return result, nil
- }
- func (vm *SInstance) AttachDisk(ctx context.Context, diskId string) error {
- input := api.ServerAttachDiskInput{}
- input.DiskId = diskId
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "attachdisk", input)
- return err
- }
- func (vm *SInstance) DetachDisk(ctx context.Context, diskId string) error {
- input := api.ServerDetachDiskInput{}
- input.DiskId = diskId
- input.KeepDisk = true
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "detachdisk", input)
- return err
- }
- func (vm *SInstance) MigrateVM(hostId string) error {
- input := api.GuestMigrateInput{}
- input.PreferHost = hostId
- input.PreferHostId = hostId
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "migrate", input)
- return err
- }
- func (vm *SInstance) LiveMigrateVM(hostId string) error {
- input := api.GuestLiveMigrateInput{}
- input.PreferHost = hostId
- input.PreferHostId = hostId
- skipCheck := true
- input.SkipCpuCheck = &skipCheck
- input.SkipKernelCheck = &skipCheck
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "live-migrate", input)
- return err
- }
- func (vm *SInstance) GetDetails() (*api.ServerDetails, error) {
- ret := &api.ServerDetails{}
- err := vm.host.zone.region.cli.get(&modules.Servers, vm.Id, nil, ret)
- if err != nil {
- return nil, err
- }
- return ret, nil
- }
- func (vm *SInstance) VMSetStatus(status string) error {
- input := apis.PerformStatusInput{}
- input.Status = status
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "status", input)
- return err
- }
- func (vm *SInstance) GetError() error {
- if utils.IsInStringArray(vm.Status, []string{api.VM_DISK_FAILED, api.VM_SCHEDULE_FAILED, api.VM_NETWORK_FAILED}) {
- return fmt.Errorf("vm create failed with status %s", vm.Status)
- }
- if vm.Status == api.VM_DEPLOY_FAILED {
- params := map[string]interface{}{"obj_id": vm.Id, "success": false}
- actions := []apis.OpsLogDetails{}
- vm.host.zone.region.list(&logger.Actions, params, &actions)
- if len(actions) > 0 {
- return fmt.Errorf("%s", actions[0].Notes)
- }
- return fmt.Errorf("vm create failed with status %s", vm.Status)
- }
- return nil
- }
- func (vm *SInstance) CreateInstanceSnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudInstanceSnapshot, error) {
- return nil, cloudprovider.ErrNotImplemented
- }
- func (vm *SInstance) GetInstanceSnapshot(idStr string) (cloudprovider.ICloudInstanceSnapshot, error) {
- return nil, cloudprovider.ErrNotImplemented
- }
- func (vm *SInstance) GetInstanceSnapshots() ([]cloudprovider.ICloudInstanceSnapshot, error) {
- return nil, cloudprovider.ErrNotImplemented
- }
- func (vm *SInstance) ResetToInstanceSnapshot(ctx context.Context, idStr string) error {
- return cloudprovider.ErrNotImplemented
- }
- func (vm *SInstance) SaveImage(opts *cloudprovider.SaveImageOptions) (cloudprovider.ICloudImage, error) {
- return vm.host.zone.region.SaveImage(vm.Id, opts.Name, opts.Notes)
- }
- func (region *SRegion) SaveImage(id, imageName, notes string) (*SImage, error) {
- input := api.ServerSaveImageInput{}
- input.GenerateName = imageName
- input.Notes = notes
- resp, err := region.perform(&modules.Servers, id, "save-image", input)
- if err != nil {
- return nil, err
- }
- imageId, err := resp.GetString("image_id")
- if err != nil {
- return nil, err
- }
- caches, err := region.GetStoragecaches()
- if err != nil {
- return nil, errors.Wrapf(err, "GetStoragecaches")
- }
- if len(caches) == 0 {
- return nil, fmt.Errorf("no storage cache found")
- }
- caches[0].region = region
- image, err := region.GetImage(imageId)
- if err != nil {
- return nil, err
- }
- image.cache = &caches[0]
- return image, nil
- }
- func (vm *SInstance) AllocatePublicIpAddress() (string, error) {
- return "", cloudprovider.ErrNotImplemented
- }
- func (host *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
- servers, err := host.zone.region.GetInstances(host.Id)
- if err != nil {
- return nil, err
- }
- ret := []cloudprovider.ICloudVM{}
- for i := range servers {
- servers[i].host = host
- ret = append(ret, &servers[i])
- }
- return ret, nil
- }
- func (host *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
- ins, err := host.zone.region.GetInstance(id)
- if err != nil {
- return nil, err
- }
- ins.host = host
- return ins, nil
- }
- func (region *SRegion) GetInstance(id string) (*SInstance, error) {
- ins := &SInstance{}
- return ins, region.cli.get(&modules.Servers, id, nil, ins)
- }
- func (region *SRegion) GetInstances(hostId string) ([]SInstance, error) {
- params := map[string]interface{}{}
- if len(hostId) > 0 {
- params["host_id"] = hostId
- }
- params["filter"] = "hypervisor.in('kvm', 'baremetal', 'pod')"
- ret := []SInstance{}
- return ret, region.list(&modules.Servers, params, &ret)
- }
- func (region *SRegion) CreateInstance(hostId, hypervisor string, opts *cloudprovider.SManagedVMCreateConfig) (*SInstance, error) {
- input := api.ServerCreateInput{
- ServerConfigs: &api.ServerConfigs{},
- }
- input.GenerateName = opts.Name
- input.Hostname = opts.Hostname
- input.Description = opts.Description
- input.InstanceType = opts.InstanceType
- input.VcpuCount = opts.Cpu
- input.VmemSize = opts.MemoryMB
- input.Password = opts.Password
- if len(input.Password) == 0 {
- resetPasswd := false
- input.ResetPassword = &resetPasswd
- }
- input.PublicIpBw = opts.PublicIpBw
- input.PublicIpChargeType = billing_api.TNetChargeType(opts.PublicIpChargeType)
- input.ProjectId = opts.ProjectId
- input.Metadata = opts.Tags
- input.UserData = opts.UserData
- input.PreferHost = hostId
- input.Hypervisor = hypervisor
- if len(input.UserData) > 0 {
- input.EnableCloudInit = true
- }
- input.Secgroups = opts.ExternalSecgroupIds
- if opts.BillingCycle != nil {
- input.Duration = opts.BillingCycle.String()
- }
- image, err := region.GetImage(opts.ExternalImageId)
- if err != nil {
- return nil, errors.Wrapf(err, "GetImage")
- }
- imageId := opts.ExternalImageId
- if image.DiskFormat == "iso" {
- input.Cdrom = opts.ExternalImageId
- imageId = ""
- }
- sysDisk := &api.DiskConfig{
- Index: 0,
- ImageId: imageId,
- DiskType: api.DISK_TYPE_SYS,
- SizeMb: opts.SysDisk.SizeGB * 1024,
- Backend: opts.SysDisk.StorageType,
- Storage: opts.SysDisk.StorageExternalId,
- }
- if len(opts.SysDisk.Driver) > 0 {
- sysDisk.Driver = opts.SysDisk.Driver
- }
- if len(opts.SysDisk.CacheMode) > 0 {
- sysDisk.Cache = opts.SysDisk.CacheMode
- }
- input.Disks = append(input.Disks, sysDisk)
- for idx, disk := range opts.DataDisks {
- dataDisk := &api.DiskConfig{
- Index: idx + 1,
- DiskType: api.DISK_TYPE_DATA,
- SizeMb: disk.SizeGB * 1024,
- Backend: disk.StorageType,
- Storage: disk.StorageExternalId,
- }
- if len(disk.Driver) > 0 {
- dataDisk.Driver = disk.Driver
- }
- if len(disk.CacheMode) > 0 {
- dataDisk.Cache = disk.CacheMode
- }
- input.Disks = append(input.Disks, dataDisk)
- }
- input.IsolatedDevices = []*api.IsolatedDeviceConfig{}
- for _, dev := range input.IsolatedDevices {
- devConfig := &api.IsolatedDeviceConfig{
- Id: dev.Id,
- }
- input.IsolatedDevices = append(input.IsolatedDevices, devConfig)
- }
- input.Networks = append(input.Networks, &api.NetworkConfig{
- Index: 0,
- Network: opts.ExternalNetworkId,
- Address: opts.IpAddr,
- })
- ins := &SInstance{}
- return ins, region.create(&modules.Servers, input, ins)
- }
- func (vm *SInstance) CreateDisk(ctx context.Context, opts *cloudprovider.GuestDiskCreateOptions) (string, error) {
- diskIds := []string{}
- for _, disk := range vm.DisksInfo {
- diskIds = append(diskIds, disk.Id)
- }
- input := jsonutils.Marshal(map[string]interface{}{
- "disks": []map[string]interface{}{
- {
- "size": opts.SizeMb,
- "storage_id": opts.StorageId,
- "preallocation": opts.Preallocation,
- },
- },
- })
- _, err := vm.host.zone.region.perform(&modules.Servers, vm.Id, "createdisk", input)
- if err != nil {
- return "", err
- }
- ret := ""
- cloudprovider.Wait(time.Second*3, time.Minute*3, func() (bool, error) {
- err = vm.Refresh()
- if err != nil {
- return false, errors.Wrapf(err, "Refresh")
- }
- for _, disk := range vm.DisksInfo {
- if !utils.IsInStringArray(disk.Id, diskIds) {
- ret = disk.Id
- return true, nil
- }
- }
- return false, nil
- })
- if len(ret) > 0 {
- return ret, nil
- }
- return "", errors.Wrapf(cloudprovider.ErrNotFound, "after disk created")
- }
- func (vm *SInstance) GetIsolateDeviceIds() ([]string, error) {
- devs, err := vm.host.zone.region.GetIsolatedDevices("", vm.Id)
- if err != nil {
- return nil, err
- }
- ret := []string{}
- for i := range devs {
- ret = append(ret, devs[i].GetGlobalId())
- }
- return ret, nil
- }
- func (vm *SInstance) GetContainers() ([]cloudprovider.ICloudContainer, error) {
- containers, err := vm.host.zone.region.GetContainers(vm.Id)
- if err != nil {
- return nil, err
- }
- ret := []cloudprovider.ICloudContainer{}
- for i := range containers {
- containers[i].region = vm.host.zone.region
- ret = append(ret, &containers[i])
- }
- return ret, nil
- }
|