diff --git a/Gopkg.lock b/Gopkg.lock index 4c92a38082..3f05cbfc65 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -79,7 +79,7 @@ version = "v1.19.0" [[projects]] - digest = "1:bad9acf125c8b98ea473f45cf8eabee4c401fcd108534b65d2033af7481be25d" + digest = "1:c9956ee9d99542ed5be2a37e8a35505a4ca1516520380623e3f54f51bfc53d7a" name = "github.com/aliyun/alibaba-cloud-sdk-go" packages = [ "sdk", @@ -93,8 +93,8 @@ "sdk/utils", ] pruneopts = "UT" - revision = "1d757c22fbf7997c68c43d72e92d462ea05e1439" - version = "0.7.4" + revision = "a46ca5dbc29d3df78e8da89737b5f40dd8c03d26" + version = "0.7.7" [[projects]] digest = "1:5a2d4da3f703696f4bec1ba1d363f50461b72934b1817c07ab0963496ed1c90d" @@ -248,12 +248,12 @@ version = "v1.2.0" [[projects]] - digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" + digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" name = "github.com/davecgh/go-spew" packages = ["spew"] pruneopts = "UT" - revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" - version = "v1.1.1" + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" [[projects]] digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55" @@ -1312,11 +1312,11 @@ [[projects]] branch = "master" - digest = "1:54554b3c72f4fcbd3c27c5137f9acd9d153d7e7150d77cd509af50c75fcb41b9" + digest = "1:cdf0f619f0b3e37a792fb7f124bf2881d8a18cc13d693454b9731a8fa4f5fedc" name = "yunion.io/x/jsonutils" packages = ["."] pruneopts = "UT" - revision = "7079aada4c7e9a37e4c3b183bddb0ef684e038f2" + revision = "f5b4a20f593f1f49039455fe40cb1274de53ac3f" [[projects]] branch = "master" @@ -1397,7 +1397,6 @@ "github.com/Microsoft/azure-vhd-utils/vhdcore/footer", "github.com/Microsoft/azure-vhd-utils/vhdcore/validator", "github.com/aliyun/alibaba-cloud-sdk-go/sdk", - "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors", "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests", "github.com/aliyun/aliyun-oss-go-sdk/oss", "github.com/aokoli/goutils", @@ -1461,6 +1460,7 @@ "github.com/vmware/govmomi/property", "github.com/vmware/govmomi/session", "github.com/vmware/govmomi/view", + "github.com/vmware/govmomi/vim25", "github.com/vmware/govmomi/vim25/mo", "github.com/vmware/govmomi/vim25/types", "golang.org/x/crypto/ssh", diff --git a/cmd/climc/shell/cloudmeta.go b/cmd/climc/shell/cloudmeta.go index 6fa020a639..25beae7eab 100644 --- a/cmd/climc/shell/cloudmeta.go +++ b/cmd/climc/shell/cloudmeta.go @@ -7,8 +7,8 @@ import ( func init() { type CloudmetaOptions struct { PROVIDER_ID string `help:"provider_id"` - REGION_ID string `help:"region_id"` - ZONE_ID string `help:"zone_id"` + REGION_ID string `help:"region_id"` + ZONE_ID string `help:"zone_id"` } R(&CloudmetaOptions{}, "instance-type-list", "query backend service for its version", func(s *mcclient.ClientSession, args *CloudmetaOptions) error { return nil diff --git a/pkg/cloudcommon/db/taskman/interface.go b/pkg/cloudcommon/db/taskman/interface.go index 39b56b2525..e4735c42b1 100644 --- a/pkg/cloudcommon/db/taskman/interface.go +++ b/pkg/cloudcommon/db/taskman/interface.go @@ -2,6 +2,7 @@ package taskman import ( "context" + "net/http" "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/mcclient" @@ -14,6 +15,8 @@ type ITask interface { GetTaskId() string SetStage(stageName string, data *jsonutils.JSONDict) + GetTaskRequestHeader() http.Header + SetStageComplete(ctx context.Context, data *jsonutils.JSONDict) SetStageFailed(ctx context.Context, reason string) } diff --git a/pkg/cloudcommon/db/taskman/tasks.go b/pkg/cloudcommon/db/taskman/tasks.go index f16cc14387..b954d86d08 100644 --- a/pkg/cloudcommon/db/taskman/tasks.go +++ b/pkg/cloudcommon/db/taskman/tasks.go @@ -427,7 +427,7 @@ func execITask(taskValue reflect.Value, task *STask, odata jsonutils.JSONObject, return } - log.Debugf("Call %s %s", task.TaskName, stageName) + log.Debugf("Call %s %s %#v", task.TaskName, stageName, params) funcValue.Call(params) // call save request context @@ -652,3 +652,11 @@ func (self *STask) GetObject() db.IStandaloneModel { func (self *STask) GetObjects() []db.IStandaloneModel { return self.taskObjects } + +func (task *STask) GetTaskRequestHeader() http.Header { + header := http.Header{} + header.Set(mcclient.AUTH_TOKEN, task.GetUserCred().GetTokenString()) + header.Set(mcclient.TASK_ID, task.GetTaskId()) + header.Set(mcclient.REGION_VERSION, "v2") + return header +} diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go index 22b42736c9..ff81090e9b 100644 --- a/pkg/cloudprovider/cloudprovider.go +++ b/pkg/cloudprovider/cloudprovider.go @@ -20,16 +20,19 @@ type ICloudProviderFactory interface { type ICloudProvider interface { GetId() string GetName() string - GetIRegions() []ICloudRegion GetSysInfo() (jsonutils.JSONObject, error) IsPublicCloud() bool + IsOnPremiseInfrastructure() bool + GetIRegions() []ICloudRegion GetIRegionById(id string) (ICloudRegion, error) - GetIHostById(id string) (ICloudHost, error) - GetIVpcById(id string) (ICloudVpc, error) - GetIStorageById(id string) (ICloudStorage, error) - GetIStoragecacheById(id string) (ICloudStoragecache, error) + GetOnPremiseIRegion() (ICloudRegion, error) + + // GetIHostById(id string) (ICloudHost, error) + // GetIVpcById(id string) (ICloudVpc, error) + // GetIStorageById(id string) (ICloudStorage, error) + // GetIStoragecacheById(id string) (ICloudStoragecache, error) GetBalance() (float64, error) diff --git a/pkg/cloudprovider/fakeregion.go b/pkg/cloudprovider/fakeregion.go new file mode 100644 index 0000000000..0634a97bd9 --- /dev/null +++ b/pkg/cloudprovider/fakeregion.go @@ -0,0 +1,85 @@ +package cloudprovider + +import ( + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/util/secrules" +) + +type SFakeOnPremiseRegion struct { +} + +func (region *SFakeOnPremiseRegion) GetId() string { + return "default" +} + +func (region *SFakeOnPremiseRegion) GetName() string { + return "Default" +} + +func (region *SFakeOnPremiseRegion) GetGlobalId() string { + return "default" +} + +func (region *SFakeOnPremiseRegion) GetStatus() string { + return "available" +} + +func (region *SFakeOnPremiseRegion) Refresh() error { + return nil +} + +func (region *SFakeOnPremiseRegion) IsEmulated() bool { + return true +} + +func (region *SFakeOnPremiseRegion) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (region *SFakeOnPremiseRegion) GetLatitude() float32 { + return 0.0 +} + +func (region *SFakeOnPremiseRegion) GetLongitude() float32 { + return 0.0 +} + +func (region *SFakeOnPremiseRegion) GetIZones() ([]ICloudZone, error) { + return nil, ErrNotSupported +} + +func (region *SFakeOnPremiseRegion) GetIZoneById(id string) (ICloudZone, error) { + return nil, ErrNotSupported +} + +func (region *SFakeOnPremiseRegion) GetIVpcById(id string) (ICloudVpc, error) { + return nil, ErrNotSupported +} + +func (region *SFakeOnPremiseRegion) GetIVpcs() ([]ICloudVpc, error) { + return nil, ErrNotSupported +} + +func (region *SFakeOnPremiseRegion) GetIEips() ([]ICloudEIP, error) { + return nil, ErrNotSupported +} + +func (region *SFakeOnPremiseRegion) GetIEipById(id string) (ICloudEIP, error) { + return nil, ErrNotSupported +} + +func (region *SFakeOnPremiseRegion) CreateIVpc(name string, desc string, cidr string) (ICloudVpc, error) { + return nil, ErrNotSupported +} + +func (region *SFakeOnPremiseRegion) CreateEIP(name string, bwMbps int, chargeType string) (ICloudEIP, error) { + return nil, ErrNotSupported +} + +func (region *SFakeOnPremiseRegion) DeleteSecurityGroup(vpcId, secgroupId string) error { + return ErrNotSupported +} + +func (region *SFakeOnPremiseRegion) SyncSecurityGroup(secgroupId string, vpcId string, name string, desc string, rules []secrules.SecurityRule) (string, error) { + return "", ErrNotSupported +} diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index 7b2af44862..32208b07e5 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -3,6 +3,7 @@ package cloudprovider import ( "time" + "context" "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/pkg/util/secrules" @@ -35,23 +36,24 @@ type ICloudRegion interface { GetIZones() ([]ICloudZone, error) GetIVpcs() ([]ICloudVpc, error) GetIEips() ([]ICloudEIP, error) - GetISnapshots() ([]ICloudSnapshot, error) - - GetISnapshotById(snapshotId string) (ICloudSnapshot, error) - GetIZoneById(id string) (ICloudZone, error) GetIVpcById(id string) (ICloudVpc, error) - GetIHostById(id string) (ICloudHost, error) - GetIStorageById(id string) (ICloudStorage, error) - GetIStoragecacheById(id string) (ICloudStoragecache, error) + GetIZoneById(id string) (ICloudZone, error) + GetIEipById(id string) (ICloudEIP, error) DeleteSecurityGroup(vpcId, secgroupId string) error SyncSecurityGroup(secgroupId string, vpcId string, name string, desc string, rules []secrules.SecurityRule) (string, error) CreateIVpc(name string, desc string, cidr string) (ICloudVpc, error) - CreateEIP(name string, bwMbps int, chargeType string) (ICloudEIP, error) - GetIEipById(id string) (ICloudEIP, error) + GetISnapshots() ([]ICloudSnapshot, error) + GetISnapshotById(snapshotId string) (ICloudSnapshot, error) + + GetIHosts() ([]ICloudHost, error) + GetIHostById(id string) (ICloudHost, error) + + GetIStorages() ([]ICloudStorage, error) + GetIStorageById(id string) (ICloudStorage, error) GetProvider() string } @@ -71,7 +73,7 @@ type ICloudZone interface { type ICloudImage interface { ICloudResource - Delete() error + Delete(ctx context.Context) error GetIStoragecache() ICloudStoragecache } @@ -79,6 +81,9 @@ type ICloudStoragecache interface { ICloudResource GetIImages() ([]ICloudImage, error) + GetIImageById(extId string) (ICloudImage, error) + + GetPath() string GetManagerId() string @@ -106,7 +111,10 @@ type ICloudStorage interface { GetManagerId() string CreateIDisk(name string, sizeGb int, desc string) (ICloudDisk, error) - GetIDisk(idStr string) (ICloudDisk, error) + + GetIDiskById(idStr string) (ICloudDisk, error) + + GetMountPoint() string } type ICloudHost interface { @@ -135,10 +143,15 @@ type ICloudHost interface { GetStorageType() string GetHostType() string + GetIsMaintenance() bool + GetVersion() string + GetManagerId() string CreateVM(name string, imgId string, sysDiskSize int, cpu int, memMB int, vswitchId string, ipAddr string, desc string, passwd string, storageType string, diskSizes []int, publicKey string, extSecGrpId string, userData string) (ICloudVM, error) + + GetIHostNics() ([]ICloudHostNetInterface, error) } type ICloudVM interface { @@ -172,22 +185,24 @@ type ICloudVM interface { // GetSecurityGroup() ICloudSecurityGroup - StartVM() error - StopVM(isForce bool) error - DeleteVM() error + StartVM(ctx context.Context) error + StopVM(ctx context.Context, isForce bool) error + DeleteVM(ctx context.Context) error - UpdateVM(name string) error + UpdateVM(ctx context.Context, name string) error UpdateUserData(userData string) error - RebuildRoot(imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) + RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) - DeployVM(name string, password string, publicKey string, deleteKeypair bool, description string) error + DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error - ChangeConfig(instanceId string, ncpu int, vmem int) error + ChangeConfig(ctx context.Context, ncpu int, vmem int) error GetVNCInfo() (jsonutils.JSONObject, error) - AttachDisk(diskId string) error - DetachDisk(diskId string) error + AttachDisk(ctx context.Context, diskId string) error + DetachDisk(ctx context.Context, diskId string) error + + CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error } type ICloudNic interface { @@ -248,7 +263,7 @@ type ICloudDisk interface { ICloudResource IBillingResource - GetIStorge() ICloudStorage + GetIStorage() (ICloudStorage, error) // GetStatus() string GetDiskFormat() string @@ -262,14 +277,17 @@ type ICloudDisk interface { GetDriver() string GetCacheMode() string GetMountpoint() string - Delete() error - CreateISnapshot(name string, desc string) (ICloudSnapshot, error) + GetAccessPath() string + + Delete(ctx context.Context) error + + CreateISnapshot(ctx context.Context, name string, desc string) (ICloudSnapshot, error) GetISnapshot(idStr string) (ICloudSnapshot, error) GetISnapshots() ([]ICloudSnapshot, error) - Resize(newSize int64) error - Reset(snapshotId string) error + Resize(ctx context.Context, newSizeMB int64) error + Reset(ctx context.Context, snapshotId string) error } type ICloudSnapshot interface { @@ -326,3 +344,14 @@ type ICloudNetwork interface { GetAllocTimeoutSeconds() int } + +type ICloudHostNetInterface interface { + GetDevice() string + GetDriver() string + GetMac() string + GetIndex() int8 + IsLinkUp() bool + GetIpAddr() string + GetMtu() int16 + GetNicType() string +} diff --git a/pkg/compute/guestdrivers/aliyun.go b/pkg/compute/guestdrivers/aliyun.go index 22b08f58bd..5d86c3a783 100644 --- a/pkg/compute/guestdrivers/aliyun.go +++ b/pkg/compute/guestdrivers/aliyun.go @@ -11,7 +11,6 @@ import ( "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/pkg/utils" - "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" @@ -96,85 +95,9 @@ func (self *SAliyunGuestDriver) ValidateCreateData(ctx context.Context, userCred return data, nil } -type SDiskInfo struct { - DiskType string - Size int - Uuid string - BillingType string - FsFromat string - AutoDelete bool - TemplateId string - DiskFormat string - ExpiredAt time.Time - - Metadata map[string]string -} - -func fetchIVMinfo(desc SManagedVMCreateConfig, iVM cloudprovider.ICloudVM, guestId string, account, passwd string, action string) *jsonutils.JSONDict { - data := jsonutils.NewDict() - - data.Add(jsonutils.NewString(iVM.GetOSType()), "os") - - if len(passwd) > 0 { - encpasswd, err := utils.EncryptAESBase64(guestId, passwd) - if err != nil { - log.Errorf("encrypt password failed %s", err) - } - data.Add(jsonutils.NewString(account), "account") - data.Add(jsonutils.NewString(encpasswd), "key") - } - - if len(desc.OsDistribution) > 0 { - data.Add(jsonutils.NewString(desc.OsDistribution), "distro") - } - if len(desc.OsVersion) > 0 { - data.Add(jsonutils.NewString(desc.OsVersion), "version") - } - - idisks, err := iVM.GetIDisks() - - if err != nil { - log.Errorf("GetiDisks error %s", err) - } else { - diskInfo := make([]SDiskInfo, len(idisks)) - for i := 0; i < len(idisks); i += 1 { - dinfo := SDiskInfo{} - dinfo.Uuid = idisks[i].GetGlobalId() - dinfo.Size = idisks[i].GetDiskSizeMB() - dinfo.DiskType = idisks[i].GetDiskType() - dinfo.BillingType = idisks[i].GetBillingType() - dinfo.DiskFormat = idisks[i].GetDiskFormat() - dinfo.AutoDelete = idisks[i].GetIsAutoDelete() - if action == "create" { - dinfo.AutoDelete = true - } - dinfo.TemplateId = idisks[i].GetTemplateId() - dinfo.FsFromat = idisks[i].GetFsFormat() - dinfo.ExpiredAt = idisks[i].GetExpiredAt() - if metaData := idisks[i].GetMetadata(); metaData != nil { - dinfo.Metadata = make(map[string]string, 0) - if err := metaData.Unmarshal(dinfo.Metadata); err != nil { - log.Errorf("Get disk %s metadata info error: %v", idisks[i].GetName(), err) - } - } - diskInfo[i] = dinfo - } - data.Add(jsonutils.Marshal(&diskInfo), "disks") - } - - data.Add(jsonutils.NewString(iVM.GetGlobalId()), "uuid") - data.Add(iVM.GetMetadata(), "metadata") - - return data -} - func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { config := guest.GetDeployConfigOnHost(ctx, host, task.GetParams()) log.Debugf("RequestDeployGuestOnHost: %s", config) - /* onfinish, err := config.GetString("on_finish") - if err != nil { - return err - } */ action, err := config.GetString("action") if err != nil { @@ -249,65 +172,8 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu return nil, err } - /*if len(guest.SecgrpId) > 0 { - if err := iVM.SyncSecurityGroup(guest.SecgrpId, guest.GetSecgroupName(), guest.GetSecRules()); err != nil { - log.Errorf("SyncSecurityGroup error: %v", err) - return nil, err - } - }*/ - - /*if onfinish == "none" { - err = iVM.StartVM() - if err != nil { - return nil, err - } - }*/ - data := fetchIVMinfo(desc, iVM, guest.Id, "root", passwd, action) - /* data.Add(jsonutils.NewString(iVM.GetOSType()), "os") - - if len(passwd) > 0 { - encpasswd, err := utils.EncryptAESBase64(guest.Id, passwd) - if err != nil { - log.Errorf("encrypt password failed %s", err) - } - data.Add(jsonutils.NewString("root"), "account") - data.Add(jsonutils.NewString(encpasswd), "key") - } - - if len(desc.OsDistribution) > 0 { - data.Add(jsonutils.NewString(desc.OsDistribution), "distro") - } - if len(desc.OsVersion) > 0 { - data.Add(jsonutils.NewString(desc.OsVersion), "version") - } - - idisks, err := iVM.GetIDisks() - - if err != nil { - log.Errorf("GetiDisks error %s", err) - } else { - diskInfo := make([]SDiskInfo, len(idisks)) - for i := 0; i < len(idisks); i += 1 { - dinfo := SDiskInfo{} - dinfo.Uuid = idisks[i].GetGlobalId() - dinfo.Size = idisks[i].GetDiskSizeMB() - if metaData := idisks[i].GetMetadata(); metaData != nil { - dinfo.Metadata = make(map[string]string, 0) - if err := metaData.Unmarshal(dinfo.Metadata); err != nil { - log.Errorf("Get disk %s metadata info error: %v", idisks[i].GetName(), err) - } - } - diskInfo[i] = dinfo - } - data.Add(jsonutils.Marshal(&diskInfo), "disks") - } - - data.Add(jsonutils.NewString(iVM.GetGlobalId()), "uuid") - data.Add(iVM.GetMetadata(), "metadata") - */ - return data, nil }) } else if action == "deploy" { @@ -345,27 +211,13 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu } } - err := iVM.DeployVM(name, passwd, publicKey, deleteKeypair, description) + err := iVM.DeployVM(ctx, name, passwd, publicKey, deleteKeypair, description) if err != nil { return nil, err } data := fetchIVMinfo(desc, iVM, guest.Id, "root", passwd, action) - /* - data := jsonutils.NewDict() - - if len(passwd) > 0 { - encpasswd, err := utils.EncryptAESBase64(guest.Id, passwd) - if err != nil { - log.Errorf("encrypt password failed %s", err) - } - - - data.Add(jsonutils.NewString("root"), "account") // 用户名 - data.Add(jsonutils.NewString(encpasswd), "key") // 密码 - }*/ - return data, nil }) } else if action == "rebuild" { @@ -384,7 +236,7 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu } } - diskId, err := iVM.RebuildRoot(desc.ExternalImageId, passwd, publicKey, desc.SysDiskSize) + diskId, err := iVM.RebuildRoot(ctx, desc.ExternalImageId, passwd, publicKey, desc.SysDiskSize) if err != nil { return nil, err } @@ -407,20 +259,20 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu log.Errorf("fail to find VM idisks %s", err) return nil, err } - if len(idisks) < len(desc.DataDisks)+1 { + if len(idisks) < len(desc.DataDisks)+1 || idisks[0].GetGlobalId() != diskId { 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) + if len(idisks) < len(desc.DataDisks)+1 { + log.Debugf("inconsistent disk number???? %d != %d", len(idisks), len(desc.DataDisks)+1) + } + if idisks[0].GetGlobalId() != diskId { + log.Errorf("system disk id inconsistent %s != %s", idisks[0].GetGlobalId(), diskId) + } 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 } } @@ -438,75 +290,6 @@ func (self *SAliyunGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu return nil } -func (self *SAliyunGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error { - - if data.Contains("disks") { - diskInfo := make([]SDiskInfo, 0) - err := data.Unmarshal(&diskInfo, "disks") - if err != nil { - return err - } - disks := guest.GetDisks() - if len(disks) != len(diskInfo) { - msg := fmt.Sprintf("inconsistent disk number: have %d want %d", len(disks), len(diskInfo)) - log.Errorf(msg) - return fmt.Errorf(msg) - } - for i := 0; i < len(diskInfo); i += 1 { - disk := disks[i].GetDisk() - _, err = disk.GetModelManager().TableSpec().Update(disk, func() error { - disk.DiskSize = diskInfo[i].Size - disk.ExternalId = diskInfo[i].Uuid - disk.DiskType = diskInfo[i].DiskType - disk.Status = models.DISK_READY - disk.BillingType = diskInfo[i].BillingType - disk.FsFormat = diskInfo[i].FsFromat - if diskInfo[i].AutoDelete { - disk.AutoDelete = true - } - // disk.TemplateId = diskInfo[i].TemplateId - disk.DiskFormat = diskInfo[i].DiskFormat - disk.ExpiredAt = diskInfo[i].ExpiredAt - 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(msg) - break - } else { - db.OpsLog.LogEvent(disk, db.ACT_ALLOCATE, disk.GetShortDesc(), task.GetUserCred()) - } - } - } - uuid, _ := data.GetString("uuid") - if len(uuid) > 0 { - guest.SetExternalId(uuid) - } - - 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) - } - } - } - } - - guest.SaveDeployInfo(ctx, task.GetUserCred(), data) - return nil -} - func (self *SAliyunGuestDriver) AllowReconfigGuest() bool { return true } diff --git a/pkg/compute/guestdrivers/aws.go b/pkg/compute/guestdrivers/aws.go index fd3198f1b1..fb272c2a55 100644 --- a/pkg/compute/guestdrivers/aws.go +++ b/pkg/compute/guestdrivers/aws.go @@ -9,7 +9,6 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" - "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" @@ -20,6 +19,11 @@ type SAwsGuestDriver struct { SManagedVirtualizedGuestDriver } +func init() { + driver := SAwsGuestDriver{} + models.RegisterGuestDriver(&driver) +} + func (self *SAwsGuestDriver) GetHypervisor() string { return models.HYPERVISOR_AWS } @@ -162,7 +166,7 @@ func (self *SAwsGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest deleteKeypair := jsonutils.QueryBoolean(params, "__delete_keypair__", false) taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - err := iVM.DeployVM(name, passwd, publicKey, deleteKeypair, description) + err := iVM.DeployVM(ctx, name, passwd, publicKey, deleteKeypair, description) if err != nil { return nil, err } @@ -178,7 +182,7 @@ func (self *SAwsGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest } taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - diskId, err := iVM.RebuildRoot(desc.ExternalImageId, passwd, publicKey, desc.SysDiskSize) + diskId, err := iVM.RebuildRoot(ctx, desc.ExternalImageId, passwd, publicKey, desc.SysDiskSize) if err != nil { return nil, err } @@ -230,74 +234,3 @@ func (self *SAwsGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest return nil } - -func (self *SAwsGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error { - if data.Contains("disks") { - diskInfo := make([]SDiskInfo, 0) - err := data.Unmarshal(&diskInfo, "disks") - if err != nil { - return err - } - disks := guest.GetDisks() - if len(disks) != len(diskInfo) { - msg := fmt.Sprintf("inconsistent disk number: have %d want %d", len(disks), len(diskInfo)) - log.Errorf(msg) - return fmt.Errorf(msg) - } - for i := 0; i < len(diskInfo); i += 1 { - disk := disks[i].GetDisk() - _, err = disk.GetModelManager().TableSpec().Update(disk, func() error { - disk.DiskSize = diskInfo[i].Size - disk.ExternalId = diskInfo[i].Uuid - disk.DiskType = diskInfo[i].DiskType - disk.Status = models.DISK_READY - disk.BillingType = diskInfo[i].BillingType - disk.FsFormat = diskInfo[i].FsFromat - disk.AutoDelete = true - //disk.TemplateId = diskInfo[i].TemplateId - disk.DiskFormat = diskInfo[i].DiskFormat - disk.ExpiredAt = diskInfo[i].ExpiredAt - 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(msg) - break - } else { - db.OpsLog.LogEvent(disk, db.ACT_ALLOCATE, disk.GetShortDesc(), task.GetUserCred()) - } - } - } - uuid, _ := data.GetString("uuid") - if len(uuid) > 0 { - guest.SetExternalId(uuid) - } - - 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) - } - } - } - } - - guest.SaveDeployInfo(ctx, task.GetUserCred(), data) - return nil -} - -func init() { - driver := SAwsGuestDriver{} - models.RegisterGuestDriver(&driver) -} diff --git a/pkg/compute/guestdrivers/azure.go b/pkg/compute/guestdrivers/azure.go index 3f2af8a3cd..4f85387fcc 100644 --- a/pkg/compute/guestdrivers/azure.go +++ b/pkg/compute/guestdrivers/azure.go @@ -16,7 +16,6 @@ import ( "yunion.io/x/pkg/utils" "yunion.io/x/jsonutils" - "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/compute/models" ) @@ -203,7 +202,7 @@ func (self *SAzureGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gue deleteKeypair := jsonutils.QueryBoolean(params, "__delete_keypair__", false) taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - err := iVM.DeployVM(name, passwd, publicKey, deleteKeypair, description) + err := iVM.DeployVM(ctx, name, passwd, publicKey, deleteKeypair, description) if err != nil { return nil, err } @@ -218,7 +217,7 @@ func (self *SAzureGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gue } taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - _, err := iVM.RebuildRoot(desc.ExternalImageId, passwd, publicKey, desc.SysDiskSize) + _, err := iVM.RebuildRoot(ctx, desc.ExternalImageId, passwd, publicKey, desc.SysDiskSize) if err != nil { return nil, err } @@ -235,73 +234,6 @@ func (self *SAzureGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gue return nil } -func (self *SAzureGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error { - - if data.Contains("disks") { - diskInfo := make([]SDiskInfo, 0) - err := data.Unmarshal(&diskInfo, "disks") - if err != nil { - return err - } - disks := guest.GetDisks() - if len(disks) != len(diskInfo) { - msg := fmt.Sprintf("inconsistent disk number: have %d want %d", len(disks), len(diskInfo)) - log.Errorf(msg) - return fmt.Errorf(msg) - } - for i := 0; i < len(diskInfo); i++ { - disk := disks[i].GetDisk() - _, err = disk.GetModelManager().TableSpec().Update(disk, func() error { - disk.DiskSize = diskInfo[i].Size - disk.ExternalId = diskInfo[i].Uuid - disk.DiskType = diskInfo[i].DiskType - disk.Status = models.DISK_READY - disk.BillingType = diskInfo[i].BillingType - disk.FsFormat = diskInfo[i].FsFromat - disk.AutoDelete = diskInfo[i].AutoDelete - // disk.TemplateId = diskInfo[i].TemplateId - disk.DiskFormat = diskInfo[i].DiskFormat - disk.ExpiredAt = diskInfo[i].ExpiredAt - 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(msg) - break - } else { - db.OpsLog.LogEvent(disk, db.ACT_ALLOCATE, disk.GetShortDesc(), task.GetUserCred()) - } - } - } - uuid, _ := data.GetString("uuid") - if len(uuid) > 0 { - guest.SetExternalId(uuid) - } - - 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) - } - } - } - } - - guest.SaveDeployInfo(ctx, task.GetUserCred(), data) - return nil -} - func (self *SAzureGuestDriver) 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() @@ -355,12 +287,12 @@ func (self *SAzureGuestDriver) RequestSyncConfigOnHost(ctx context.Context, gues return nil, err } for _, disk := range removed { - if err := iVM.DetachDisk(disk.GetId()); err != nil { + if err := iVM.DetachDisk(ctx, disk.GetId()); err != nil { return nil, err } } for _, disk := range added { - if err := iVM.AttachDisk(disk.ExternalId); err != nil { + if err := iVM.AttachDisk(ctx, disk.ExternalId); err != nil { return nil, err } } diff --git a/pkg/compute/guestdrivers/base.go b/pkg/compute/guestdrivers/base.go index b3aed3eb01..9717dd65e8 100644 --- a/pkg/compute/guestdrivers/base.go +++ b/pkg/compute/guestdrivers/base.go @@ -189,9 +189,5 @@ func (self *SBaseGuestDriver) RequestSyncToBackup(ctx context.Context, guest *mo } func (self *SBaseGuestDriver) getTaskRequestHeader(task taskman.ITask) http.Header { - header := http.Header{} - header.Set(mcclient.AUTH_TOKEN, task.GetUserCred().GetTokenString()) - header.Set(mcclient.TASK_ID, task.GetTaskId()) - header.Set(mcclient.REGION_VERSION, "v2") - return header + return task.GetTaskRequestHeader() } diff --git a/pkg/compute/guestdrivers/container.go b/pkg/compute/guestdrivers/container.go index 5718a65521..431ce30468 100644 --- a/pkg/compute/guestdrivers/container.go +++ b/pkg/compute/guestdrivers/container.go @@ -112,6 +112,10 @@ func (self *SContainerDriver) OnGuestDeployTaskComplete(ctx context.Context, gue return nil } +func (self *SContainerDriver) GetJsonDescAtHost(ctx context.Context, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject { + return guest.GetJsonDescAtHypervisor(ctx, host) +} + func (self *SContainerDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { config := guest.GetDeployConfigOnHost(ctx, host, task.GetParams()) config.Add(jsonutils.JSONTrue, "k8s_pod") diff --git a/pkg/compute/guestdrivers/esxi.go b/pkg/compute/guestdrivers/esxi.go index 7d06890ba5..98010911a3 100644 --- a/pkg/compute/guestdrivers/esxi.go +++ b/pkg/compute/guestdrivers/esxi.go @@ -2,6 +2,12 @@ package guestdrivers import ( "context" + "fmt" + "net/http" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/utils" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/compute/models" @@ -33,6 +39,10 @@ func (self *SESXiGuestDriver) GetAttachDiskStatus() ([]string, error) { return []string{models.VM_READY}, nil } +func (self *SESXiGuestDriver) GetChangeConfigStatus() ([]string, error) { + return []string{models.VM_READY}, nil +} + func (self *SESXiGuestDriver) CanKeepDetachDisk() bool { return false } @@ -50,3 +60,107 @@ func (self *SESXiGuestDriver) RequestGuestHotAddIso(ctx context.Context, guest * task.ScheduleRun(nil) return nil } + +func (self *SESXiGuestDriver) GetRebuildRootStatus() ([]string, error) { + return []string{models.VM_READY}, nil +} + +func (self *SESXiGuestDriver) GetDeployStatus() ([]string, error) { + return []string{models.VM_READY}, nil +} + +func (self *SESXiGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *models.SDisk, storage *models.SStorage) error { + if !utils.IsInStringArray(guest.Status, []string{models.VM_READY}) { + return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status) + } + if disk.DiskType == models.DISK_TYPE_SYS { + return fmt.Errorf("Cannot resize system disk") + } + /*if !utils.IsInStringArray(storage.StorageType, []string{models.STORAGE_PUBLIC_CLOUD, models.STORAGE_CLOUD_SSD, models.STORAGE_CLOUD_EFFICIENCY}) { + return fmt.Errorf("Cannot resize %s disk", storage.StorageType) + }*/ + return nil +} + +func (self *SESXiGuestDriver) RequestDetachDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { + return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId()) +} + +func (self *SESXiGuestDriver) GetJsonDescAtHost(ctx context.Context, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject { + return guest.GetJsonDescAtHypervisor(ctx, host) +} + +func (self *SESXiGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { + config := guest.GetDeployConfigOnHost(ctx, host, task.GetParams()) + log.Debugf("RequestDeployGuestOnHost: %s", config) + + agent, err := host.GetEsxiAgentHost() + if err != nil { + return err + } + if agent == nil { + return fmt.Errorf("No ESXi agent host") + } + + diskCat := guest.CategorizeDisks() + if diskCat.Root == nil { + return fmt.Errorf("no root disk???") + } + storage := diskCat.Root.GetStorage() + if storage == nil { + return fmt.Errorf("root disk has no storage???") + } + + config.Add(jsonutils.NewString(host.AccessIp), "host_ip") + config.Add(jsonutils.NewString(guest.Id), "guest_id") + + accessInfo, err := host.GetCloudaccount().GetVCenterAccessInfo(storage.ExternalId) + if err != nil { + return err + } + config.Add(jsonutils.Marshal(accessInfo), "datastore") + + url := "/disks/agent/deploy" + + body := jsonutils.NewDict() + body.Add(config, "disk") + + header := http.Header{} + header.Add("X-Task-Id", task.GetTaskId()) + header.Add("X-Region-Version", "v2") + + _, err = agent.Request(task.GetUserCred(), "POST", url, header, body) + return err +} + +func (self *SESXiGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error { + + if data.Contains("host_ip") { + hostIp, _ := data.GetString("host_ip") + host, err := models.HostManager.GetHostByIp(hostIp) + if err != nil { + log.Errorf("fail to find host with IP %s: %s", hostIp, err) + return err + } + if host.Id != guest.HostId { + models.HostManager.ClearSchedDescCache(host.Id) + models.HostManager.ClearSchedDescCache(guest.HostId) + guest.SetHostId(host.Id) + } + } + + return self.SManagedVirtualizedGuestDriver.OnGuestDeployTaskDataReceived(ctx, guest, task, data) +} + +func (self *SESXiGuestDriver) AllowReconfigGuest() bool { + return true +} + +func (self *SESXiGuestDriver) DoGuestCreateDisksTask(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { + subtask, err := taskman.TaskManager.NewTask(ctx, "ESXiGuestCreateDiskTask", guest, task.GetUserCred(), task.GetParams(), task.GetTaskId(), "", nil) + if err != nil { + return err + } + subtask.ScheduleRun(nil) + return nil +} diff --git a/pkg/compute/guestdrivers/kvm.go b/pkg/compute/guestdrivers/kvm.go index 9f61b6b96d..78a916e22d 100644 --- a/pkg/compute/guestdrivers/kvm.go +++ b/pkg/compute/guestdrivers/kvm.go @@ -179,6 +179,10 @@ func (self *SKVMGuestDriver) RequestUndeployGuestOnHost(ctx context.Context, gue return nil } +func (self *SKVMGuestDriver) GetJsonDescAtHost(ctx context.Context, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject { + return guest.GetJsonDescAtHypervisor(ctx, host) +} + func (self *SKVMGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { config := guest.GetDeployConfigOnHost(ctx, host, task.GetParams()) log.Debugf("RequestDeployGuestOnHost: %s", config) @@ -207,7 +211,7 @@ func (self *SKVMGuestDriver) RequestStartOnHost(ctx context.Context, guest *mode header := self.getTaskRequestHeader(task) config := jsonutils.NewDict() - desc := self.GetJsonDescAtHost(ctx, guest, host) + desc := guest.GetDriver().GetJsonDescAtHost(ctx, guest, host) config.Add(desc, "desc") params := task.GetParams() if params.Length() > 0 { diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index d4b4b52360..e322254008 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -10,6 +10,7 @@ import ( "yunion.io/x/pkg/util/compare" "yunion.io/x/pkg/util/secrules" + "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" @@ -116,11 +117,7 @@ func (self *SManagedVirtualizedGuestDriver) RequestDeployGuestOnHost(ctx context return nil } -func (self *SManagedVirtualizedGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error { - return nil -} - -func (self *SManagedVirtualizedGuestDriver) RequestStartOnHost(_ context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) (jsonutils.JSONObject, error) { +func (self *SManagedVirtualizedGuestDriver) RequestStartOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) (jsonutils.JSONObject, error) { ihost, e := host.GetIHost() if e != nil { return nil, e @@ -133,7 +130,7 @@ func (self *SManagedVirtualizedGuestDriver) RequestStartOnHost(_ context.Context result := jsonutils.NewDict() if ivm.GetStatus() != models.VM_RUNNING { - if err := ivm.StartVM(); err != nil { + if err := ivm.StartVM(ctx); err != nil { return nil, e } else { task.ScheduleRun(result) @@ -149,18 +146,20 @@ func (self *SManagedVirtualizedGuestDriver) RequestUndeployGuestOnHost(ctx conte taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { ihost, err := host.GetIHost() if err != nil { + log.Errorf("host.GetIHost fail %s", err) return nil, err } ivm, err := ihost.GetIVMById(guest.ExternalId) if err != nil { if err == cloudprovider.ErrNotFound { return nil, nil - } else { - return nil, err } + log.Errorf("ihost.GetIVMById fail %s", err) + return nil, err } - err = ivm.DeleteVM() + err = ivm.DeleteVM(ctx) if err != nil { + log.Errorf("ivm.DeleteVM fail %s", err) return nil, err } @@ -170,12 +169,13 @@ func (self *SManagedVirtualizedGuestDriver) RequestUndeployGuestOnHost(ctx conte if err != nil { if err == cloudprovider.ErrNotFound { continue - } else { - return nil, err } + log.Errorf("disk.GetIDisk fail %s", err) + return nil, err } - err = idisk.Delete() + err = idisk.Delete(ctx) if err != nil { + log.Errorf("idisk.Delete fail %s", err) return nil, err } } @@ -195,7 +195,7 @@ func (self *SManagedVirtualizedGuestDriver) RequestStopOnHost(ctx context.Contex if err != nil { return nil, err } - err = ivm.StopVM(true) + err = ivm.StopVM(ctx, true) return nil, err }) return nil @@ -293,7 +293,7 @@ func (self *SManagedVirtualizedGuestDriver) RequestChangeVmConfig(ctx context.Co } if int(guest.VcpuCount) != config.Cpu || guest.VmemSize != config.Memory { - err = iVM.ChangeConfig(config.InstanceId, config.Cpu, config.Memory) + err = iVM.ChangeConfig(ctx, config.Cpu, config.Memory) if err != nil { return err } @@ -311,7 +311,7 @@ func (self *SManagedVirtualizedGuestDriver) RequestDiskSnapshot(ctx context.Cont iSnapshot, _ := models.SnapshotManager.FetchById(snapshotId) snapshot := iSnapshot.(*models.SSnapshot) taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - cloudSnapshot, err := providerDisk.CreateISnapshot(snapshot.Name, "") + cloudSnapshot, err := providerDisk.CreateISnapshot(ctx, snapshot.Name, "") if err != nil { return nil, err } @@ -322,6 +322,87 @@ func (self *SManagedVirtualizedGuestDriver) RequestDiskSnapshot(ctx context.Cont return nil } +func (self *SManagedVirtualizedGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error { + + if data.Contains("disks") { + diskInfo := make([]SDiskInfo, 0) + err := data.Unmarshal(&diskInfo, "disks") + if err != nil { + return err + } + + disks := guest.GetDisks() + if len(disks) != len(diskInfo) { + msg := fmt.Sprintf("inconsistent disk number: have %d want %d", len(disks), len(diskInfo)) + log.Errorf(msg) + return fmt.Errorf(msg) + } + for i := 0; i < len(diskInfo); i += 1 { + disk := disks[i].GetDisk() + _, err = disk.GetModelManager().TableSpec().Update(disk, func() error { + disk.DiskSize = diskInfo[i].Size + disk.ExternalId = diskInfo[i].Uuid + disk.DiskType = diskInfo[i].DiskType + disk.Status = models.DISK_READY + disk.BillingType = diskInfo[i].BillingType + disk.FsFormat = diskInfo[i].FsFromat + if diskInfo[i].AutoDelete { + disk.AutoDelete = true + } + // disk.TemplateId = diskInfo[i].TemplateId + disk.AccessPath = diskInfo[i].Path + disk.DiskFormat = diskInfo[i].DiskFormat + disk.ExpiredAt = diskInfo[i].ExpiredAt + 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(msg) + break + } + db.OpsLog.LogEvent(disk, db.ACT_ALLOCATE, disk.GetShortDesc(), task.GetUserCred()) + guestdisk := guest.GetGuestDisk(disk.Id) + _, err = guestdisk.GetModelManager().TableSpec().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(msg) + break + } + } + } + uuid, _ := data.GetString("uuid") + if len(uuid) > 0 { + guest.SetExternalId(uuid) + } + + 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) + } + } + } + } + + guest.SaveDeployInfo(ctx, task.GetUserCred(), data) + return nil +} + func (self *SManagedVirtualizedGuestDriver) 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() @@ -378,12 +459,12 @@ func (self *SManagedVirtualizedGuestDriver) RequestSyncConfigOnHost(ctx context. return nil, err } for _, disk := range removed { - if err := iVM.DetachDisk(disk.GetId()); err != nil { + if err := iVM.DetachDisk(ctx, disk.GetId()); err != nil { return nil, err } } for _, disk := range added { - if err := iVM.AttachDisk(disk.ExternalId); err != nil { + if err := iVM.AttachDisk(ctx, disk.ExternalId); err != nil { return nil, err } } @@ -391,3 +472,51 @@ func (self *SManagedVirtualizedGuestDriver) RequestSyncConfigOnHost(ctx context. }) return nil } + + +/*func (self *SManagedVirtualizedGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + if ihost, err := host.GetIHost(); err != nil { + return nil, err + } else if iVM, err := ihost.GetIVMById(guest.ExternalId); err != nil { + return nil, err + } else { + if fw_only, _ := task.GetParams().Bool("fw_only"); fw_only { + if err := iVM.SyncSecurityGroup(guest.SecgrpId, guest.GetSecgroupName(), guest.GetSecRules()); err != nil { + return nil, err + } + } else { + if iDisks, err := iVM.GetIDisks(); err != nil { + return nil, err + } else { + 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 +}*/ \ No newline at end of file diff --git a/pkg/compute/guestdrivers/qcloud.go b/pkg/compute/guestdrivers/qcloud.go index 64c5b5a47f..c94dff4579 100644 --- a/pkg/compute/guestdrivers/qcloud.go +++ b/pkg/compute/guestdrivers/qcloud.go @@ -11,7 +11,6 @@ import ( "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/pkg/utils" - "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" @@ -202,7 +201,7 @@ func (self *SQcloudGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu // } // } - err := iVM.DeployVM(name, passwd, publicKey, deleteKeypair, description) + err := iVM.DeployVM(ctx, name, passwd, publicKey, deleteKeypair, description) if err != nil { return nil, err } @@ -227,7 +226,7 @@ func (self *SQcloudGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu // } // } - diskId, err := iVM.RebuildRoot(desc.ExternalImageId, passwd, publicKey, desc.SysDiskSize) + diskId, err := iVM.RebuildRoot(ctx, desc.ExternalImageId, passwd, publicKey, desc.SysDiskSize) if err != nil { return nil, err } @@ -281,73 +280,6 @@ func (self *SQcloudGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu return nil } -func (self *SQcloudGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error { - - if data.Contains("disks") { - diskInfo := make([]SDiskInfo, 0) - err := data.Unmarshal(&diskInfo, "disks") - if err != nil { - return err - } - disks := guest.GetDisks() - if len(disks) != len(diskInfo) { - msg := fmt.Sprintf("inconsistent disk number: have %d want %d", len(disks), len(diskInfo)) - log.Errorf(msg) - return fmt.Errorf(msg) - } - for i := 0; i < len(diskInfo); i += 1 { - disk := disks[i].GetDisk() - _, err = disk.GetModelManager().TableSpec().Update(disk, func() error { - disk.DiskSize = diskInfo[i].Size - disk.ExternalId = diskInfo[i].Uuid - disk.DiskType = diskInfo[i].DiskType - disk.Status = models.DISK_READY - disk.BillingType = diskInfo[i].BillingType - disk.FsFormat = diskInfo[i].FsFromat - disk.AutoDelete = true - //disk.TemplateId = diskInfo[i].TemplateId - disk.DiskFormat = diskInfo[i].DiskFormat - disk.ExpiredAt = diskInfo[i].ExpiredAt - 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(msg) - break - } else { - db.OpsLog.LogEvent(disk, db.ACT_ALLOCATE, disk.GetShortDesc(), task.GetUserCred()) - } - } - } - uuid, _ := data.GetString("uuid") - if len(uuid) > 0 { - guest.SetExternalId(uuid) - } - - 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) - } - } - } - } - - guest.SaveDeployInfo(ctx, task.GetUserCred(), data) - return nil -} - func (self *SQcloudGuestDriver) AllowReconfigGuest() bool { return true } diff --git a/pkg/compute/guestdrivers/utils.go b/pkg/compute/guestdrivers/utils.go new file mode 100644 index 0000000000..ca594446ce --- /dev/null +++ b/pkg/compute/guestdrivers/utils.go @@ -0,0 +1,89 @@ +package guestdrivers + +import ( + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/utils" + + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SDiskInfo struct { + DiskType string + Size int + Uuid string + BillingType string + FsFromat string + AutoDelete bool + TemplateId string + DiskFormat string + Path string + Driver string + CacheMode string + ExpiredAt time.Time + + Metadata map[string]string +} + +func fetchIVMinfo(desc SManagedVMCreateConfig, iVM cloudprovider.ICloudVM, guestId string, account, passwd string, action string) *jsonutils.JSONDict { + data := jsonutils.NewDict() + + data.Add(jsonutils.NewString(iVM.GetOSType()), "os") + + if len(passwd) > 0 { + encpasswd, err := utils.EncryptAESBase64(guestId, passwd) + if err != nil { + log.Errorf("encrypt password failed %s", err) + } + data.Add(jsonutils.NewString(account), "account") + data.Add(jsonutils.NewString(encpasswd), "key") + } + + if len(desc.OsDistribution) > 0 { + data.Add(jsonutils.NewString(desc.OsDistribution), "distro") + } + if len(desc.OsVersion) > 0 { + data.Add(jsonutils.NewString(desc.OsVersion), "version") + } + + idisks, err := iVM.GetIDisks() + + if err != nil { + log.Errorf("GetiDisks error %s", err) + } else { + diskInfo := make([]SDiskInfo, len(idisks)) + for i := 0; i < len(idisks); i += 1 { + dinfo := SDiskInfo{} + dinfo.Uuid = idisks[i].GetGlobalId() + dinfo.Size = idisks[i].GetDiskSizeMB() + dinfo.DiskType = idisks[i].GetDiskType() + dinfo.BillingType = idisks[i].GetBillingType() + dinfo.DiskFormat = idisks[i].GetDiskFormat() + dinfo.AutoDelete = idisks[i].GetIsAutoDelete() + if action == "create" { + dinfo.AutoDelete = true + } + dinfo.Path = idisks[i].GetAccessPath() + dinfo.Driver = idisks[i].GetDriver() + dinfo.CacheMode = idisks[i].GetCacheMode() + dinfo.TemplateId = idisks[i].GetTemplateId() + dinfo.FsFromat = idisks[i].GetFsFormat() + dinfo.ExpiredAt = idisks[i].GetExpiredAt() + if metaData := idisks[i].GetMetadata(); metaData != nil { + dinfo.Metadata = make(map[string]string, 0) + if err := metaData.Unmarshal(dinfo.Metadata); err != nil { + log.Errorf("Get disk %s metadata info error: %v", idisks[i].GetName(), err) + } + } + diskInfo[i] = dinfo + } + data.Add(jsonutils.Marshal(&diskInfo), "disks") + } + + data.Add(jsonutils.NewString(iVM.GetGlobalId()), "uuid") + data.Add(iVM.GetMetadata(), "metadata") + + return data +} diff --git a/pkg/compute/guestdrivers/virtualization.go b/pkg/compute/guestdrivers/virtualization.go index f72a47eb81..79aa93d2b2 100644 --- a/pkg/compute/guestdrivers/virtualization.go +++ b/pkg/compute/guestdrivers/virtualization.go @@ -193,10 +193,6 @@ func (self *SVirtualizedGuestDriver) ValidateCreateHostData(ctx context.Context, return data, nil } -func (self *SVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Context, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject { - return guest.GetJsonDescAtHypervisor(ctx, host) -} - func (self *SVirtualizedGuestDriver) PerformStart(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, data *jsonutils.JSONDict) error { return guest.StartGueststartTask(ctx, userCred, data, "") } diff --git a/pkg/compute/hostdrivers/aliyun.go b/pkg/compute/hostdrivers/aliyun.go index 2c9df47e38..6d7fc57c7d 100644 --- a/pkg/compute/hostdrivers/aliyun.go +++ b/pkg/compute/hostdrivers/aliyun.go @@ -1,22 +1,11 @@ package hostdrivers import ( - "context" - "fmt" - "os" - - "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" ) type SAliyunHostDriver struct { - SBaseHostDriver + SManagedVirtualizationHostDriver } func init() { @@ -27,190 +16,3 @@ func init() { func (self *SAliyunHostDriver) GetHostType() string { return models.HOST_TYPE_ALIYUN } - -func (self *SAliyunHostDriver) CheckAndSetCacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error { - params := task.GetParams() - imageId, err := params.GetString("image_id") - if err != nil { - return err - } - - osArch, _ := params.GetString("os_arch") - osType, _ := params.GetString("os_type") - osDist, _ := params.GetString("os_distribution") - - isForce := jsonutils.QueryBoolean(params, "is_force", false) - userCred := task.GetUserCred() - taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - - lockman.LockRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId)) - defer lockman.ReleaseRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId)) - - scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), storageCache.Id, imageId) - - iStorageCache, err := storageCache.GetIStorageCache() - if err != nil { - return nil, err - } - - extImgId, err := iStorageCache.UploadImage(userCred, imageId, osArch, osType, osDist, scimg.ExternalId, isForce) - - if err != nil { - return nil, err - } else { - scimg.SetExternalId(extImgId) - - ret := jsonutils.NewDict() - ret.Add(jsonutils.NewString(extImgId), "image_id") - return ret, nil - } - }) - return nil -} - -func (self *SAliyunHostDriver) RequestPrepareSaveDiskOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask) error { - task.ScheduleRun(nil) - return nil -} - -func (self *SAliyunHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask, data jsonutils.JSONObject) error { - if iDisk, err := disk.GetIDisk(); err != nil { - return err - } else if iStorage, err := disk.GetIStorage(); err != nil { - return err - } else if iStoragecache := iStorage.GetIStoragecache(); iStoragecache == nil { - return httperrors.NewResourceNotFoundError("fail to find iStoragecache for storage: %s", iStorage.GetName()) - } else { - taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - if snapshot, err := iDisk.CreateISnapshot(fmt.Sprintf("Snapshot-%s", imageId), "PrepareSaveImage"); err != nil { - return nil, err - } else { - params := task.GetParams() - osType, _ := params.GetString("properties", "os_type") - - scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), iStoragecache.GetId(), imageId) - if scimg.Status != models.CACHED_IMAGE_STATUS_READY { - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHING, "request_prepare_save_disk_on_host") - } - if iImage, err := iStoragecache.CreateIImage(snapshot.GetId(), fmt.Sprintf("Image-%s", imageId), osType, ""); err != nil { - log.Errorf("fail to create iImage: %v", err) - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error()) - return nil, err - } else { - scimg.SetExternalId(iImage.GetId()) - if _, err := os.Stat(options.Options.TempPath); os.IsNotExist(err) { - if err = os.MkdirAll(options.Options.TempPath, 0755); err != nil { - return nil, err - } - } - if result, err := iStoragecache.DownloadImage(task.GetUserCred(), imageId, iImage.GetId(), options.Options.TempPath); err != nil { - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error()) - return nil, err - } else { - if err := iImage.Delete(); err != nil { - log.Errorf("Delete iImage %s failed: %v", iImage.GetId(), err) - } - if err := snapshot.Delete(); err != nil { - log.Errorf("Delete snapshot %s failed: %v", snapshot.GetId(), err) - } - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_READY, "") - return result, nil - } - } - } - }) - } - return nil -} - -func (self *SAliyunHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask, content *jsonutils.JSONDict) error { - if iCloudStorage, err := storage.GetIStorage(); err != nil { - return err - } else { - if size, err := content.Int("size"); err != nil { - return err - } else { - size = size >> 10 - if iDisk, err := iCloudStorage.CreateIDisk(disk.GetName(), int(size), ""); err != nil { - return err - } else { - if _, err := disk.GetModelManager().TableSpec().Update(disk, func() error { - disk.ExternalId = iDisk.GetGlobalId() - - if metaData := iDisk.GetMetadata(); metaData != nil { - meta := make(map[string]string) - if err := metaData.Unmarshal(meta); err != nil { - log.Errorf("Get disk %s Metadata error: %v", disk.Name, err) - } else { - for key, value := range meta { - 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 - }); err != nil { - log.Errorf("Update disk externalId err: %v", err) - return err - } - data := jsonutils.NewDict() - data.Add(jsonutils.NewInt(int64(iDisk.GetDiskSizeMB())), "disk_size") - data.Add(jsonutils.NewString(iDisk.GetDiskFormat()), "disk_format") - task.ScheduleRun(data) - } - } - } - return nil -} - -func (self *SAliyunHostDriver) RequestDeallocateDiskOnHost(host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask) error { - data := jsonutils.NewDict() - if iCloudStorage, err := storage.GetIStorage(); err != nil { - return err - } else if iDisk, err := iCloudStorage.GetIDisk(disk.GetExternalId()); err != nil { - if err == cloudprovider.ErrNotFound { - task.ScheduleRun(data) - return nil - } - return err - } else if err := iDisk.Delete(); err != nil { - return err - } - task.ScheduleRun(data) - return nil -} - -func (self *SAliyunHostDriver) RequestResizeDiskOnHostOnline(host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error { - return self.RequestResizeDiskOnHost(host, storage, disk, size, task) -} - -func (self *SAliyunHostDriver) RequestResizeDiskOnHost(host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error { - if iCloudStorage, err := storage.GetIStorage(); err != nil { - return err - } else if iDisk, err := iCloudStorage.GetIDisk(disk.GetExternalId()); err != nil { - return err - } else if err := iDisk.Resize(size >> 10); err != nil { - return err - } else { - task.ScheduleRun(jsonutils.Marshal(map[string]int64{"disk_size": size})) - } - return nil -} - -func (self *SAliyunHostDriver) RequestResetDisk(ctx context.Context, host *models.SHost, disk *models.SDisk, params *jsonutils.JSONDict, task taskman.ITask) error { - iDisk, err := disk.GetIDisk() - if err != nil { - return err - } - snapshotId, err := params.GetString("snapshot_id") - if err != nil { - return err - } - taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - err := iDisk.Reset(snapshotId) - return nil, err - }) - return nil -} diff --git a/pkg/compute/hostdrivers/aws.go b/pkg/compute/hostdrivers/aws.go index 255282bcc7..4a7fa74563 100644 --- a/pkg/compute/hostdrivers/aws.go +++ b/pkg/compute/hostdrivers/aws.go @@ -1,210 +1,18 @@ package hostdrivers import ( - "context" - "fmt" - "yunion.io/x/log" - "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" - "yunion.io/x/onecloud/pkg/cloudprovider" - "yunion.io/x/onecloud/pkg/compute/options" - "yunion.io/x/onecloud/pkg/httperrors" - - "yunion.io/x/jsonutils" - "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/compute/models" ) type SAwsHostDriver struct { - SBaseHostDriver -} - -func (self *SAwsHostDriver) GetHostType() string { - return models.HOST_TYPE_AWS -} - -func (self *SAwsHostDriver) CheckAndSetCacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error { - params := task.GetParams() - imageId, err := params.GetString("image_id") - if err != nil { - return err - } - - osArch, _ := params.GetString("os_arch") - osType, _ := params.GetString("os_type") - osDist, _ := params.GetString("os_distribution") - - isForce := jsonutils.QueryBoolean(params, "is_force", false) - userCred := task.GetUserCred() - taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - - lockman.LockRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId)) - defer lockman.ReleaseRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId)) - - scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), storageCache.Id, imageId) - - iStorageCache, err := storageCache.GetIStorageCache() - if err != nil { - return nil, err - } - - extImgId, err := iStorageCache.UploadImage(userCred, imageId, osArch, osType, osDist, scimg.ExternalId, isForce) - - if err != nil { - return nil, err - } else { - scimg.SetExternalId(extImgId) - - ret := jsonutils.NewDict() - ret.Add(jsonutils.NewString(extImgId), "image_id") - return ret, nil - } - }) - return nil -} - -func (self *SAwsHostDriver) RequestPrepareSaveDiskOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask) error { - task.ScheduleRun(nil) - return nil -} - -func (self *SAwsHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask, data jsonutils.JSONObject) error { - if iDisk, err := disk.GetIDisk(); err != nil { - return err - } else if iStorage, err := disk.GetIStorage(); err != nil { - return err - } else if iStoragecache := iStorage.GetIStoragecache(); iStoragecache == nil { - return httperrors.NewResourceNotFoundError("fail to find iStoragecache for storage: %s", iStorage.GetName()) - } else { - taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - if snapshot, err := iDisk.CreateISnapshot(fmt.Sprintf("Snapshot-%s", imageId), "PrepareSaveImage"); err != nil { - return nil, err - } else { - params := task.GetParams() - osType, _ := params.GetString("properties", "os_type") - - scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), iStoragecache.GetId(), imageId) - if scimg.Status != models.CACHED_IMAGE_STATUS_READY { - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHING, "request_prepare_save_disk_on_host") - } - if iImage, err := iStoragecache.CreateIImage(snapshot.GetId(), fmt.Sprintf("Image-%s", imageId), osType, ""); err != nil { - log.Errorf("fail to create iImage: %v", err) - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error()) - return nil, err - } else { - scimg.SetExternalId(iImage.GetId()) - if result, err := iStoragecache.DownloadImage(task.GetUserCred(), imageId, iImage.GetId(), options.Options.TempPath); err != nil { - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error()) - return nil, err - } else { - if err := iImage.Delete(); err != nil { - log.Errorf("Delete iImage %s failed: %v", iImage.GetId(), err) - } - if err := snapshot.Delete(); err != nil { - log.Errorf("Delete snapshot %s failed: %v", snapshot.GetId(), err) - } - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_READY, "") - return result, nil - } - } - } - }) - } - return nil -} - -func (self *SAwsHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask, content *jsonutils.JSONDict) error { - if iCloudStorage, err := storage.GetIStorage(); err != nil { - return err - } else { - if size, err := content.Int("size"); err != nil { - return err - } else { - size = size >> 10 - if iDisk, err := iCloudStorage.CreateIDisk(disk.GetName(), int(size), ""); err != nil { - return err - } else { - if _, err := disk.GetModelManager().TableSpec().Update(disk, func() error { - disk.ExternalId = iDisk.GetGlobalId() - - if metaData := iDisk.GetMetadata(); metaData != nil { - meta := make(map[string]string) - if err := metaData.Unmarshal(meta); err != nil { - log.Errorf("Get disk %s Metadata error: %v", disk.Name, err) - } else { - for key, value := range meta { - 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 - }); err != nil { - log.Errorf("Update disk externalId err: %v", err) - return err - } - data := jsonutils.NewDict() - data.Add(jsonutils.NewInt(int64(iDisk.GetDiskSizeMB())), "disk_size") - data.Add(jsonutils.NewString(iDisk.GetDiskFormat()), "disk_format") - task.ScheduleRun(data) - } - } - } - return nil -} - -func (self *SAwsHostDriver) RequestDeallocateDiskOnHost(host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask) error { - data := jsonutils.NewDict() - if iCloudStorage, err := storage.GetIStorage(); err != nil { - return err - } else if iDisk, err := iCloudStorage.GetIDisk(disk.GetExternalId()); err != nil { - if err == cloudprovider.ErrNotFound { - task.ScheduleRun(data) - return nil - } - return err - } else if err := iDisk.Delete(); err != nil { - return err - } - task.ScheduleRun(data) - return nil -} - -func (self *SAwsHostDriver) RequestResizeDiskOnHostOnline(host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error { - return self.RequestResizeDiskOnHost(host, storage, disk, size, task) -} - -func (self *SAwsHostDriver) RequestResizeDiskOnHost(host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error { - if iCloudStorage, err := storage.GetIStorage(); err != nil { - return err - } else if iDisk, err := iCloudStorage.GetIDisk(disk.GetExternalId()); err != nil { - return err - } else if err := iDisk.Resize(size >> 10); err != nil { - return err - } else { - task.ScheduleRun(jsonutils.Marshal(map[string]int64{"disk_size": size})) - } - return nil -} - -func (self *SAwsHostDriver) RequestResetDisk(ctx context.Context, host *models.SHost, disk *models.SDisk, params *jsonutils.JSONDict, task taskman.ITask) error { - iDisk, err := disk.GetIDisk() - if err != nil { - return err - } - snapshotId, err := params.GetString("snapshot_id") - if err != nil { - return err - } - taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - err := iDisk.Reset(snapshotId) - return nil, err - }) - return nil + SManagedVirtualizationHostDriver } func init() { driver := SAwsHostDriver{} models.RegisterHostDriver(&driver) } + +func (self *SAwsHostDriver) GetHostType() string { + return models.HOST_TYPE_AWS +} diff --git a/pkg/compute/hostdrivers/azure.go b/pkg/compute/hostdrivers/azure.go index 6635e9c879..d2552d309c 100644 --- a/pkg/compute/hostdrivers/azure.go +++ b/pkg/compute/hostdrivers/azure.go @@ -2,22 +2,15 @@ package hostdrivers import ( "context" - "fmt" - "os" - "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" ) type SAzureHostDriver struct { - SBaseHostDriver + SManagedVirtualizationHostDriver } func init() { @@ -36,174 +29,6 @@ func (self *SAzureHostDriver) ValidateUpdateDisk(ctx context.Context, userCred m return data, nil } -func (self *SAzureHostDriver) CheckAndSetCacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error { - params := task.GetParams() - imageId, err := params.GetString("image_id") - if err != nil { - return err - } - - osArch, _ := params.GetString("os_arch") - osType, _ := params.GetString("os_type") - osDist, _ := params.GetString("os_distribution") - - isForce := jsonutils.QueryBoolean(params, "is_force", false) - userCred := task.GetUserCred() - taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - lockman.LockRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId)) - defer lockman.ReleaseRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId)) - - scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), storageCache.Id, imageId) - iStorageCache, err := storageCache.GetIStorageCache() - if err != nil { - return nil, err - } - - extImgId, err := iStorageCache.UploadImage(userCred, imageId, osArch, osType, osDist, scimg.ExternalId, isForce) - if err != nil { - return nil, err - } else { - scimg.SetExternalId(extImgId) - - ret := jsonutils.NewDict() - ret.Add(jsonutils.NewString(extImgId), "image_id") - return ret, nil - } - }) - return nil -} - -func (self *SAzureHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask, content *jsonutils.JSONDict) error { - if iCloudStorage, err := storage.GetIStorage(); err != nil { - return err - } else { - if size, err := content.Int("size"); err != nil { - return err - } else { - size = size >> 10 - if iDisk, err := iCloudStorage.CreateIDisk(disk.GetName(), int(size), ""); err != nil { - return err - } else { - if _, err := disk.GetModelManager().TableSpec().Update(disk, func() error { - disk.ExternalId = iDisk.GetGlobalId() - - if metaData := iDisk.GetMetadata(); metaData != nil { - meta := make(map[string]string) - if err := metaData.Unmarshal(meta); err != nil { - log.Errorf("Get disk %s Metadata error: %v", disk.Name, err) - } else { - for key, value := range meta { - 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 - }); err != nil { - log.Errorf("Update disk externalId err: %v", err) - return err - } - data := jsonutils.NewDict() - data.Add(jsonutils.NewInt(int64(iDisk.GetDiskSizeMB())), "disk_size") - data.Add(jsonutils.NewString(iDisk.GetDiskFormat()), "disk_format") - task.ScheduleRun(data) - } - } - } - return nil -} - -func (self *SAzureHostDriver) RequestDeallocateDiskOnHost(host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask) error { - data := jsonutils.NewDict() - if iCloudStorage, err := storage.GetIStorage(); err != nil { - return err - } else if iDisk, err := iCloudStorage.GetIDisk(disk.GetExternalId()); err != nil { - if err == cloudprovider.ErrNotFound { - task.ScheduleRun(data) - return nil - } - return err - } else if err := iDisk.Delete(); err != nil { - return err - } - task.ScheduleRun(data) - return nil -} - -func (self *SAzureHostDriver) RequestResizeDiskOnHostOnline(host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error { - return self.RequestResizeDiskOnHost(host, storage, disk, size, task) -} - -func (self *SAzureHostDriver) RequestResizeDiskOnHost(host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error { - if iCloudStorage, err := storage.GetIStorage(); err != nil { - return err - } else if iDisk, err := iCloudStorage.GetIDisk(disk.GetExternalId()); err != nil { - return err - } else if err := iDisk.Resize(size >> 10); err != nil { - return err - } else { - task.ScheduleRun(jsonutils.Marshal(map[string]int64{"disk_size": size})) - } - return nil -} - -func (self *SAzureHostDriver) RequestPrepareSaveDiskOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask) error { - task.ScheduleRun(nil) - return nil -} - -func (self *SAzureHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask, data jsonutils.JSONObject) error { - if iDisk, err := disk.GetIDisk(); err != nil { - return err - } else if iStorage, err := disk.GetIStorage(); err != nil { - return err - } else if iStoragecache := iStorage.GetIStoragecache(); iStoragecache == nil { - return httperrors.NewResourceNotFoundError("fail to find iStoragecache for storage: %s", iStorage.GetName()) - } else { - taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - if snapshot, err := iDisk.CreateISnapshot(fmt.Sprintf("Snapshot-%s", imageId), "PrepareSaveImage"); err != nil { - return nil, err - } else { - params := task.GetParams() - osType, _ := params.GetString("properties", "os_type") - - scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), iStoragecache.GetId(), imageId) - if scimg.Status != models.CACHED_IMAGE_STATUS_READY { - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHING, "request_prepare_save_disk_on_host") - } - if iImage, err := iStoragecache.CreateIImage(snapshot.GetId(), fmt.Sprintf("Image-%s", imageId), osType, ""); err != nil { - log.Errorf("fail to create iImage: %v", err) - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error()) - return nil, err - } else { - scimg.SetExternalId(iImage.GetId()) - if _, err := os.Stat(options.Options.TempPath); os.IsNotExist(err) { - if err = os.MkdirAll(options.Options.TempPath, 0755); err != nil { - return nil, err - } - } - if result, err := iStoragecache.DownloadImage(task.GetUserCred(), imageId, iImage.GetId(), options.Options.TempPath); err != nil { - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error()) - return nil, err - } else { - if err := iImage.Delete(); err != nil { - log.Errorf("Delete iImage %s failed: %v", iImage.GetId(), err) - } - if err := snapshot.Delete(); err != nil { - log.Errorf("Delete snapshot %s failed: %v", snapshot.GetId(), err) - } - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_READY, "") - return result, nil - } - } - } - }) - } - return nil -} - func (self *SAzureHostDriver) RequestDeleteSnapshotWithStorage(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, task taskman.ITask) error { return httperrors.NewNotImplementedError("not implement") } diff --git a/pkg/compute/hostdrivers/esxi.go b/pkg/compute/hostdrivers/esxi.go new file mode 100644 index 0000000000..d7d1158007 --- /dev/null +++ b/pkg/compute/hostdrivers/esxi.go @@ -0,0 +1,259 @@ +package hostdrivers + +import ( + "context" + "fmt" + "yunion.io/x/jsonutils" + "yunion.io/x/log" + + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/util/httputils" +) + +type SESXiHostDriver struct { + SManagedVirtualizationHostDriver +} + +func init() { + driver := SESXiHostDriver{} + models.RegisterHostDriver(&driver) +} + +func (self *SESXiHostDriver) GetHostType() string { + return models.HOST_TYPE_ESXI +} + +func (self *SESXiHostDriver) CheckAndSetCacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error { + params := task.GetParams() + imageId, err := params.GetString("image_id") + if err != nil { + return err + } + isForce := jsonutils.QueryBoolean(params, "is_force", false) + obj, err := models.CachedimageManager.FetchById(imageId) + if err != nil { + return err + } + cacheImage := obj.(*models.SCachedimage) + srcHostCacheImage, err := cacheImage.ChooseSourceStoragecacheInRange(models.HOST_TYPE_ESXI, []string{host.Id}, + []interface{}{host.GetZone(), host.GetCloudprovider()}) + if err != nil { + return err + } + + type contentStruct struct { + ImageId string + HostId string + HostIp string + SrcHostIp string + SrcPath string + SrcDatastore models.SVCenterAccessInfo + Datastore models.SVCenterAccessInfo + Format string + IsForce bool + StoragecacheId string + } + + content := contentStruct{} + content.ImageId = imageId + content.HostId = host.Id + content.HostIp = host.AccessIp + content.Format = cacheImage.GetFormat() + + storage := host.GetStorageByFilePath(storageCache.Path) + accessInfo, err := host.GetCloudaccount().GetVCenterAccessInfo(storage.ExternalId) + if err != nil { + return err + } + content.Datastore = accessInfo + + if srcHostCacheImage != nil { + err = srcHostCacheImage.AddDownloadRefcount() + if err != nil { + return err + } + srcHost, err := srcHostCacheImage.GetHost() + if err != nil { + return err + } + content.SrcHostIp = srcHost.AccessIp + content.SrcPath = srcHostCacheImage.Path + srcStorage := srcHost.GetStorageByFilePath(srcHostCacheImage.Path) + accessInfo, err := srcHost.GetCloudaccount().GetVCenterAccessInfo(srcStorage.ExternalId) + if err != nil { + return err + } + content.SrcDatastore = accessInfo + } + + agent, err := host.GetEsxiAgentHost() + if err != nil { + log.Errorf("find ESXi agent fail: %s", err) + return err + } + + if agent == nil { + return fmt.Errorf("fail to find valid ESXi agent") + } + + url := fmt.Sprintf("%s/disks/image_cache", agent.ManagerUri) + + if isForce { + content.IsForce = true + } + content.StoragecacheId = storageCache.Id + + body := jsonutils.NewDict() + body.Add(jsonutils.Marshal(&content), "disk") + + header := task.GetTaskRequestHeader() + + _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false) + if err != nil { + return err + } + return nil +} + +func (self *SESXiHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask, content *jsonutils.JSONDict) error { + agent, err := host.GetEsxiAgentHost() + if err != nil { + log.Errorf("find ESXi agent fail: %s", err) + return err + } + + if agent == nil { + return fmt.Errorf("fail to find valid ESXi agent") + } + + type specStruct struct { + Datastore models.SVCenterAccessInfo + HostIp string + Format string + } + + spec := specStruct{} + spec.HostIp = host.AccessIp + spec.Format = "vmdk" + + accessInfo, err := host.GetCloudaccount().GetVCenterAccessInfo(storage.ExternalId) + if err != nil { + return err + } + spec.Datastore = accessInfo + + body := jsonutils.NewDict() + body.Add(jsonutils.Marshal(&spec), "disk") + + url := fmt.Sprintf("/disks/agent/create/%s", disk.Id) + + header := task.GetTaskRequestHeader() + + _, err = agent.Request(task.GetUserCred(), "POST", url, header, body) + return err +} + +func (self *SESXiHostDriver) RequestPrepareSaveDiskOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask) error { + agent, err := host.GetEsxiAgentHost() + if err != nil { + log.Errorf("find ESXi agent fail: %s", err) + return err + } + + if agent == nil { + return fmt.Errorf("fail to find valid ESXi agent") + } + + guests := disk.GetGuests() + if len(guests) == 0 { + return fmt.Errorf("No VM associate with this disk") + } + + if len(guests) > 1 { + return fmt.Errorf("The disk is attached to multiple guests") + } + + guest := guests[0] + + if guest.HostId != host.Id { + return fmt.Errorf("The only guest is not on the host????") + } + + type specStruct struct { + Vm models.SVCenterAccessInfo + Disk models.SVCenterAccessInfo + HostIp string + ImageId string + } + + spec := specStruct{} + spec.HostIp = host.AccessIp + spec.ImageId = imageId + + account := host.GetCloudaccount() + accessInfo, err := account.GetVCenterAccessInfo(guest.ExternalId) + if err != nil { + return err + } + spec.Vm = accessInfo + + accessInfo, err = account.GetVCenterAccessInfo(disk.ExternalId) + if err != nil { + return err + } + spec.Disk = accessInfo + + body := jsonutils.NewDict() + body.Add(jsonutils.Marshal(&spec), "disk") + + url := fmt.Sprintf("/disks/agent/save-prepare/%s", disk.Id) + header := task.GetTaskRequestHeader() + + _, err = agent.Request(task.GetUserCred(), "POST", url, header, body) + return err +} + +func (self *SESXiHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask, data jsonutils.JSONObject) error { + + imagePath, _ := data.GetString("backup") + if len(imagePath) == 0 { + return fmt.Errorf("missing parameter backup") + } + agentId, _ := data.GetString("agent_id") + if len(agentId) == 0 { + return fmt.Errorf("missing parameter agent_id") + } + + agent := models.HostManager.FetchHostById(agentId) + if agent == nil { + return fmt.Errorf("cannot find host with id %s", agentId) + } + + storage := disk.GetStorage() + + type specStruct struct { + ImagePath string + ImageId string + StorageId string + StoragecacheId string + Compress bool `json:",allowempty"` + } + + spec := specStruct{} + spec.ImageId = imageId + spec.ImagePath = imagePath + spec.StorageId = storage.Id + spec.StoragecacheId = storage.StoragecacheId + spec.Compress = false + + body := jsonutils.NewDict() + body.Add(jsonutils.Marshal(&spec), "disk") + + url := "/disks/agent/upload" + + header := task.GetTaskRequestHeader() + + _, err := agent.Request(task.GetUserCred(), "POST", url, header, body) + return err +} diff --git a/pkg/compute/hostdrivers/kvm.go b/pkg/compute/hostdrivers/kvm.go index 57e9fad69a..0eb2149043 100644 --- a/pkg/compute/hostdrivers/kvm.go +++ b/pkg/compute/hostdrivers/kvm.go @@ -3,7 +3,6 @@ package hostdrivers import ( "context" "fmt" - "net/http" "net/url" "yunion.io/x/jsonutils" @@ -17,7 +16,7 @@ import ( ) type SKVMHostDriver struct { - SBaseHostDriver + SVirtualizationHostDriver } func init() { @@ -46,8 +45,16 @@ func (self *SKVMHostDriver) CheckAndSetCacheImage(ctx context.Context, host *mod return err } - content := jsonutils.NewDict() - content.Add(jsonutils.NewString(imageId), "image_id") + type contentStruct struct { + ImageId string + SrcUrl string + IsForce bool + StoragecacheId string + } + + content := contentStruct{} + content.ImageId = imageId + if srcHostCacheImage != nil { err = srcHostCacheImage.AddDownloadRefcount() if err != nil { @@ -57,21 +64,19 @@ func (self *SKVMHostDriver) CheckAndSetCacheImage(ctx context.Context, host *mod if err != nil { return err } - srcUrl := fmt.Sprintf("%s/download/images/%s", srcHost.ManagerUri, imageId) - content.Add(jsonutils.NewString(srcUrl), "src_url") + content.SrcUrl = fmt.Sprintf("%s/download/images/%s", srcHost.ManagerUri, imageId) } url := fmt.Sprintf("%s/disks/image_cache", host.ManagerUri) if isForce { - content.Add(jsonutils.NewBool(true), "is_force") + content.IsForce = true } - content.Add(jsonutils.NewString(storageCache.Id), "storagecache_id") + content.StoragecacheId = storageCache.Id body := jsonutils.NewDict() - body.Add(content, "disk") - header := http.Header{} - header.Set("X-Auth-Token", task.GetUserCred().GetTokenString()) - header.Set("X-Task-Id", task.GetTaskId()) - header.Set("X-Region-Version", "v2") + body.Add(jsonutils.Marshal(&content), "disk") + + header := task.GetTaskRequestHeader() + _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false) if err != nil { return err @@ -79,10 +84,39 @@ func (self *SKVMHostDriver) CheckAndSetCacheImage(ctx context.Context, host *mod return nil } +func (self *SKVMHostDriver) RequestUncacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error { + type contentStruct struct { + ImageId string + StoragecacheId string + } + + params := task.GetParams() + imageId, err := params.GetString("image_id") + if err != nil { + return err + } + + content := contentStruct{} + content.ImageId = imageId + content.StoragecacheId = storageCache.Id + + url := fmt.Sprintf("%s/disks/image_cache", host.ManagerUri) + + body := jsonutils.NewDict() + body.Add(jsonutils.Marshal(&content), "disk") + + header := task.GetTaskRequestHeader() + + _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "DELETE", url, header, body, false) + if err != nil { + return err + } + return nil +} + func (self *SKVMHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask, content *jsonutils.JSONDict) error { - header := http.Header{} - header.Add("X-Task-Id", task.GetTaskId()) - header.Add("X-Region-Version", "v2") + header := task.GetTaskRequestHeader() + url := fmt.Sprintf("/disks/%s/create/%s", storage.Id, disk.Id) body := jsonutils.NewDict() body.Add(content, "disk") @@ -90,40 +124,38 @@ func (self *SKVMHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, ho return err } -func (self *SKVMHostDriver) RequestDeallocateDiskOnHost(host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask) error { +func (self *SKVMHostDriver) RequestDeallocateDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask) error { log.Infof("Deallocating disk on host %s", host.GetName()) - header := http.Header{} - header.Add("X-Task-Id", task.GetTaskId()) - header.Add("X-Region-Version", "v2") + header := task.GetTaskRequestHeader() + url := fmt.Sprintf("/disks/%s/delete/%s", storage.Id, disk.Id) body := jsonutils.NewDict() _, err := host.Request(task.GetUserCred(), "POST", url, header, body) return err } -func (self *SKVMHostDriver) RequestResizeDiskOnHost(host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error { - header := http.Header{} - header.Add("X-Task-Id", task.GetTaskId()) - header.Add("X-Region-Version", "v2") +func (self *SKVMHostDriver) RequestResizeDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, sizeMb int64, task taskman.ITask) error { + header := task.GetTaskRequestHeader() + url := fmt.Sprintf("/disks/%s/resize/%s", storage.Id, disk.Id) body := jsonutils.NewDict() content := jsonutils.NewDict() - content.Add(jsonutils.NewInt(size), "size") + content.Add(jsonutils.NewInt(sizeMb), "size") body.Add(content, "disk") _, err := host.Request(task.GetUserCred(), "POST", url, header, body) return err } -func (self *SKVMHostDriver) RequestResizeDiskOnHostOnline(host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error { - self.RequestResizeDiskOnHost(host, storage, disk, size, task) - header := http.Header{} - header.Add("X-Task-Id", task.GetTaskId()) - header.Add("X-Region-Version", "v2") +func (self *SKVMHostDriver) RequestResizeDiskOnHostOnline(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, sizeMb int64, task taskman.ITask) error { + self.RequestResizeDiskOnHost(ctx, host, storage, disk, sizeMb, task) + + header := task.GetTaskRequestHeader() + for _, guest := range disk.GetAttachedGuests() { guestdisk := guest.GetGuestDisk(disk.GetId()) url := fmt.Sprintf("/servers/%s/monitor", guest.GetId()) body := jsonutils.NewDict() - cmd := fmt.Sprintf("block_resize drive_%d %dM", guestdisk.Index, size) + cmd := fmt.Sprintf("block_resize drive_%d %dM", guestdisk.Index, sizeMb) body.Add(jsonutils.NewString(cmd), "cmd") host.Request(task.GetUserCred(), "POST", url, header, body) } @@ -134,7 +166,9 @@ func (self *SKVMHostDriver) RequestPrepareSaveDiskOnHost(ctx context.Context, ho body := jsonutils.NewDict() body.Add(jsonutils.Marshal(map[string]string{"image_id": imageId}), "disk") url := fmt.Sprintf("/disks/%s/save-prepare/%s", disk.StorageId, disk.Id) - header := http.Header{"X-Task-Id": []string{task.GetTaskId()}, "X-Region-Version": []string{"v2"}} + + header := task.GetTaskRequestHeader() + _, err := host.Request(task.GetUserCred(), "POST", url, header, body) return err } @@ -148,7 +182,9 @@ func (self *SKVMHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, ho } body.Add(jsonutils.Marshal(content), "disk") url := fmt.Sprintf("/disks/%s/upload", disk.StorageId) - header := http.Header{"X-Task-Id": []string{task.GetTaskId()}, "X-Region-Version": []string{"v2"}} + + header := task.GetTaskRequestHeader() + _, err := host.Request(task.GetUserCred(), "POST", url, header, body) return err } @@ -157,27 +193,27 @@ func (self *SKVMHostDriver) RequestDeleteSnapshotsWithStorage(ctx context.Contex url := fmt.Sprintf("/storages/%s/delete-snapshots", snapshot.StorageId) body := jsonutils.NewDict() body.Set("disk_id", jsonutils.NewString(snapshot.DiskId)) - header := http.Header{} - header.Add("X-Task-Id", task.GetTaskId()) - header.Add("X-Region-Version", "v2") + + header := task.GetTaskRequestHeader() + _, err := host.Request(task.GetUserCred(), "POST", url, header, body) return err } func (self *SKVMHostDriver) RequestResetDisk(ctx context.Context, host *models.SHost, disk *models.SDisk, params *jsonutils.JSONDict, task taskman.ITask) error { url := fmt.Sprintf("/disks/%s/reset/%s", disk.StorageId, disk.Id) - header := http.Header{} - header.Add("X-Task-Id", task.GetTaskId()) - header.Add("X-Region-Version", "v2") + + header := task.GetTaskRequestHeader() + _, err := host.Request(task.GetUserCred(), "POST", url, header, params) return err } func (self *SKVMHostDriver) RequestCleanUpDiskSnapshots(ctx context.Context, host *models.SHost, disk *models.SDisk, params *jsonutils.JSONDict, task taskman.ITask) error { url := fmt.Sprintf("/disks/%s/cleanup-snapshots/%s", disk.StorageId, disk.Id) - header := http.Header{} - header.Add("X-Task-Id", task.GetTaskId()) - header.Add("X-Region-Version", "v2") + + header := task.GetTaskRequestHeader() + _, err := host.Request(task.GetUserCred(), "POST", url, header, params) return err } diff --git a/pkg/compute/hostdrivers/managedvirtual.go b/pkg/compute/hostdrivers/managedvirtual.go new file mode 100644 index 0000000000..fa0bf40e7f --- /dev/null +++ b/pkg/compute/hostdrivers/managedvirtual.go @@ -0,0 +1,289 @@ +package hostdrivers + +import ( + "context" + "fmt" + "os" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/httperrors" + + "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/options" + + "yunion.io/x/onecloud/pkg/compute/models" +) + +type SManagedVirtualizationHostDriver struct { + SVirtualizationHostDriver +} + +func (self *SManagedVirtualizationHostDriver) CheckAndSetCacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error { + params := task.GetParams() + imageId, err := params.GetString("image_id") + if err != nil { + return err + } + + osArch, _ := params.GetString("os_arch") + osType, _ := params.GetString("os_type") + osDist, _ := params.GetString("os_distribution") + + isForce := jsonutils.QueryBoolean(params, "is_force", false) + userCred := task.GetUserCred() + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + + lockman.LockRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId)) + defer lockman.ReleaseRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId)) + + scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), storageCache.Id, imageId) + + iStorageCache, err := storageCache.GetIStorageCache() + if err != nil { + return nil, err + } + + extImgId, err := iStorageCache.UploadImage(userCred, imageId, osArch, osType, osDist, scimg.ExternalId, isForce) + + if err != nil { + return nil, err + } + + // scimg.SetExternalId(extImgId) + + ret := jsonutils.NewDict() + ret.Add(jsonutils.NewString(extImgId), "image_id") + return ret, nil + }) + return nil +} + +func (self *SManagedVirtualizationHostDriver) RequestUncacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error { + params := task.GetParams() + imageId, err := params.GetString("image_id") + if err != nil { + return err + } + + scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), storageCache.Id, imageId) + if scimg == nil { + task.ScheduleRun(nil) + return nil + } + + if len(scimg.ExternalId) == 0 { + log.Errorf("cached image has not external ID???") + task.ScheduleRun(nil) + return nil + } + + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + lockman.LockRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId)) + defer lockman.ReleaseRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId)) + + iStorageCache, err := storageCache.GetIStorageCache() + if err != nil { + log.Errorf("GetIStorageCache fail %s", err) + return nil, err + } + + iImage, err := iStorageCache.GetIImageById(scimg.ExternalId) + if err != nil { + if err == cloudprovider.ErrNotFound { + return nil, nil + } + log.Errorf("GetIImageById fail %s", err) + return nil, err + } + + err = iImage.Delete(ctx) + if err != nil { + log.Errorf("iImage Delete fail %s", err) + return nil, err + } + + return nil, nil + }) + return nil +} + +func (self *SManagedVirtualizationHostDriver) RequestPrepareSaveDiskOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask) error { + task.ScheduleRun(nil) + return nil +} + +func (self *SManagedVirtualizationHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask, data jsonutils.JSONObject) error { + iDisk, err := disk.GetIDisk() + if err != nil { + return err + } + iStorage, err := disk.GetIStorage() + if err != nil { + return err + } + iStoragecache := iStorage.GetIStoragecache() + if iStoragecache == nil { + return httperrors.NewResourceNotFoundError("fail to find iStoragecache for storage: %s", iStorage.GetName()) + } + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + snapshot, err := iDisk.CreateISnapshot(ctx, fmt.Sprintf("Snapshot-%s", imageId), "PrepareSaveImage") + if err != nil { + return nil, err + } + params := task.GetParams() + osType, _ := params.GetString("properties", "os_type") + + scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), iStoragecache.GetId(), imageId) + if scimg.Status != models.CACHED_IMAGE_STATUS_READY { + scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHING, "request_prepare_save_disk_on_host") + } + iImage, err := iStoragecache.CreateIImage(snapshot.GetId(), fmt.Sprintf("Image-%s", imageId), osType, "") + if err != nil { + log.Errorf("fail to create iImage: %v", err) + scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error()) + return nil, err + } + scimg.SetExternalId(iImage.GetId()) + if _, err := os.Stat(options.Options.TempPath); os.IsNotExist(err) { + if err = os.MkdirAll(options.Options.TempPath, 0755); err != nil { + return nil, err + } + } + result, err := iStoragecache.DownloadImage(task.GetUserCred(), imageId, iImage.GetId(), options.Options.TempPath) + if err != nil { + scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error()) + return nil, err + } + if err := iImage.Delete(ctx); err != nil { + log.Errorf("Delete iImage %s failed: %v", iImage.GetId(), err) + } + if err := snapshot.Delete(); err != nil { + log.Errorf("Delete snapshot %s failed: %v", snapshot.GetId(), err) + } + scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_READY, "") + return result, nil + }) + return nil +} + +func (self *SManagedVirtualizationHostDriver) RequestResizeDiskOnHostOnline(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error { + return self.RequestResizeDiskOnHost(ctx, host, storage, disk, size, task) +} + +func (self *SManagedVirtualizationHostDriver) RequestResizeDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, sizeMb int64, task taskman.ITask) error { + iCloudStorage, err := storage.GetIStorage() + if err != nil { + log.Errorf("storage.GetIStorage fail %s", err) + return err + } + + iDisk, err := iCloudStorage.GetIDiskById(disk.GetExternalId()) + if err != nil { + log.Errorf("iCloudStorage.GetIDisk fail %s", err) + return err + } + + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + err = iDisk.Resize(ctx, sizeMb) + if err != nil { + log.Errorf("iDisk.Resize fail %s", err) + return nil, err + } + return jsonutils.Marshal(map[string]int64{"disk_size": sizeMb}), nil + }) + + return nil +} + +func (self *SManagedVirtualizationHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask, content *jsonutils.JSONDict) error { + iCloudStorage, err := storage.GetIStorage() + if err != nil { + return err + } + size, err := content.Int("size") + if err != nil { + return err + } + size = size >> 10 + + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + iDisk, err := iCloudStorage.CreateIDisk(disk.GetName(), int(size), "") + if err != nil { + return nil, err + } + _, err = disk.GetModelManager().TableSpec().Update(disk, func() error { + disk.ExternalId = iDisk.GetGlobalId() + return nil + }) + if err != nil { + log.Errorf("Update disk externalId err: %v", err) + return nil, err + } + + if metaData := iDisk.GetMetadata(); metaData != nil { + meta := make(map[string]string) + if err := metaData.Unmarshal(meta); err != nil { + log.Errorf("Get disk %s Metadata error: %v", disk.Name, err) + } else { + for key, value := range meta { + 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) + } + } + } + } + + data := jsonutils.NewDict() + data.Add(jsonutils.NewInt(int64(iDisk.GetDiskSizeMB())), "disk_size") + data.Add(jsonutils.NewString(iDisk.GetDiskFormat()), "disk_format") + + return data, nil + }) + + return nil +} + +func (self *SManagedVirtualizationHostDriver) RequestDeallocateDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask) error { + data := jsonutils.NewDict() + + iCloudStorage, err := storage.GetIStorage() + if err != nil { + return err + } + + iDisk, err := iCloudStorage.GetIDiskById(disk.GetExternalId()) + if err != nil { + if err == cloudprovider.ErrNotFound { + task.ScheduleRun(data) + return nil + } + return err + } + + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + err := iDisk.Delete(ctx) + return nil, err + }) + + return nil +} + +func (self *SAliyunHostDriver) RequestResetDisk(ctx context.Context, host *models.SHost, disk *models.SDisk, params *jsonutils.JSONDict, task taskman.ITask) error { + iDisk, err := disk.GetIDisk() + if err != nil { + return err + } + snapshotId, err := params.GetString("snapshot_id") + if err != nil { + return err + } + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + err := iDisk.Reset(ctx, snapshotId) + return nil, err + }) + return nil +} diff --git a/pkg/compute/hostdrivers/qcloud.go b/pkg/compute/hostdrivers/qcloud.go index 536c620c64..0cacf12199 100644 --- a/pkg/compute/hostdrivers/qcloud.go +++ b/pkg/compute/hostdrivers/qcloud.go @@ -2,21 +2,13 @@ package hostdrivers import ( "context" - "fmt" - "os" - - "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" ) type SQcloudHostDriver struct { - SBaseHostDriver + SManagedVirtualizationHostDriver } func init() { @@ -28,182 +20,6 @@ func (self *SQcloudHostDriver) GetHostType() string { return models.HOST_TYPE_QCLOUD } -func (self *SQcloudHostDriver) CheckAndSetCacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error { - params := task.GetParams() - imageId, err := params.GetString("image_id") - if err != nil { - return err - } - - osArch, _ := params.GetString("os_arch") - osType, _ := params.GetString("os_type") - osDist, _ := params.GetString("os_distribution") - - isForce := jsonutils.QueryBoolean(params, "is_force", false) - userCred := task.GetUserCred() - taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - lockman.LockRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId)) - defer lockman.ReleaseRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId)) - - scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), storageCache.Id, imageId) - iStorageCache, err := storageCache.GetIStorageCache() - if err != nil { - return nil, err - } - - extImgId, err := iStorageCache.UploadImage(userCred, imageId, osArch, osType, osDist, scimg.ExternalId, isForce) - if err != nil { - return nil, err - } - scimg.SetExternalId(extImgId) - - ret := jsonutils.NewDict() - ret.Add(jsonutils.NewString(extImgId), "image_id") - return ret, nil - }) - return nil -} - -func (self *SQcloudHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask, content *jsonutils.JSONDict) error { - iCloudStorage, err := storage.GetIStorage() - if err != nil { - return err - } - size, err := content.Int("size") - if err != nil { - return err - } - size = size >> 10 - iDisk, err := iCloudStorage.CreateIDisk(disk.GetName(), int(size), "") - if err != nil { - return err - } - _, err = disk.GetModelManager().TableSpec().Update(disk, func() error { - disk.ExternalId = iDisk.GetGlobalId() - - if metaData := iDisk.GetMetadata(); metaData != nil { - meta := make(map[string]string) - if err := metaData.Unmarshal(meta); err != nil { - log.Errorf("Get disk %s Metadata error: %v", disk.Name, err) - } else { - for key, value := range meta { - 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 { - log.Errorf("Update disk externalId err: %v", err) - return err - } - data := jsonutils.NewDict() - data.Add(jsonutils.NewInt(int64(iDisk.GetDiskSizeMB())), "disk_size") - data.Add(jsonutils.NewString(iDisk.GetDiskFormat()), "disk_format") - task.ScheduleRun(data) - return nil -} - -func (self *SQcloudHostDriver) RequestDeallocateDiskOnHost(host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask) error { - data := jsonutils.NewDict() - if iCloudStorage, err := storage.GetIStorage(); err != nil { - return err - } else if iDisk, err := iCloudStorage.GetIDisk(disk.GetExternalId()); err != nil { - if err == cloudprovider.ErrNotFound { - task.ScheduleRun(data) - return nil - } - return err - } else if err := iDisk.Delete(); err != nil { - return err - } - task.ScheduleRun(data) - return nil -} - -func (self *SQcloudHostDriver) RequestResizeDiskOnHostOnline(host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error { - return self.RequestResizeDiskOnHost(host, storage, disk, size, task) -} - -func (self *SQcloudHostDriver) RequestResizeDiskOnHost(host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error { - iCloudStorage, err := storage.GetIStorage() - if err != nil { - return err - } - iDisk, err := iCloudStorage.GetIDisk(disk.GetExternalId()) - if err != nil { - return err - } - err = iDisk.Resize(size >> 10) - if err != nil { - return err - } - task.ScheduleRun(jsonutils.Marshal(map[string]int64{"disk_size": size})) - return nil -} - -func (self *SQcloudHostDriver) RequestPrepareSaveDiskOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask) error { - task.ScheduleRun(nil) - return nil -} - -func (self *SQcloudHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask, data jsonutils.JSONObject) error { - iDisk, err := disk.GetIDisk() - if err != nil { - return err - } - iStorage, err := disk.GetIStorage() - if err != nil { - return err - } - iStoragecache := iStorage.GetIStoragecache() - if iStoragecache == nil { - return httperrors.NewResourceNotFoundError("fail to find iStoragecache for storage: %s", iStorage.GetName()) - } - taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - snapshot, err := iDisk.CreateISnapshot(fmt.Sprintf("Snapshot-%s", imageId), "PrepareSaveImage") - if err != nil { - return nil, err - } - params := task.GetParams() - osType, _ := params.GetString("properties", "os_type") - - scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), iStoragecache.GetId(), imageId) - if scimg.Status != models.CACHED_IMAGE_STATUS_READY { - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHING, "request_prepare_save_disk_on_host") - } - iImage, err := iStoragecache.CreateIImage(snapshot.GetId(), fmt.Sprintf("Image-%s", imageId), osType, "") - if err != nil { - log.Errorf("fail to create iImage: %v", err) - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error()) - return nil, err - } - scimg.SetExternalId(iImage.GetId()) - if _, err := os.Stat(options.Options.TempPath); os.IsNotExist(err) { - if err = os.MkdirAll(options.Options.TempPath, 0755); err != nil { - return nil, err - } - } - result, err := iStoragecache.DownloadImage(task.GetUserCred(), imageId, iImage.GetId(), options.Options.TempPath) - if err != nil { - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error()) - return nil, err - } - if err := iImage.Delete(); err != nil { - log.Errorf("Delete iImage %s failed: %v", iImage.GetId(), err) - } - if err := snapshot.Delete(); err != nil { - log.Errorf("Delete snapshot %s failed: %v", snapshot.GetId(), err) - } - scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_READY, "") - return result, nil - }) - return nil -} - func (self *SQcloudHostDriver) RequestDeleteSnapshotWithStorage(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, task taskman.ITask) error { return httperrors.NewNotImplementedError("not implement") } diff --git a/pkg/compute/hostdrivers/virtualization.go b/pkg/compute/hostdrivers/virtualization.go new file mode 100644 index 0000000000..d91c5105a5 --- /dev/null +++ b/pkg/compute/hostdrivers/virtualization.go @@ -0,0 +1,5 @@ +package hostdrivers + +type SVirtualizationHostDriver struct { + SBaseHostDriver +} diff --git a/pkg/compute/models/cachedimages.go b/pkg/compute/models/cachedimages.go index 3d49d59128..4093d90ba2 100644 --- a/pkg/compute/models/cachedimages.go +++ b/pkg/compute/models/cachedimages.go @@ -60,7 +60,7 @@ func (self *SCachedimage) ValidateDeleteCondition(ctx context.Context) error { if self.getStoragecacheCount() > 0 { return httperrors.NewNotEmptyError("The image has been cached on storages") } - if self.getStatus() == "active" && !self.isReferenceSessionExpire() { + if self.GetStatus() == "active" && !self.isReferenceSessionExpire() { return httperrors.NewConflictError("the image reference session has not been expired!") } return self.SStandaloneResourceBase.ValidateDeleteCondition(ctx) @@ -82,27 +82,27 @@ func (self *SCachedimage) isRefreshSessionExpire() bool { } } -func (self *SCachedimage) getName() string { +func (self *SCachedimage) GetName() string { name, _ := self.Info.GetString("name") return name } -func (self *SCachedimage) getOwner() string { +func (self *SCachedimage) GetOwner() string { owner, _ := self.Info.GetString("owner") return owner } -func (self *SCachedimage) getFormat() string { +func (self *SCachedimage) GetFormat() string { format, _ := self.Info.GetString("disk_format") return format } -func (self *SCachedimage) getStatus() string { +func (self *SCachedimage) GetStatus() string { status, _ := self.Info.GetString("status") return status } -func (self *SCachedimage) getOSType() string { +func (self *SCachedimage) GetOSType() string { osType, _ := self.Info.GetString("properties", "os_type") return osType } @@ -116,7 +116,7 @@ func (self *SCachedimage) getStoragecacheCount() int { return self.getStoragecacheQuery().Count() } -func (self *SCachedimage) getImage() (*SImage, error) { +func (self *SCachedimage) GetImage() (*SImage, error) { image := SImage{} err := self.Info.Unmarshal(&image) @@ -183,8 +183,8 @@ func (manager *SCachedimageManager) GetImageById(ctx context.Context, userCred m imgObj, _ := manager.FetchById(imageId) if imgObj != nil { cachedImage := imgObj.(*SCachedimage) - if cachedImage.getStatus() == "active" && len(cachedImage.getOSType()) > 0 && cachedImage.isRefreshSessionExpire() { - return cachedImage.getImage() + if cachedImage.GetStatus() == "active" && len(cachedImage.GetOSType()) > 0 && cachedImage.isRefreshSessionExpire() { + return cachedImage.GetImage() } } } @@ -198,7 +198,7 @@ func (manager *SCachedimageManager) GetImageById(ctx context.Context, userCred m if err != nil { return nil, err } - return cachedImage.getImage() + return cachedImage.GetImage() } func (manager *SCachedimageManager) getImageByName(ctx context.Context, userCred mcclient.TokenCredential, imageId string) (*SImage, error) { @@ -211,7 +211,7 @@ func (manager *SCachedimageManager) getImageByName(ctx context.Context, userCred if err != nil { return nil, err } - return cachedImage.getImage() + return cachedImage.GetImage() } func (manager *SCachedimageManager) getImageInfo(ctx context.Context, userCred mcclient.TokenCredential, imageId string, refresh bool) (*SImage, error) { @@ -225,10 +225,10 @@ func (manager *SCachedimageManager) getImageInfo(ctx context.Context, userCred m func (self *SCachedimage) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict { extra := self.SStandaloneResourceBase.GetCustomizeColumns(ctx, userCred, query) - extra.Add(jsonutils.NewString(self.getName()), "name") - extra.Add(jsonutils.NewString(self.getOwner()), "owner") - extra.Add(jsonutils.NewString(self.getFormat()), "format") - extra.Add(jsonutils.NewString(self.getStatus()), "status") + extra.Add(jsonutils.NewString(self.GetName()), "name") + extra.Add(jsonutils.NewString(self.GetOwner()), "owner") + extra.Add(jsonutils.NewString(self.GetFormat()), "format") + extra.Add(jsonutils.NewString(self.GetStatus()), "status") for _, k := range []string{"os_type", "os_distribution", "os_version", "hypervisor"} { val, _ := self.Info.GetString("properties", k) if len(val) > 0 { @@ -252,7 +252,7 @@ func (self *SCachedimage) PerformRefresh(ctx context.Context, userCred mcclient. } func (self *SCachedimage) addRefCount() { - if self.getStatus() != "active" { + if self.GetStatus() != "active" { return } _, err := CachedimageManager.TableSpec().Update(self, func() error { @@ -294,7 +294,7 @@ func (self *SCachedimage) ChooseSourceStoragecacheInRange(hostType string, exclu for _, obj := range v { q = q.Filter(sqlchemy.Equals(host.Field("zone_id"), obj.Id)) } - case []*SVCenter: + case []*SCloudprovider: for _, obj := range v { q = q.Filter(sqlchemy.Equals(host.Field("manager_id"), obj.Id)) } diff --git a/pkg/compute/models/cloudaccounts.go b/pkg/compute/models/cloudaccounts.go index a9325f5669..7c824e6ca2 100644 --- a/pkg/compute/models/cloudaccounts.go +++ b/pkg/compute/models/cloudaccounts.go @@ -13,6 +13,8 @@ import ( "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" + "net/url" + "strconv" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -494,7 +496,9 @@ func (manager *SCloudaccountManager) FetchCloudaccountByIdOrName(accountId strin } func (self *SCloudaccount) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict { - extra.Add(jsonutils.Marshal(self.GetCloudproviders()), "accounts") + providers := self.GetCloudproviders() + extra.Add(jsonutils.NewInt(int64(len(providers))), "account_count") + extra.Add(jsonutils.Marshal(providers), "accounts") return extra } @@ -615,3 +619,54 @@ func (self *SCloudaccount) GetDetailsBalance(ctx context.Context, userCred mccli ret.Add(jsonutils.NewFloat(balance), "balance") return ret, nil } + +func (self *SCloudaccount) getHostPort() (string, int, error) { + urlComponent, err := url.Parse(self.AccessUrl) + if err != nil { + return "", 0, err + } + host := urlComponent.Hostname() + portStr := urlComponent.Port() + port := 0 + if len(portStr) > 0 { + port, err = strconv.Atoi(portStr) + if err != nil { + return "", 0, err + } + } + if port == 0 { + if urlComponent.Scheme == "http" { + port = 80 + } else if urlComponent.Scheme == "https" { + port = 443 + } + } + return host, port, nil +} + +type SVCenterAccessInfo struct { + VcenterId string + Host string + Port int + Account string + Password string + PrivateId string +} + +func (self *SCloudaccount) GetVCenterAccessInfo(privateId string) (SVCenterAccessInfo, error) { + info := SVCenterAccessInfo{} + + host, port, err := self.getHostPort() + if err != nil { + return info, err + } + + info.VcenterId = self.Id + info.Host = host + info.Port = port + info.Account = self.Account + info.Password = self.Secret + info.PrivateId = privateId + + return info, nil +} diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index e5b63489b1..cbf25ac56c 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -133,7 +133,19 @@ func (self *SCloudproviderManager) ValidateCreateData(ctx context.Context, userC return nil, httperrors.NewUnsupportOperationError("Directly creating cloudprovider is not supported, create cloudaccount instead") } +func (self *SCloudprovider) getAccessUrl() string { + if len(self.AccessUrl) > 0 { + return self.AccessUrl + } + account := self.GetCloudaccount() + return account.AccessUrl +} + func (self *SCloudprovider) getPassword() (string, error) { + if len(self.Secret) == 0 { + account := self.GetCloudaccount() + return account.getPassword() + } return utils.DescryptAESBase64(self.Id, self.Secret) } @@ -378,11 +390,12 @@ func (self *SCloudprovider) GetDriver() (cloudprovider.ICloudProvider, error) { return nil, fmt.Errorf("Cloud provider is not enabled") } + accessUrl := self.getAccessUrl() passwd, err := self.getPassword() if err != nil { return nil, err } - return cloudprovider.GetProvider(self.Id, self.Name, self.AccessUrl, self.Account, passwd, self.Provider) + return cloudprovider.GetProvider(self.Id, self.Name, accessUrl, self.Account, passwd, self.Provider) } func (self *SCloudprovider) savePassword(secret string) error { diff --git a/pkg/compute/models/disks.go b/pkg/compute/models/disks.go index 2319762a2b..aeeffa117d 100644 --- a/pkg/compute/models/disks.go +++ b/pkg/compute/models/disks.go @@ -602,22 +602,24 @@ func (self *SDisk) PerformResize(ctx context.Context, userCred mcclient.TokenCre } func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { - if storage := self.GetStorage(); storage == nil { + storage := self.GetStorage() + if storage == nil { return nil, httperrors.NewResourceNotFoundError("fail to find storage for disk %s", self.GetName()) - } else if provider, err := storage.GetDriver(); err != nil { - return nil, err - } else { - return provider.GetIStorageById(storage.GetExternalId()) } + istorage, err := storage.GetIStorage() + if err != nil { + return nil, err + } + return istorage, nil } func (self *SDisk) GetIDisk() (cloudprovider.ICloudDisk, error) { - if iStorage, err := self.GetIStorage(); err != nil { + iStorage, err := self.GetIStorage() + if err != nil { log.Errorf("fail to find iStorage: %v", err) return nil, err - } else { - return iStorage.GetIDisk(self.GetExternalId()) } + return iStorage.GetIDiskById(self.GetExternalId()) } func (self *SDisk) GetZone() *SZone { @@ -761,7 +763,8 @@ func (manager *SDiskManager) syncCloudDisk(ctx context.Context, userCred mcclien diskObj, err := manager.FetchByExternalId(vdisk.GetGlobalId()) if err != nil { if err == sql.ErrNoRows { - vstorage := vdisk.GetIStorge() + vstorage, _ := vdisk.GetIStorage() + storageObj, err := StorageManager.FetchByExternalId(vstorage.GetGlobalId()) if err != nil { log.Errorf("cannot find storage of vdisk %s", err) @@ -845,6 +848,7 @@ func (self *SDisk) syncWithCloudDisk(ctx context.Context, userCred mcclient.Toke self.Status = extDisk.GetStatus() self.DiskFormat = extDisk.GetDiskFormat() self.DiskSize = extDisk.GetDiskSizeMB() + self.AccessPath = extDisk.GetAccessPath() if extDisk.GetIsAutoDelete() { self.AutoDelete = true } @@ -1206,9 +1210,9 @@ func (self *SDisk) GetCustomizeColumns(ctx context.Context, userCred mcclient.To return self.getMoreDetails(extra) } -func (self *SDisk) StartDiskResizeTask(ctx context.Context, userCred mcclient.TokenCredential, size int64, parentTaskId string, pendingUsage quotas.IQuota) error { +func (self *SDisk) StartDiskResizeTask(ctx context.Context, userCred mcclient.TokenCredential, sizeMb int64, parentTaskId string, pendingUsage quotas.IQuota) error { params := jsonutils.NewDict() - params.Add(jsonutils.NewInt(size), "size") + params.Add(jsonutils.NewInt(sizeMb), "size") if task, err := taskman.TaskManager.NewTask(ctx, "DiskResizeTask", self, userCred, params, parentTaskId, "", pendingUsage); err != nil { return err } else { diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index adf3dce2bc..4de892036f 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -696,7 +696,7 @@ func (self *SGuest) PerformCreatedisk(ctx context.Context, userCred mcclient.Tok logclient.AddActionLog(self, logclient.ACT_CREATE, "No valid storage on current host", userCred, false) return nil, httperrors.NewBadRequestError("No valid storage on current host") } - if storage.GetCapacity() < size { + if storage.GetCapacity() > 0 && storage.GetCapacity() < size { logclient.AddActionLog(self, logclient.ACT_CREATE, "Not eough storage space on current host", userCred, false) return nil, httperrors.NewBadRequestError("Not eough storage space on current host") } @@ -1473,11 +1473,16 @@ func (self *SGuest) PerformDeploy(ctx context.Context, userCred mcclient.TokenCr kwargs.Set("delete_public_key", jsonutils.NewString(okey.PublicKey)) } - self.GetModelManager().TableSpec().Update(self, func() error { + _, err := self.GetModelManager().TableSpec().Update(self, func() error { self.KeypairId = kpId return nil }) + if err != nil { + log.Errorf("update keypair fail: %s", err) + return nil, httperrors.NewInternalServerError(err.Error()) + } + kwargs.Set("reset_password", jsonutils.JSONTrue) } } diff --git a/pkg/compute/models/guestdisks.go b/pkg/compute/models/guestdisks.go index 0e61957918..ab86de0496 100644 --- a/pkg/compute/models/guestdisks.go +++ b/pkg/compute/models/guestdisks.go @@ -203,7 +203,7 @@ func (self *SGuestdisk) GetDetailedJson() *jsonutils.JSONDict { cachedImageObj, _ := CachedimageManager.FetchById(imageId) if cachedImageObj != nil { cachedImage := cachedImageObj.(*SCachedimage) - desc.Add(jsonutils.NewString(cachedImage.getName()), "image") + desc.Add(jsonutils.NewString(cachedImage.GetName()), "image") } } diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 9213938a3a..384b8e051e 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -2931,7 +2931,7 @@ func (self *SGuest) GetShortDesc() *jsonutils.JSONDict { templateId := self.GetTemplateId() if len(templateId) > 0 { - desc.Set("cpu", jsonutils.NewString(templateId)) + desc.Set("template_id", jsonutils.NewString(templateId)) } extBw := self.getBandwidth(true) intBw := self.getBandwidth(false) diff --git a/pkg/compute/models/hostdrivers.go b/pkg/compute/models/hostdrivers.go index d6217e3c69..2e490fc365 100644 --- a/pkg/compute/models/hostdrivers.go +++ b/pkg/compute/models/hostdrivers.go @@ -12,14 +12,17 @@ import ( type IHostDriver interface { GetHostType() string + CheckAndSetCacheImage(ctx context.Context, host *SHost, storagecache *SStoragecache, task taskman.ITask) error + RequestUncacheImage(ctx context.Context, host *SHost, storageCache *SStoragecache, task taskman.ITask) error + ValidateUpdateDisk(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) RequestPrepareSaveDiskOnHost(ctx context.Context, host *SHost, disk *SDisk, imageId string, task taskman.ITask) error RequestSaveUploadImageOnHost(ctx context.Context, host *SHost, disk *SDisk, imageId string, task taskman.ITask, data jsonutils.JSONObject) error RequestAllocateDiskOnStorage(ctx context.Context, host *SHost, storage *SStorage, disk *SDisk, task taskman.ITask, content *jsonutils.JSONDict) error - RequestDeallocateDiskOnHost(host *SHost, storage *SStorage, disk *SDisk, task taskman.ITask) error - RequestResizeDiskOnHostOnline(host *SHost, storage *SStorage, disk *SDisk, size int64, task taskman.ITask) error - RequestResizeDiskOnHost(host *SHost, storage *SStorage, disk *SDisk, size int64, task taskman.ITask) error + RequestDeallocateDiskOnHost(ctx context.Context, host *SHost, storage *SStorage, disk *SDisk, task taskman.ITask) error + RequestResizeDiskOnHostOnline(ctx context.Context, host *SHost, storage *SStorage, disk *SDisk, size int64, task taskman.ITask) error + RequestResizeDiskOnHost(ctx context.Context, host *SHost, storage *SStorage, disk *SDisk, size int64, task taskman.ITask) error RequestDeleteSnapshotsWithStorage(ctx context.Context, host *SHost, snapshot *SSnapshot, task taskman.ITask) error RequestResetDisk(ctx context.Context, host *SHost, disk *SDisk, params *jsonutils.JSONDict, task taskman.ITask) error RequestCleanUpDiskSnapshots(ctx context.Context, host *SHost, disk *SDisk, params *jsonutils.JSONDict, task taskman.ITask) error diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index 9ec7dfc202..dc7b8304a9 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -2,6 +2,7 @@ package models import ( "context" + "database/sql" "fmt" "net/http" "net/url" @@ -10,6 +11,8 @@ import ( "strings" "time" + "github.com/serialx/hashring" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/tristate" @@ -457,6 +460,19 @@ func (self *SHost) GetHoststorageOfId(storageId string) *SHoststorage { return &hoststorage } +func (self *SHost) GetStorageByFilePath(path string) *SStorage { + hoststorages := self.GetHoststorages() + if hoststorages == nil { + return nil + } + for i := 0; i < len(hoststorages); i += 1 { + if strings.HasPrefix(path, hoststorages[i].MountPoint) { + return hoststorages[i].GetStorage() + } + } + return nil +} + func (self *SHost) GetBaremetalstorage() *SHoststorage { if !self.IsBaremetal { return nil @@ -943,9 +959,12 @@ func (self *SHost) GetHostDriver() IHostDriver { return GetHostDriver(self.HostType) } -func (manager *SHostManager) getHostsByZone(zone *SZone, provider *SCloudprovider) ([]SHost, error) { +func (manager *SHostManager) getHostsByZoneProvider(zone *SZone, provider *SCloudprovider) ([]SHost, error) { hosts := make([]SHost, 0) - q := manager.Query().Equals("zone_id", zone.Id) + q := manager.Query() + if zone != nil { + q = q.Equals("zone_id", zone.Id) + } if provider != nil { q = q.Equals("manager_id", provider.Id) } @@ -962,7 +981,7 @@ func (manager *SHostManager) SyncHosts(ctx context.Context, userCred mcclient.To remoteHosts := make([]cloudprovider.ICloudHost, 0) syncResult := compare.SyncResult{} - dbHosts, err := manager.getHostsByZone(zone, provider) + dbHosts, err := manager.getHostsByZoneProvider(zone, provider) if err != nil { syncResult.Error(err) return nil, nil, syncResult @@ -1027,6 +1046,7 @@ func (manager *SHostManager) SyncHosts(ctx context.Context, userCred mcclient.To func (self *SHost) syncWithCloudHost(extHost cloudprovider.ICloudHost, projectSync bool) error { _, err := self.GetModelManager().TableSpec().Update(self, func() error { self.Name = extHost.GetName() + self.Status = extHost.GetStatus() self.HostStatus = extHost.GetHostStatus() self.AccessIp = extHost.GetAccessIp() @@ -1046,6 +1066,9 @@ func (self *SHost) syncWithCloudHost(extHost cloudprovider.ICloudHost, projectSy self.IsEmulated = extHost.IsEmulated() self.Enabled = extHost.GetEnabled() + self.IsMaintenance = extHost.GetIsMaintenance() + self.Version = extHost.GetVersion() + return nil }) if err != nil { @@ -1061,13 +1084,23 @@ func (self *SHost) syncWithCloudHost(extHost cloudprovider.ICloudHost, projectSy return err } -func (manager *SHostManager) newFromCloudHost(extHost cloudprovider.ICloudHost, zone *SZone) (*SHost, error) { +func (manager *SHostManager) newFromCloudHost(extHost cloudprovider.ICloudHost, izone *SZone) (*SHost, error) { host := SHost{} host.SetModelManager(manager) + if izone == nil { + wire, err := WireManager.GetWireOfIp(extHost.GetAccessIp()) + if err != nil { + msg := fmt.Sprintf("fail to find wire for host %s %s: %s", extHost.GetName(), extHost.GetAccessIp(), err) + log.Errorf(msg) + return nil, fmt.Errorf(msg) + } + izone = wire.GetZone() + } + host.Name = extHost.GetName() host.ExternalId = extHost.GetGlobalId() - host.ZoneId = zone.Id + host.ZoneId = izone.Id // host.ManagerId = extHost.GetManagerId() host.HostType = extHost.GetHostType() @@ -1090,6 +1123,9 @@ func (manager *SHostManager) newFromCloudHost(extHost cloudprovider.ICloudHost, host.ManagerId = extHost.GetManagerId() host.IsEmulated = extHost.IsEmulated() + host.IsMaintenance = extHost.GetIsMaintenance() + host.Version = extHost.GetVersion() + err := manager.TableSpec().Insert(&host) if err != nil { log.Errorf("newFromCloudHost fail %s", err) @@ -1103,7 +1139,9 @@ func (manager *SHostManager) newFromCloudHost(extHost cloudprovider.ICloudHost, return &host, nil } -func (self *SHost) SyncHostStorages(ctx context.Context, userCred mcclient.TokenCredential, storages []cloudprovider.ICloudStorage) compare.SyncResult { +func (self *SHost) SyncHostStorages(ctx context.Context, userCred mcclient.TokenCredential, storages []cloudprovider.ICloudStorage) ([]SStorage, []cloudprovider.ICloudStorage, compare.SyncResult) { + localStorages := make([]SStorage, 0) + remoteStorages := make([]cloudprovider.ICloudStorage, 0) syncResult := compare.SyncResult{} dbStorages := make([]SStorage, 0) @@ -1128,7 +1166,7 @@ func (self *SHost) SyncHostStorages(ctx context.Context, userCred mcclient.Token err := compare.CompareSets(dbStorages, storages, &removed, &commondb, &commonext, &added) if err != nil { syncResult.Error(err) - return syncResult + return nil, nil, syncResult } for i := 0; i < len(removed); i += 1 { @@ -1144,29 +1182,34 @@ func (self *SHost) SyncHostStorages(ctx context.Context, userCred mcclient.Token for i := 0; i < len(commondb); i += 1 { log.Infof("host %s is still connected with %s, to update ...", self.Id, commondb[i].Id) - err := self.syncWithCloudHostStorage(commonext[i]) + err := self.syncWithCloudHostStorage(&commondb[i], commonext[i]) if err != nil { syncResult.UpdateError(err) } else { syncResult.Update() } + localStorages = append(localStorages, commondb[i]) + remoteStorages = append(remoteStorages, commonext[i]) } for i := 0; i < len(added); i += 1 { log.Infof("host %s is found connected with %s, to add ...", self.Id, added[i].GetId()) - err := self.newCloudHostStorage(ctx, userCred, added[i]) + local, err := self.newCloudHostStorage(ctx, userCred, added[i]) if err != nil { syncResult.AddError(err) } else { syncResult.Add() } + localStorages = append(localStorages, *local) + remoteStorages = append(remoteStorages, added[i]) } - return syncResult + return localStorages, remoteStorages, syncResult } -func (self *SHost) syncWithCloudHostStorage(extStorage cloudprovider.ICloudStorage) error { +func (self *SHost) syncWithCloudHostStorage(localStorage *SStorage, extStorage cloudprovider.ICloudStorage) error { // do nothing - return nil + hs := self.GetHoststorageOfId(localStorage.Id) + return hs.syncWithCloudHostStorage(extStorage) } func (self *SHost) Attach2Storage(ctx context.Context, userCred mcclient.TokenCredential, storage *SStorage, mountPoint string) error { @@ -1185,15 +1228,25 @@ func (self *SHost) Attach2Storage(ctx context.Context, userCred mcclient.TokenCr return nil } -func (self *SHost) newCloudHostStorage(ctx context.Context, userCred mcclient.TokenCredential, extStorage cloudprovider.ICloudStorage) error { +func (self *SHost) newCloudHostStorage(ctx context.Context, userCred mcclient.TokenCredential, extStorage cloudprovider.ICloudStorage) (*SStorage, error) { storageObj, err := StorageManager.FetchByExternalId(extStorage.GetGlobalId()) if err != nil { - log.Errorf("%s", err) - return nil + if err == sql.ErrNoRows { + // no cloud storage found, this may happen for on-premise host + // create the storage right now + storageObj, err = StorageManager.newFromCloudStorage(extStorage, self.GetZone()) + if err != nil { + log.Errorf("create by cloud storage fail %s", err) + return nil, err + } + } else { + log.Errorf("%s", err) + return nil, err + } } storage := storageObj.(*SStorage) - err = self.Attach2Storage(ctx, userCred, storage, "") - return err + err = self.Attach2Storage(ctx, userCred, storage, extStorage.GetMountPoint()) + return storage, err } func (self *SHost) SyncHostWires(ctx context.Context, userCred mcclient.TokenCredential, wires []cloudprovider.ICloudWire) compare.SyncResult { @@ -1624,7 +1677,23 @@ func (self *SHost) GetIHost() (cloudprovider.ICloudHost, error) { if err != nil { return nil, fmt.Errorf("No cloudprovide for host: %s", err) } - ihost, err := provider.GetIHostById(self.ExternalId) + var iregion cloudprovider.ICloudRegion + if provider.IsOnPremiseInfrastructure() { + iregion, err = provider.GetOnPremiseIRegion() + } else { + region := self.GetRegion() + if region == nil { + msg := "fail to find region of host???" + log.Errorf(msg) + return nil, fmt.Errorf(msg) + } + iregion, err = provider.GetIRegionById(region.ExternalId) + } + if err != nil { + log.Errorf("fail to find iregion: %s", err) + return nil, err + } + ihost, err := iregion.GetIHostById(self.ExternalId) if err != nil { log.Errorf("fail to find ihost by id %s %s", self.ExternalId, err) return nil, fmt.Errorf("fail to find ihost by id %s", err) @@ -2408,26 +2477,49 @@ func (self *SHost) PerformAddNetif(ctx context.Context, userCred mcclient.TokenC } wire, _ := data.GetString("wire") ipAddr, _ := data.GetString("ip_addr") + rate, _ := data.Int("rate") + nicType, _ := data.GetString("nic_type") + index, _ := data.Int("index") + linkUp, _ := data.GetString("link_up") + mtu, _ := data.Int("mtu") + reset := jsonutils.QueryBoolean(data, "reset", false) + strInterface, _ := data.GetString("interface") + bridge, _ := data.GetString("bridge") + reserve := jsonutils.QueryBoolean(data, "reserve", false) + requireDesignatedIp := jsonutils.QueryBoolean(data, "require_designated_ip", false) + + err := self.addNetif(ctx, userCred, mac, wire, ipAddr, int(rate), nicType, int8(index), utils.ToBool(linkUp), + int16(mtu), reset, strInterface, bridge, reserve, requireDesignatedIp) + return nil, err +} + +func (self *SHost) addNetif(ctx context.Context, userCred mcclient.TokenCredential, + mac string, wire string, ipAddr string, + rate int, nicType string, index int8, linkUp bool, mtu int16, + reset bool, strInterface string, bridge string, + reserve bool, requireDesignatedIp bool, +) error { + var sw *SWire if len(wire) > 0 && len(ipAddr) == 0 { iWire, err := WireManager.FetchByIdOrName(userCred, wire) if err != nil { - return nil, httperrors.NewBadRequestError("Wire %s not found", wire) + return httperrors.NewBadRequestError("Wire %s not found", wire) } sw = iWire.(*SWire) } else if len(ipAddr) > 0 && len(wire) == 0 { ipWire, err := WireManager.GetWireOfIp(ipAddr) if err != nil { - return nil, httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr) + return httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr) } sw = ipWire } else if len(wire) > 0 && len(ipAddr) > 0 { ipWire, err := WireManager.GetWireOfIp(ipAddr) if err != nil { - return nil, httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr) + return httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr) } if ipWire.Id != wire && ipWire.GetName() != wire { - httperrors.NewBadRequestError("IP %s not attach to wire %s", ipAddr, wire) + return httperrors.NewBadRequestError("IP %s not attach to wire %s", ipAddr, wire) } sw = ipWire } @@ -2439,24 +2531,14 @@ func (self *SHost) PerformAddNetif(ctx context.Context, userCred mcclient.TokenC if sw != nil { netif.WireId = sw.Id } - if rate, err := data.Int("rate"); err == nil { - netif.Rate = int(rate) - } - if nicType, err := data.GetString("nic_type"); err == nil { - netif.NicType = nicType - } - if index, err := data.Int("index"); err == nil { - netif.Index = int8(index) - } - if linkUp, err := data.GetString("link_up"); err == nil { - netif.LinkUp = utils.ToBool(linkUp) - } - if mtu, err := data.Int("mtu"); err == nil { - netif.Mtu = int16(mtu) - } + netif.Rate = rate + netif.NicType = nicType + netif.Index = index + netif.LinkUp = linkUp + netif.Mtu = mtu err = NetInterfaceManager.TableSpec().Insert(netif) if err != nil { - return nil, err + return err } } else { var changed = false @@ -2469,44 +2551,42 @@ func (self *SHost) PerformAddNetif(ctx context.Context, userCred mcclient.TokenC changed = true netif.WireId = sw.Id } - if rate, err := data.Int("rate"); err == nil { + if rate != netif.Rate { changed = true netif.Rate = int(rate) } - if nicType, err := data.GetString("nic_type"); err == nil { + if nicType != netif.NicType { changed = true netif.NicType = nicType } - if index, err := data.Int("index"); err == nil && index >= 0 { + if index >= 0 && index != netif.Index { changed = true netif.Index = int8(index) } - if linkUp, err := data.GetString("link_up"); err != nil { + if linkUp != netif.LinkUp { changed = true - netif.LinkUp = utils.ToBool(linkUp) + netif.LinkUp = linkUp } - if mtu, err := data.Int("mtu"); err != nil { + if mtu != netif.Mtu { changed = true netif.Mtu = int16(mtu) } return nil }) if err != nil { - return nil, err + return err } - if changed || jsonutils.QueryBoolean(data, "reset", false) { + if changed || reset { self.DisableNetif(ctx, userCred, netif, false) } } sw = netif.GetWire() if sw != nil { - strInterface, err := data.GetString("interface") - if err != nil { + if len(strInterface) == 0 { strInterface = fmt.Sprintf("eth%d", netif.Index) } if len(strInterface) > 0 { - bridge, err := data.GetString("bridge") - if err != nil { + if len(bridge) == 0 { bridge = fmt.Sprintf("br%s", sw.GetName()) } var isMaster = netif.NicType == NIC_TYPE_ADMIN @@ -2521,7 +2601,7 @@ func (self *SHost) PerformAddNetif(ctx context.Context, userCred mcclient.TokenC hw.MacAddr = mac err := HostwireManager.TableSpec().Insert(hw) if err != nil { - return nil, err + return err } } else { hw := ihw.(*SHostwire) @@ -2535,15 +2615,13 @@ func (self *SHost) PerformAddNetif(ctx context.Context, userCred mcclient.TokenC } } } - reserve := jsonutils.QueryBoolean(data, "reserve", false) - requireDesignatedIp := jsonutils.QueryBoolean(data, "require_designated_ip", false) if len(ipAddr) > 0 { err = self.EnableNetif(ctx, userCred, netif, "", ipAddr, "", reserve, requireDesignatedIp) if err != nil { - return nil, httperrors.NewBadRequestError(err.Error()) + return httperrors.NewBadRequestError(err.Error()) } } - return nil, nil + return nil } func (self *SHost) AllowPerformEnableNetif(ctx context.Context, @@ -2994,3 +3072,164 @@ func (self *SHost) UpdateDiskConfig(layouts []baremetal.Layout) error { } return nil } + +func (host *SHost) SyncHostExternalNics(ctx context.Context, userCred mcclient.TokenCredential, ihost cloudprovider.ICloudHost) compare.SyncResult { + result := compare.SyncResult{} + + netIfs := host.GetNetInterfaces() + extNics, err := ihost.GetIHostNics() + if err != nil { + result.Error(err) + return result + } + + disables := make([]*SNetInterface, 0) + enables := make([]cloudprovider.ICloudHostNetInterface, 0) + + type sRemoveNetInterface struct { + netif *SNetInterface + reserveIp bool + } + + type sAddNetInterface struct { + netif cloudprovider.ICloudHostNetInterface + reserveIp bool + } + + removes := make([]sRemoveNetInterface, 0) + adds := make([]sAddNetInterface, 0) + + nicMax := len(netIfs) + if nicMax < len(extNics) { + nicMax = len(extNics) + } + for i := 0; i < nicMax; i += 1 { + if i < len(netIfs) && i < len(extNics) { + obn := netIfs[i].GetBaremetalNetwork() + var oip string + if obn != nil { + oip = obn.IpAddr + } + nip := extNics[i].GetIpAddr() + if netIfs[i].Mac == extNics[i].GetMac() { + if oip != nip { + if obn != nil { + disables = append(disables, &netIfs[i]) + } + if len(nip) > 0 { + enables = append(enables, extNics[i]) + } + } else { + // do nothing, in sync + } + } else { + reserveIp := false + if len(oip) > 0 && oip == nip { + // # mac change case + reserveIp = true + } + removes = append(removes, sRemoveNetInterface{netif: &netIfs[i], reserveIp: reserveIp}) + adds = append(adds, sAddNetInterface{netif: extNics[i], reserveIp: reserveIp}) + } + } else if i < len(netIfs) && i >= len(extNics) { + removes = append(removes, sRemoveNetInterface{netif: &netIfs[i], reserveIp: false}) + } else if i >= len(netIfs) && i < len(extNics) { + adds = append(adds, sAddNetInterface{netif: extNics[i], reserveIp: false}) + } + } + + for i := len(removes) - 1; i >= 0; i -= 1 { + err = host.RemoveNetif(ctx, userCred, removes[i].netif, removes[i].reserveIp) + if err != nil { + result.DeleteError(err) + } else { + result.Delete() + } + } + + for i := len(disables) - 1; i >= 0; i -= 1 { + err = host.DisableNetif(ctx, userCred, disables[i], false) + if err != nil { + result.DeleteError(err) + } else { + result.Delete() + } + } + + for i := 0; i < len(enables); i += 1 { + netif := host.GetNetInterface(enables[i].GetMac()) + err = host.EnableNetif(ctx, userCred, netif, "", enables[i].GetIpAddr(), "", false, true) + if err != nil { + result.AddError(err) + } else { + result.Add() + } + } + + for i := 0; i < len(adds); i += 1 { + extNic := adds[i].netif + err = host.addNetif(ctx, userCred, extNic.GetMac(), "", extNic.GetIpAddr(), 0, extNic.GetNicType(), extNic.GetIndex(), + extNic.IsLinkUp(), extNic.GetMtu(), false, "", "", false, true) + if err != nil { + result.AddError(err) + } else { + result.Add() + } + } + + return result +} + +func (manager *SHostManager) GetEsxiAgentHostId(key string) (string, error) { + q := HostManager.Query("id") + q = q.Equals("host_status", HOST_ONLINE) + q = q.Equals("host_type", HOST_TYPE_HYPERVISOR) + q = q.IsTrue("enabled") + + rows, err := q.Rows() + if err != nil { + return "", err + } + + var hostId string + hostIds := make([]string, 0) + for rows.Next() { + err = rows.Scan(&hostId) + if err != nil { + return "", err + } + hostIds = append(hostIds, hostId) + } + + ring := hashring.New(hostIds) + ret, _ := ring.GetNode(key) + return ret, nil +} + +func (manager *SHostManager) GetEsxiAgentHost(key string) (*SHost, error) { + hostId, err := manager.GetEsxiAgentHostId(key) + if err != nil { + return nil, err + } + return manager.FetchHostById(hostId), nil +} + +func (host *SHost) GetEsxiAgentHost() (*SHost, error) { + return HostManager.GetEsxiAgentHost(host.Id) +} + +func (manager *SHostManager) GetHostByIp(hostIp string) (*SHost, error) { + q := manager.Query() + q = q.Equals("access_ip", hostIp) + + host, err := db.NewModelObject(manager) + if err != nil { + return nil, err + } + err = q.First(host) + if err != nil { + return nil, err + } + + return host.(*SHost), nil +} diff --git a/pkg/compute/models/hoststorages.go b/pkg/compute/models/hoststorages.go index 2b234dd825..90a55f5ba9 100644 --- a/pkg/compute/models/hoststorages.go +++ b/pkg/compute/models/hoststorages.go @@ -7,13 +7,15 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" - "yunion.io/x/onecloud/pkg/cloudcommon/db" - "yunion.io/x/onecloud/pkg/httperrors" - "yunion.io/x/onecloud/pkg/mcclient" - "yunion.io/x/onecloud/pkg/util/httputils" "yunion.io/x/pkg/tristate" "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" + + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/httputils" ) type SHoststorageManager struct { @@ -244,3 +246,11 @@ func (manager *SHoststorageManager) GetStorages(hostId string) ([]SHoststorage, } return hoststorage, nil } + +func (self *SHoststorage) syncWithCloudHostStorage(extStorage cloudprovider.ICloudStorage) error { + _, err := self.GetModelManager().TableSpec().Update(self, func() error { + self.MountPoint = extStorage.GetMountPoint() + return nil + }) + return err +} diff --git a/pkg/compute/models/managedresource.go b/pkg/compute/models/managedresource.go index fc6c7a0153..3b490b17da 100644 --- a/pkg/compute/models/managedresource.go +++ b/pkg/compute/models/managedresource.go @@ -19,6 +19,14 @@ func (self *SManagedResourceBase) GetCloudprovider() *SCloudprovider { return nil } +func (self *SManagedResourceBase) GetCloudaccount() *SCloudaccount { + cp := self.GetCloudprovider() + if cp == nil { + return nil + } + return cp.GetCloudaccount() +} + func (self *SManagedResourceBase) GetDriver() (cloudprovider.ICloudProvider, error) { provider := self.GetCloudprovider() if provider == nil { diff --git a/pkg/compute/models/storagecachedimages.go b/pkg/compute/models/storagecachedimages.go index 4a646c146d..a91cc895b1 100644 --- a/pkg/compute/models/storagecachedimages.go +++ b/pkg/compute/models/storagecachedimages.go @@ -153,7 +153,7 @@ func (self *SStoragecachedimage) getExtraDetails(extra *jsonutils.JSONDict) *jso } cachedImage := self.GetCachedimage() if cachedImage != nil { - extra.Add(jsonutils.NewString(cachedImage.getName()), "image") + extra.Add(jsonutils.NewString(cachedImage.GetName()), "image") extra.Add(jsonutils.NewInt(cachedImage.Size), "size") } extra.Add(jsonutils.NewInt(int64(self.getReferenceCount())), "reference") @@ -237,7 +237,8 @@ func (self *SStoragecachedimage) markDeleting(ctx context.Context, userCred mccl lockman.LockJointObject(ctx, cache, image) defer lockman.ReleaseJointObject(ctx, cache, image) - if !isForce && !utils.IsInStringArray(self.Status, []string{CACHED_IMAGE_STATUS_READY, CACHED_IMAGE_STATUS_DELETING}) { + if !isForce && !utils.IsInStringArray(self.Status, + []string{CACHED_IMAGE_STATUS_READY, CACHED_IMAGE_STATUS_DELETING, CACHED_IMAGE_STATUS_CACHE_FAILED}) { return httperrors.NewInvalidStatusError("Cannot uncache in status %s", self.Status) } _, err = self.GetModelManager().TableSpec().Update(self, func() error { diff --git a/pkg/compute/models/storagecaches.go b/pkg/compute/models/storagecaches.go index 5d2d07624a..e873a29f33 100644 --- a/pkg/compute/models/storagecaches.go +++ b/pkg/compute/models/storagecaches.go @@ -9,6 +9,7 @@ import ( "yunion.io/x/log" "yunion.io/x/sqlchemy" + "fmt" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -40,7 +41,7 @@ type SStoragecache struct { SInfrastructure SManagedResourceBase - Path string `width:"256" charset:"utf8" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // = Column(VARCHAR(256, charset='utf8'), nullable=True) + Path string `width:"256" charset:"utf8" nullable:"true" list:"user" update:"admin" create:"admin_optional"` // = Column(VARCHAR(256, charset='utf8'), nullable=True) } func (self *SStoragecache) getStorages() []SStorage { @@ -141,6 +142,8 @@ func (manager *SStoragecacheManager) newFromCloudStoragecache(cloudCache cloudpr local.IsEmulated = cloudCache.IsEmulated() local.ManagerId = cloudCache.GetManagerId() + local.Path = cloudCache.GetPath() + err := manager.TableSpec().Insert(&local) if err != nil { return nil, err @@ -153,6 +156,8 @@ func (self *SStoragecache) syncWithCloudStoragecache(cloudCache cloudprovider.IC _, err := self.GetModelManager().TableSpec().Update(self, func() error { self.Name = cloudCache.GetName() + self.Path = cloudCache.GetPath() + self.IsEmulated = cloudCache.IsEmulated() self.ManagerId = cloudCache.GetManagerId() @@ -197,7 +202,9 @@ func (self *SStoragecache) getCachedImageSize() int64 { var size int64 = 0 for _, img := range images { imginfo := img.GetCachedimage() - size += imginfo.Size + if imginfo != nil { + size += imginfo.Size + } } return size } @@ -237,13 +244,6 @@ func (self *SStoragecache) StartImageCacheTask(ctx context.Context, userCred mcc } func (self *SStoragecache) StartImageUncacheTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, isForce bool, parentTaskId string) error { - if !isForce { - err := self.ValidateDeleteCondition(ctx) - if err != nil { - return err - } - } - data := jsonutils.NewDict() data.Add(jsonutils.NewString(imageId), "image_id") if isForce { @@ -258,12 +258,18 @@ func (self *SStoragecache) StartImageUncacheTask(ctx context.Context, userCred m } func (self *SStoragecache) GetIStorageCache() (cloudprovider.ICloudStoragecache, error) { - provider, err := self.GetDriver() + storages := self.getStorages() + if len(storages) == 0 { + msg := "no storages for this storagecache???" + log.Errorf(msg) + return nil, fmt.Errorf(msg) + } + istorage, err := storages[0].GetIStorage() if err != nil { - log.Errorf("fail to find cloud provider") + log.Errorf("fail to find istorage for storage %s", err) return nil, err } - return provider.GetIStoragecacheById(self.GetExternalId()) + return istorage.GetIStoragecache(), nil } func (manager *SStoragecacheManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) { diff --git a/pkg/compute/models/storages.go b/pkg/compute/models/storages.go index 37ca701ead..78361afd5f 100644 --- a/pkg/compute/models/storages.go +++ b/pkg/compute/models/storages.go @@ -9,16 +9,17 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/tristate" + "yunion.io/x/pkg/util/compare" + "yunion.io/x/pkg/utils" + "yunion.io/x/sqlchemy" + "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudprovider" "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/logclient" - "yunion.io/x/pkg/tristate" - "yunion.io/x/pkg/util/compare" - "yunion.io/x/pkg/utils" - "yunion.io/x/sqlchemy" ) const ( @@ -476,6 +477,14 @@ func (self *SStorage) getZone() *SZone { return nil } +func (self *SStorage) GetRegion() *SCloudregion { + zone := self.getZone() + if zone == nil { + return nil + } + return zone.GetRegion() +} + func (self *SStorage) GetReserved() int { return self.Reserved } @@ -923,7 +932,28 @@ func (self *SStorage) GetIStorage() (cloudprovider.ICloudStorage, error) { log.Errorf("fail to find cloud provider") return nil, err } - return provider.GetIStorageById(self.GetExternalId()) + var iRegion cloudprovider.ICloudRegion + if provider.IsOnPremiseInfrastructure() { + iRegion, err = provider.GetOnPremiseIRegion() + } else { + region := self.GetRegion() + if region == nil { + msg := "cannot find region for storage???" + log.Errorf(msg) + return nil, fmt.Errorf(msg) + } + iRegion, err = provider.GetIRegionById(region.ExternalId) + } + if err != nil { + log.Errorf("provider.GetIRegionById fail %s", err) + return nil, err + } + istore, err := iRegion.GetIStorageById(self.GetExternalId()) + if err != nil { + log.Errorf("iRegion.GetIStorageById fail %s", err) + return nil, err + } + return istore, nil } func (manager *SStorageManager) FetchStorageById(storageId string) *SStorage { @@ -1025,3 +1055,20 @@ func (manager *SStorageManager) ListItemFilter(ctx context.Context, q *sqlchemy. return q, err } + +func (self *SStorage) ClearSchedDescCache() error { + hosts := self.GetAllAttachingHosts() + if hosts == nil { + msg := "get attaching host error" + log.Errorf(msg) + return fmt.Errorf(msg) + } + for i := 0; i < len(hosts); i += 1 { + err := hosts[i].ClearSchedDescCache() + if err != nil { + log.Errorf("host CleanHostSchedCache error: %v", err) + return err + } + } + return nil +} diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index 20b3eaad36..19672f747d 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -411,7 +411,28 @@ func (self *SVpc) GetIVpc() (cloudprovider.ICloudVpc, error) { log.Errorf("fail to find cloud provider") return nil, err } - return provider.GetIVpcById(self.GetExternalId()) + var iregion cloudprovider.ICloudRegion + if provider.IsOnPremiseInfrastructure() { + iregion, err = provider.GetOnPremiseIRegion() + } else { + region := self.GetRegion() + if region == nil { + msg := "fail to find region of host???" + log.Errorf(msg) + return nil, fmt.Errorf(msg) + } + iregion, err = provider.GetIRegionById(region.ExternalId) + } + if err != nil { + log.Errorf("fail to find iregion: %s", err) + return nil, err + } + ivpc, err := iregion.GetIVpcById(self.ExternalId) + if err != nil { + log.Errorf("fail to find ivpc by id %s %s", self.ExternalId, err) + return nil, fmt.Errorf("fail to find ivpc by id %s", err) + } + return ivpc, nil } func (self *SVpc) Delete(ctx context.Context, userCred mcclient.TokenCredential) error { diff --git a/pkg/compute/tasks/cloud_provider_sync_info_task.go b/pkg/compute/tasks/cloud_provider_sync_info_task.go index 84b563631d..4ea10e20ca 100644 --- a/pkg/compute/tasks/cloud_provider_sync_info_task.go +++ b/pkg/compute/tasks/cloud_provider_sync_info_task.go @@ -93,7 +93,17 @@ func syncCloudProviderInfo(ctx context.Context, provider *models.SCloudprovider, notes := fmt.Sprintf("Start sync host info ...") log.Infof(notes) db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_START, "", task.UserCred) + + if driver.IsOnPremiseInfrastructure() { + syncOnPremiseCloudProviderInfo(ctx, provider, task, driver, syncRange) + } else { + syncPublicCloudProviderInfo(ctx, provider, task, driver, syncRange) + } +} + +func syncPublicCloudProviderInfo(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, driver cloudprovider.ICloudProvider, syncRange *models.SSyncRange) { regions := driver.GetIRegions() + localRegions, remoteRegions, result := models.CloudregionManager.SyncRegions(ctx, task.UserCred, provider.Provider, regions) msg := result.Result() log.Infof("SyncRegion result: %s", msg) @@ -390,7 +400,7 @@ func syncHostStorages(ctx context.Context, provider *models.SCloudprovider, task logSyncFailed(provider, task, msg) return } - result := localHost.SyncHostStorages(ctx, task.UserCred, storages) + localStorages, remoteStorages, result := localHost.SyncHostStorages(ctx, task.UserCred, storages) msg := result.Result() notes := fmt.Sprintf("SyncHostStorages for host %s result: %s", localHost.Name, msg) log.Infof(notes) @@ -400,6 +410,10 @@ func syncHostStorages(ctx context.Context, provider *models.SCloudprovider, task } db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_COMPLETE, msg, task.UserCred) logclient.AddActionLog(provider, getAction(task.Params), notes, task.UserCred, true) + + for i := 0; i < len(localStorages); i += 1 { + syncStorageCaches(ctx, provider, task, &localStorages[i], remoteStorages[i]) + } } func syncHostWires(ctx context.Context, provider *models.SCloudprovider, task taskman.ITask, localHost *models.SHost, remoteHost cloudprovider.ICloudHost) { diff --git a/pkg/compute/tasks/cloud_provider_sync_onpremise_info_task.go b/pkg/compute/tasks/cloud_provider_sync_onpremise_info_task.go new file mode 100644 index 0000000000..ceb15a3f7c --- /dev/null +++ b/pkg/compute/tasks/cloud_provider_sync_onpremise_info_task.go @@ -0,0 +1,65 @@ +package tasks + +import ( + "context" + "fmt" + + "yunion.io/x/log" + "yunion.io/x/pkg/utils" + + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +func syncOnPremiseCloudProviderInfo(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, driver cloudprovider.ICloudProvider, syncRange *models.SSyncRange) { + iregion, err := driver.GetOnPremiseIRegion() + if err != nil { + msg := fmt.Sprintf("GetOnPremiseIRegion for provider %s failed %s", provider.GetName(), err) + log.Errorf(msg) + logSyncFailed(provider, task, msg) + return + } + + ihosts, err := iregion.GetIHosts() + if err != nil { + msg := fmt.Sprintf("GetIHosts for provider %s failed %s", provider.GetName(), err) + log.Errorf(msg) + logSyncFailed(provider, task, msg) + return + } + + localHosts, remoteHosts, result := models.HostManager.SyncHosts(ctx, task.UserCred, provider, nil, ihosts, syncRange.ProjectSync) + msg := result.Result() + notes := fmt.Sprintf("SyncHosts for provider %s result: %s", provider.Name, msg) + log.Infof(notes) + if result.IsError() { + logSyncFailed(provider, task, msg) + return + } + db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_COMPLETE, msg, task.UserCred) + logclient.AddActionLog(provider, getAction(task.Params), notes, task.UserCred, true) + + for i := 0; i < len(localHosts); i += 1 { + if len(syncRange.Host) > 0 && !utils.IsInStringArray(localHosts[i].Id, syncRange.Host) { + continue + } + syncHostStorages(ctx, provider, task, &localHosts[i], remoteHosts[i]) + syncHostNics(ctx, provider, task, &localHosts[i], remoteHosts[i]) + syncHostVMs(ctx, provider, task, &localHosts[i], remoteHosts[i], syncRange) + } +} + +func syncHostNics(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localHost *models.SHost, remoteHost cloudprovider.ICloudHost) { + result := localHost.SyncHostExternalNics(ctx, task.GetUserCred(), remoteHost) + msg := result.Result() + notes := fmt.Sprintf("SyncHostWires for host %s result: %s", localHost.Name, msg) + log.Infof(notes) + if result.IsError() { + logSyncFailed(provider, task, msg) + return + } + db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_COMPLETE, msg, task.GetUserCred()) + logclient.AddActionLog(provider, getAction(task.GetParams()), notes, task.GetUserCred(), true) +} diff --git a/pkg/compute/tasks/disk_base_task.go b/pkg/compute/tasks/disk_base_task.go index 872dc93587..2cf747cc3e 100644 --- a/pkg/compute/tasks/disk_base_task.go +++ b/pkg/compute/tasks/disk_base_task.go @@ -3,8 +3,6 @@ package tasks import ( "context" - "yunion.io/x/log" - "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/compute/models" ) @@ -34,23 +32,10 @@ func (self *SDiskBaseTask) finalReleasePendingUsage(ctx context.Context) { } } -func (self *SDiskBaseTask) CleanStorageSchedCache(storage *models.SStorage) { - if hosts := storage.GetAllAttachingHosts(); hosts == nil { - log.Errorf("get attaching host error") - } else { - for _, h := range hosts { - if err := h.ClearSchedDescCache(); err != nil { - log.Errorf("host CleanHostSchedCache error: %v", err) - } - } - } -} - func (self *SDiskBaseTask) CleanHostSchedCache(disk *models.SDisk) { - storage := disk.GetStorage() - self.CleanStorageSchedCache(storage) + disk.GetStorage().ClearSchedDescCache() if len(disk.BackupStorageId) > 0 { bkStorage := models.StorageManager.FetchStorageById(disk.BackupStorageId) - self.CleanStorageSchedCache(bkStorage) + bkStorage.ClearSchedDescCache() } } diff --git a/pkg/compute/tasks/disk_create_task.go b/pkg/compute/tasks/disk_create_task.go index a4a101c83e..6d8cd3cbb6 100644 --- a/pkg/compute/tasks/disk_create_task.go +++ b/pkg/compute/tasks/disk_create_task.go @@ -125,7 +125,7 @@ func (self *DiskCreateBackupTask) OnInit(ctx context.Context, obj db.IStandalone func (self *DiskCreateBackupTask) OnDiskReady(ctx context.Context, disk *models.SDisk, data jsonutils.JSONObject) { bkStorage := models.StorageManager.FetchStorageById(disk.BackupStorageId) - self.CleanStorageSchedCache(bkStorage) + bkStorage.ClearSchedDescCache() disk.SetStatus(self.UserCred, models.DISK_READY, "") db.OpsLog.LogEvent(disk, db.ACT_BACKUP_ALLOCATE, disk.GetShortDesc(), self.UserCred) self.SetStageComplete(ctx, nil) diff --git a/pkg/compute/tasks/disk_delete_task.go b/pkg/compute/tasks/disk_delete_task.go index 590a6e264a..a08d765ff0 100644 --- a/pkg/compute/tasks/disk_delete_task.go +++ b/pkg/compute/tasks/disk_delete_task.go @@ -2,13 +2,14 @@ package tasks import ( "context" - "fmt" "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/compute/options" + "yunion.io/x/onecloud/pkg/httperrors" ) type DiskDeleteTask struct { @@ -57,8 +58,8 @@ func (self *DiskDeleteTask) startDeleteDisk(ctx context.Context, disk *models.SD self.SetStage("OnGuestDiskDeleteSucc", nil) } if host == nil { - self.OnGuestDiskDeleteFailed(ctx, disk, fmt.Errorf("fail to find master host")) - } else if err := host.GetHostDriver().RequestDeallocateDiskOnHost(host, storage, disk, self); err != nil { + self.OnGuestDiskDeleteFailed(ctx, disk, httperrors.NewNotFoundError("fail to find master host")) + } else if err := host.GetHostDriver().RequestDeallocateDiskOnHost(ctx, host, storage, disk, self); err != nil { self.OnGuestDiskDeleteFailed(ctx, disk, err) } } @@ -69,8 +70,8 @@ func (self *DiskDeleteTask) OnMasterStorageDeleteDiskSucc(ctx context.Context, d storage := models.StorageManager.FetchStorageById(disk.BackupStorageId) host := storage.GetMasterHost() if host == nil { - self.OnGuestDiskDeleteFailed(ctx, disk, fmt.Errorf("backup storage %s fail to find master host", disk.BackupStorageId)) - } else if err := host.GetHostDriver().RequestDeallocateDiskOnHost(host, storage, disk, self); err != nil { + self.OnGuestDiskDeleteFailed(ctx, disk, httperrors.NewNotFoundError("backup storage %s fail to find master host", disk.BackupStorageId)) + } else if err := host.GetHostDriver().RequestDeallocateDiskOnHost(ctx, host, storage, disk, self); err != nil { self.OnGuestDiskDeleteFailed(ctx, disk, err) } } diff --git a/pkg/compute/tasks/disk_resize_task.go b/pkg/compute/tasks/disk_resize_task.go index 3fff258569..443caf9bdd 100644 --- a/pkg/compute/tasks/disk_resize_task.go +++ b/pkg/compute/tasks/disk_resize_task.go @@ -49,12 +49,12 @@ func (self *DiskResizeTask) OnInit(ctx context.Context, obj db.IStandaloneModel, func (self *DiskResizeTask) StartResizeDisk(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, online bool) { log.Infof("Resizing disk on host %s ...", host.GetName()) self.SetStage("on_disk_resize_complete", nil) - size, _ := self.GetParams().Int("size") + sizeMb, _ := self.GetParams().Int("size") proc := host.GetHostDriver().RequestResizeDiskOnHost if online { proc = host.GetHostDriver().RequestResizeDiskOnHostOnline } - if err := proc(host, storage, disk, size, self); err != nil { + if err := proc(ctx, host, storage, disk, sizeMb, self); err != nil { log.Errorf("request_resize_disk_on_host: %v", err) self.OnStartResizeDiskFailed(ctx, disk, err) return @@ -80,7 +80,7 @@ func (self *DiskResizeTask) OnDiskResizeComplete(ctx context.Context, disk *mode self.OnStartResizeDiskFailed(ctx, disk, err) return } - size, err := jSize.Int() + sizeMb, err := jSize.Int() if err != nil { log.Errorf("OnDiskResizeComplete error: %s", err.Error()) self.OnStartResizeDiskFailed(ctx, disk, err) @@ -89,7 +89,7 @@ func (self *DiskResizeTask) OnDiskResizeComplete(ctx context.Context, disk *mode oldStatus := disk.Status _, err = disk.GetModelManager().TableSpec().Update(disk, func() error { disk.Status = models.DISK_READY - disk.DiskSize = int(size) + disk.DiskSize = int(sizeMb) return nil }) if err != nil { @@ -107,8 +107,8 @@ func (self *DiskResizeTask) OnDiskResizeComplete(ctx context.Context, disk *mode self.finalReleasePendingUsage(ctx) } -func (self *DiskResizeTask) OnDiskResizeCompleteFailed(ctx context.Context, disk *models.SDisk, reason error) { - disk.SetDiskReady(ctx, self.GetUserCred(), reason.Error()) +func (self *DiskResizeTask) OnDiskResizeCompleteFailed(ctx context.Context, disk *models.SDisk, reason jsonutils.JSONObject) { + disk.SetDiskReady(ctx, self.GetUserCred(), reason.String()) db.OpsLog.LogEvent(disk, db.ACT_RESIZE_FAIL, disk.GetShortDesc(), self.UserCred) - logclient.AddActionLog(disk, logclient.ACT_RESIZE, reason.Error(), self.UserCred, false) + logclient.AddActionLog(disk, logclient.ACT_RESIZE, reason.String(), self.UserCred, false) } diff --git a/pkg/compute/tasks/guest_change_config_task.go b/pkg/compute/tasks/guest_change_config_task.go index 6c1a9ea3da..4586541dcb 100644 --- a/pkg/compute/tasks/guest_change_config_task.go +++ b/pkg/compute/tasks/guest_change_config_task.go @@ -2,6 +2,7 @@ package tasks import ( "context" + "fmt" "yunion.io/x/jsonutils" @@ -32,69 +33,63 @@ func (self *GuestChangeConfigTask) OnInit(ctx context.Context, obj db.IStandalon } func (self *GuestChangeConfigTask) OnDisksResizeComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + iResizeDisks, err := self.Params.Get("resize") if iResizeDisks == nil || err != nil { - self.markStageFailed(obj, ctx, err.Error()) + self.markStageFailed(ctx, guest, err.Error()) return } resizeDisks := iResizeDisks.(*jsonutils.JSONArray) for i := 0; i < resizeDisks.Length(); i++ { iResizeSet, err := resizeDisks.GetAt(i) if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(obj, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("resizeDisks.GetAt fail %s", err)) return } resizeSet := iResizeSet.(*jsonutils.JSONArray) diskId, err := resizeSet.GetAt(0) if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(obj, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("resizeSet.GetAt(0) fail %s", err)) return } idStr, err := diskId.GetString() if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(obj, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("diskId.GetString fail %s", err)) return } jSize, err := resizeSet.GetAt(1) if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(obj, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("resizeSet.GetAt(1) fail %s", err)) return } size, err := jSize.Int() if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(obj, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("jSize.Int fail %s", err)) return } iDisk, err := models.DiskManager.FetchById(idStr) if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(obj, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("models.DiskManager.FetchById(idStr) fail %s", err)) return } disk := iDisk.(*models.SDisk) - if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(disk, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) - return - } if disk.DiskSize < int(size) { var pendingUsage models.SQuota err = self.GetPendingUsage(&pendingUsage) if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(disk, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("self.GetPendingUsage(&pendingUsage) fail %s", err)) + return + } + err = disk.StartDiskResizeTask(ctx, self.UserCred, size, self.GetTaskId(), &pendingUsage) + if err != nil { + self.markStageFailed(ctx, guest, fmt.Sprintf("disk.StartDiskResizeTask fail %s", err)) return } - disk.StartDiskResizeTask(ctx, self.UserCred, size, self.GetTaskId(), &pendingUsage) return } } - guest := obj.(*models.SGuest) + self.DoCreateDisksTask(ctx, guest) } @@ -107,12 +102,11 @@ func (self *GuestChangeConfigTask) DoCreateDisksTask(ctx context.Context, guest data := (iCreateData).(*jsonutils.JSONDict) self.SetStage("on_create_disks_complete", nil) guest.StartGuestCreateDiskTask(ctx, self.UserCred, data, self.GetTaskId()) - } func (self *GuestChangeConfigTask) OnCreateDisksCompleteFailed(ctx context.Context, obj db.IStandaloneModel, err jsonutils.JSONObject) { - self.markStageFailed(obj, ctx, err.String()) - logclient.AddActionLog(obj, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + guest := obj.(*models.SGuest) + self.markStageFailed(ctx, guest, fmt.Sprintf("OnCreateDisksCompleteFailed %s", err)) } func (self *GuestChangeConfigTask) OnCreateDisksComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { @@ -125,23 +119,20 @@ func (self *GuestChangeConfigTask) OnCreateDisksComplete(ctx context.Context, ob if iVcpuCount != nil { vcpuCount, err = iVcpuCount.Int() if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(guest, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("iVcpuCount.Int() fail %s", err)) return } } if iVmemSize != nil { vmemSize, err = iVmemSize.Int() if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(guest, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("iVmemSize.Int fail %s", err)) return } } err = guest.GetDriver().RequestChangeVmConfig(ctx, guest, self, vcpuCount, vmemSize) if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(guest, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("guest.GetDriver().RequestChangeVmConfig fail %s", err)) return } var addCpu, addMem = 0, 0 @@ -167,15 +158,13 @@ func (self *GuestChangeConfigTask) OnCreateDisksComplete(ctx context.Context, ob return nil }) if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(guest, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("Update fail %s", err)) return } var pendingUsage models.SQuota err = self.GetPendingUsage(&pendingUsage) if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(guest, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("GetPendingUsage %s", err)) return } // ownerCred := guest.GetOwnerUserCred() @@ -192,22 +181,19 @@ func (self *GuestChangeConfigTask) OnCreateDisksComplete(ctx context.Context, ob err = models.QuotaManager.CancelPendingUsage(ctx, self.UserCred, guest.ProjectId, &pendingUsage, &cancelUsage) if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(guest, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("CancelPendingUsage fail %s", err)) return } err = self.SetPendingUsage(&pendingUsage) if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(guest, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("SetPendingUsage fail %s", err)) return } } self.SetStage("on_sync_status_complete", nil) err = guest.StartSyncstatus(ctx, self.UserCred, self.GetTaskId()) if err != nil { - self.markStageFailed(obj, ctx, err.Error()) - logclient.AddActionLog(guest, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false) + self.markStageFailed(ctx, guest, fmt.Sprintf("StartSyncstatus fail %s", err)) return } } @@ -221,8 +207,8 @@ func (self *GuestChangeConfigTask) OnSyncStatusComplete(ctx context.Context, obj } else { dt := jsonutils.NewDict() dt.Add(jsonutils.NewString(guest.Id), "id") - self.SetStageComplete(ctx, dt) logclient.AddActionLog(guest, logclient.ACT_VM_CHANGE_FLAVOR, "", self.UserCred, true) + self.SetStageComplete(ctx, dt) } } @@ -233,8 +219,9 @@ func (self *GuestChangeConfigTask) OnGuestStartComplete(ctx context.Context, obj self.SetStageComplete(ctx, dt) } -func (self *GuestChangeConfigTask) markStageFailed(obj db.IStandaloneModel, ctx context.Context, reason string) { - guest := obj.(*models.SGuest) +func (self *GuestChangeConfigTask) markStageFailed(ctx context.Context, guest *models.SGuest, reason string) { guest.SetStatus(self.UserCred, models.VM_CHANGE_FLAVOR_FAIL, reason) + db.OpsLog.LogEvent(guest, db.ACT_CHANGE_FLAVOR_FAIL, reason, self.UserCred) + logclient.AddActionLog(guest, logclient.ACT_VM_CHANGE_FLAVOR, reason, self.UserCred, false) self.SetStageFailed(ctx, reason) } diff --git a/pkg/compute/tasks/guest_create_disk_task.go b/pkg/compute/tasks/guest_create_disk_task.go index b4f221b1d3..702e997a14 100644 --- a/pkg/compute/tasks/guest_create_disk_task.go +++ b/pkg/compute/tasks/guest_create_disk_task.go @@ -10,6 +10,7 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/compute/models" + "time" ) type GuestCreateDiskTask struct { @@ -105,7 +106,7 @@ func (self *KVMGuestCreateDiskTask) OnKvmDiskPrepared(ctx context.Context, obj d guest := obj.(*models.SGuest) if guest.Status == models.VM_RUNNING { self.SetStage("on_config_sync_complete", nil) - err := guest.StartSyncstatus(ctx, self.UserCred, self.GetTaskId()) + err := guest.StartSyncTask(ctx, self.UserCred, false, self.GetTaskId()) if err != nil { self.SetStageFailed(ctx, err.Error()) } @@ -130,99 +131,146 @@ func (self *ManagedGuestCreateDiskTask) OnInit(ctx context.Context, obj db.IStan func (self *ManagedGuestCreateDiskTask) OnManagedDiskPrepared(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { var diskIndex = 0 - var diskReady = true - for { + + for self.Params.Contains(fmt.Sprintf("disk.%d.id", diskIndex)) { diskId, err := self.Params.GetString(fmt.Sprintf("disk.%d.id", diskIndex)) - if !diskReady || err != nil { - break + if err != nil { + self.SetStageFailed(ctx, err.Error()) + return } iDisk, err := models.DiskManager.FetchById(diskId) if err != nil { self.SetStageFailed(ctx, err.Error()) return } - if iDisk == nil { - self.SetStageFailed(ctx, "Disk not found") - return - } disk := iDisk.(*models.SDisk) if disk.Status == models.DISK_INIT { - snapInfo, err := self.Params.GetString(fmt.Sprintf("disk.%d.snapshot", diskIndex)) - if err != nil { - snapInfo = "" - } + snapInfo, _ := self.Params.GetString(fmt.Sprintf("disk.%d.snapshot", diskIndex)) err = disk.StartDiskCreateTask(ctx, self.UserCred, false, snapInfo, self.GetTaskId()) if err != nil { self.SetStageFailed(ctx, err.Error()) return } - diskReady = false - break + return } diskIndex += 1 } + diskIndex = 0 guest := obj.(*models.SGuest) - for { + for self.Params.Contains(fmt.Sprintf("disk.%d.id", diskIndex)) { diskId, err := self.Params.GetString(fmt.Sprintf("disk.%d.id", diskIndex)) - if !diskReady || err != nil { - break + if err != nil { + return } iDisk, err := models.DiskManager.FetchById(diskId) if err != nil { self.SetStageFailed(ctx, err.Error()) return } - if iDisk == nil { - self.SetStageFailed(ctx, "Disk not found") - return - } disk := iDisk.(*models.SDisk) if disk.Status != models.DISK_READY { - diskReady = false - break - } - - ihost, err := guest.GetHost().GetIHost() - if err != nil { - self.SetStageFailed(ctx, "Host not found") + self.SetStageFailed(ctx, fmt.Sprintf("disk %s is not ready", disk.Id)) return } - iVM, e := ihost.GetIVMById(guest.GetExternalId()) + iVM, e := guest.GetIVM() if e != nil { - self.SetStageFailed(ctx, "Aliyun VM not found") + self.SetStageFailed(ctx, "iVM not found") return } - err = iVM.AttachDisk(disk.GetExternalId()) + err = iVM.AttachDisk(ctx, disk.GetExternalId()) if err != nil { log.Debugf("Attach Disk %s to guest fail: %s", diskId, err) self.SetStageFailed(ctx, "Attach Disk to guest fail") return } + time.Sleep(time.Second*5) diskIndex += 1 } - if diskReady { - if guest.Status == models.VM_RUNNING { - self.SetStage("on_config_sync_complete", nil) - err := guest.StartSyncstatus(ctx, self.UserCred, self.GetTaskId()) - if err != nil { - self.SetStageFailed(ctx, err.Error()) - } - } else { - self.SetStageComplete(ctx, nil) - } - } -} - -func (self *ManagedGuestCreateDiskTask) OnConfigSyncComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { self.SetStageComplete(ctx, nil) } -func (self *ManagedGuestCreateDiskTask) AttachManagedDisks(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { +/* +func (self *ManagedGuestCreateDiskTask) OnConfigSyncComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + self.SetStageComplete(ctx, nil) +} +*/ + +type ESXiGuestCreateDiskTask struct { + SGuestBaseTask +} + +func (self *ESXiGuestCreateDiskTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + host := guest.GetHost() + if host == nil { + self.SetStageFailed(ctx, "no valid host") + return + } + + diskIndex := 0 + for { + diskKey := fmt.Sprintf("disk.%d.id", diskIndex) + if !self.Params.Contains(diskKey) { + break + } + diskId, _ := self.Params.GetString(diskKey) + diskIndex += 1 + guestDisk := guest.GetGuestDisk(diskId) + if guestDisk == nil { + self.SetStageFailed(ctx, "fail to find guestdisk") + return + } + disk := guestDisk.GetDisk() + if disk == nil { + self.SetStageFailed(ctx, fmt.Sprintf("Disk %s not found", diskId)) + return + } + if disk.Status != models.DISK_INIT { + self.SetStageFailed(ctx, fmt.Sprintf("Disk %s already created??", diskId)) + return + } + ivm, err := guest.GetIVM() + if err != nil { + self.SetStageFailed(ctx, fmt.Sprintf("fail to find iVM for %s", guest.GetName())) + return + } + err = ivm.CreateDisk(ctx, disk.DiskSize, disk.Id, guestDisk.Driver) + if err != nil { + self.SetStageFailed(ctx, fmt.Sprintf("ivm.CreateDisk fail %s", guest.GetName())) + return + } + idisks, err := ivm.GetIDisks() + if err != nil { + self.SetStageFailed(ctx, fmt.Sprintf("ivm.GetIDisks fail %s", err)) + return + } + + log.Debugf("diskcount after create: %d", len(idisks)) + + vdisk := idisks[len(idisks)-1] + + _, err = disk.GetModelManager().TableSpec().Update(disk, func() error { + disk.DiskSize = vdisk.GetDiskSizeMB() + disk.AccessPath = vdisk.GetAccessPath() + disk.ExternalId = vdisk.GetGlobalId() + return nil + }) + if err != nil { + self.SetStageFailed(ctx, fmt.Sprintf("disk.GetModelManager().TableSpec().Update fail %s", err)) + return + } + + disk.SetStatus(self.UserCred, models.DISK_READY, "create disk success") + disk.GetStorage().ClearSchedDescCache() + db.OpsLog.LogEvent(disk, db.ACT_ALLOCATE, disk.GetShortDesc(), self.UserCred) + db.OpsLog.LogAttachEvent(guest, disk, self.UserCred, disk.GetShortDesc()) + } + self.SetStageComplete(ctx, nil) } @@ -260,4 +308,5 @@ func init() { taskman.RegisterTask(GuestCreateDiskTask{}) taskman.RegisterTask(KVMGuestCreateDiskTask{}) taskman.RegisterTask(ManagedGuestCreateDiskTask{}) + taskman.RegisterTask(ESXiGuestCreateDiskTask{}) } diff --git a/pkg/compute/tasks/guest_start_task.go b/pkg/compute/tasks/guest_start_task.go index 7830a62a3d..ba9f250e89 100644 --- a/pkg/compute/tasks/guest_start_task.go +++ b/pkg/compute/tasks/guest_start_task.go @@ -71,9 +71,9 @@ func (self *GuestStartTask) RequestStart(ctx context.Context, guest *models.SGue self.onStartGuestFailed(ctx, guest, err) } else { if result != nil && jsonutils.QueryBoolean(result, "is_running", false) { - self.OnStartComplete(ctx, guest, nil) // guest.SetStatus(self.UserCred, models.VM_RUNNING, "start") // self.taskComplete(ctx, guest) + self.OnStartComplete(ctx, guest, nil) } } } diff --git a/pkg/compute/tasks/storage_cache_image_task.go b/pkg/compute/tasks/storage_cache_image_task.go index bd3d476fab..3b226aa9f7 100644 --- a/pkg/compute/tasks/storage_cache_image_task.go +++ b/pkg/compute/tasks/storage_cache_image_task.go @@ -46,8 +46,8 @@ func (self *StorageCacheImageTask) OnImageCacheComplete(ctx context.Context, obj storageCache := obj.(*models.SStoragecache) imageId, _ := self.Params.GetString("image_id") scimg := models.StoragecachedimageManager.Register(ctx, self.UserCred, storageCache.Id, imageId) - // extImgId, _ := data.GetString("image_id") - self.OnCacheSucc(ctx, storageCache, imageId, scimg) + extImgId, _ := data.GetString("image_id") + self.OnCacheSucc(ctx, storageCache, imageId, scimg, extImgId) } func (self *StorageCacheImageTask) OnImageCacheCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { @@ -55,14 +55,15 @@ func (self *StorageCacheImageTask) OnImageCacheCompleteFailed(ctx context.Contex imageId, _ := self.Params.GetString("image_id") scimg := models.StoragecachedimageManager.Register(ctx, self.UserCred, storageCache.Id, imageId) err := fmt.Errorf(data.String()) - self.OnCacheFailed(ctx, storageCache, imageId, scimg, err) + extImgId, _ := data.GetString("image_id") + self.OnCacheFailed(ctx, storageCache, imageId, scimg, err, extImgId) } -func (self *StorageCacheImageTask) OnCacheFailed(ctx context.Context, cache *models.SStoragecache, imageId string, scimg *models.SStoragecachedimage, err error) { +func (self *StorageCacheImageTask) OnCacheFailed(ctx context.Context, cache *models.SStoragecache, imageId string, scimg *models.SStoragecachedimage, err error, extImgId string) { scimg.SetStatus(self.UserCred, models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error()) - /* if len(extImgId) > 0 && scimg.ExternalId != extImgId { + if len(extImgId) > 0 && scimg.ExternalId != extImgId { scimg.SetExternalId(extImgId) - }*/ + } body := jsonutils.NewDict() body.Add(jsonutils.NewString(err.Error()), "reason") body.Add(jsonutils.NewString(imageId), "image_id") @@ -70,8 +71,11 @@ func (self *StorageCacheImageTask) OnCacheFailed(ctx context.Context, cache *mod self.SetStageFailed(ctx, err.Error()) } -func (self *StorageCacheImageTask) OnCacheSucc(ctx context.Context, cache *models.SStoragecache, imageId string, scimg *models.SStoragecachedimage) { +func (self *StorageCacheImageTask) OnCacheSucc(ctx context.Context, cache *models.SStoragecache, imageId string, scimg *models.SStoragecachedimage, extImgId string) { scimg.SetStatus(self.UserCred, models.CACHED_IMAGE_STATUS_READY, "cached") + if len(extImgId) > 0 && scimg.ExternalId != extImgId { + scimg.SetExternalId(extImgId) + } models.CachedimageManager.ImageAddRefCount(imageId) db.OpsLog.LogEvent(cache, db.ACT_CACHED_IMAGE, imageId, self.UserCred) self.SetStageComplete(ctx, nil) diff --git a/pkg/compute/tasks/storage_uncache_image_task.go b/pkg/compute/tasks/storage_uncache_image_task.go index 7e4e9743b9..63020d977a 100644 --- a/pkg/compute/tasks/storage_uncache_image_task.go +++ b/pkg/compute/tasks/storage_uncache_image_task.go @@ -2,11 +2,15 @@ package tasks import ( "context" + "fmt" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/compute/models" + + "yunion.io/x/onecloud/pkg/util/logclient" ) type StorageUncacheImageTask struct { @@ -23,15 +27,74 @@ func (self *StorageUncacheImageTask) OnInit(ctx context.Context, obj db.IStandal storageCache := obj.(*models.SStoragecache) + db.OpsLog.LogEvent(storageCache, db.ACT_UNCACHING_IMAGE, imageId, self.UserCred) + scimg := models.StoragecachedimageManager.Register(ctx, self.UserCred, storageCache.Id, imageId) - db.OpsLog.LogEvent(storageCache, db.ACT_UNCACHED_IMAGE, imageId, self.UserCred) + if scimg == nil || len(scimg.Path) == 0 { + // "image is not cached on this storage" + self.OnImageUncacheComplete(ctx, storageCache, nil) + } if isForce { - scimg.Detach(ctx, self.UserCred) - self.SetStageComplete(ctx, nil) + self.OnImageUncacheComplete(ctx, obj, data) return } - // TODO + host, err := storageCache.GetHost() + if err != nil { + self.OnTaskFailed(ctx, storageCache, fmt.Sprintf("fail to get host %s", err)) + return + } + + if host == nil { + self.OnImageUncacheComplete(ctx, obj, data) + return + } + + self.SetStage("OnImageUncacheComplete", nil) + + err = host.GetHostDriver().RequestUncacheImage(ctx, host, storageCache, self) + + if err != nil { + self.OnTaskFailed(ctx, storageCache, fmt.Sprintf("fail to uncache image %s", err)) + } +} + +func (self *StorageUncacheImageTask) OnTaskFailed(ctx context.Context, storageCache *models.SStoragecache, reason string) { + body := jsonutils.NewDict() + body.Add(jsonutils.NewString(reason), "reason") + imageId, _ := self.Params.GetString("image_id") + body.Add(jsonutils.NewString(imageId), "image_id") + + db.OpsLog.LogEvent(storageCache, db.ACT_UNCACHE_IMAGE_FAIL, body, self.UserCred) + + logclient.AddActionLog(storageCache, logclient.ACT_UNCACHED_IMAGE, body, self.UserCred, false) + + self.SetStageFailed(ctx, reason) +} + +func (self *StorageUncacheImageTask) OnImageUncacheCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + storageCache := obj.(*models.SStoragecache) + + self.OnTaskFailed(ctx, storageCache, data.String()) +} + +func (self *StorageUncacheImageTask) OnImageUncacheComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + log.Infof("Uncached image task success: %s", data) + storageCache := obj.(*models.SStoragecache) + + imageId, _ := self.Params.GetString("image_id") + scimg := models.StoragecachedimageManager.Register(ctx, self.UserCred, storageCache.Id, imageId) + if scimg != nil { + scimg.Detach(ctx, self.UserCred) + } + + body := jsonutils.NewDict() + body.Add(jsonutils.NewString(imageId), "image_id") + db.OpsLog.LogEvent(storageCache, db.ACT_UNCACHED_IMAGE, body, self.UserCred) + + logclient.AddActionLog(storageCache, db.ACT_UNCACHED_IMAGE, body, self.UserCred, true) + + self.SetStageComplete(ctx, nil) } diff --git a/pkg/httperrors/errors_test.go b/pkg/httperrors/errors_test.go index a126b7fc71..5ac12d5153 100644 --- a/pkg/httperrors/errors_test.go +++ b/pkg/httperrors/errors_test.go @@ -60,22 +60,22 @@ func TestMsgToTemplate(t *testing.T) { }{ { name: "non-empty msg to template", - msg: "%% baremetals %s delete.time %d%", - out: "% baremetals {0} delete.time {1}%", + msg: "%% baremetals %s delete.time %d%", + out: "% baremetals {0} delete.time {1}%", }, { name: "empty msg to template", - msg: "", - out: "", + msg: "", + out: "", }, { name: "non-empty with zh-utf8 characters msg to template", - msg: "%% baremetals %s 中文%d ¥%%", - out: "% baremetals {0} 中文{1} ¥%", + msg: "%% baremetals %s 中文%d ¥%%", + out: "% baremetals {0} 中文{1} ¥%", }, } - for _, c := range cases{ + for _, c := range cases { t.Run(c.name, func(t *testing.T) { resp := msgToTemplate(c.msg) if resp != c.out { @@ -83,4 +83,4 @@ func TestMsgToTemplate(t *testing.T) { } }) } -} \ No newline at end of file +} diff --git a/pkg/mcclient/mcclient.go b/pkg/mcclient/mcclient.go index 96b81240c7..28adaafbd4 100644 --- a/pkg/mcclient/mcclient.go +++ b/pkg/mcclient/mcclient.go @@ -76,7 +76,9 @@ func getDefaultHeader(header http.Header, token string) http.Header { if header == nil { header = http.Header{} } - header.Add("X-Auth-Token", token) + if len(header.Get(AUTH_TOKEN)) == 0 { + header.Add(AUTH_TOKEN, token) + } } return header } diff --git a/pkg/mcclient/session.go b/pkg/mcclient/session.go index fdf29f278d..6d9d3cc019 100644 --- a/pkg/mcclient/session.go +++ b/pkg/mcclient/session.go @@ -42,7 +42,8 @@ type ClientSession struct { func populateHeader(self *http.Header, update http.Header) { for k, v := range update { for _, vv := range v { - self.Add(k, vv) + // self.Add(k, vv) + self.Set(k, vv) } } } diff --git a/pkg/util/aliyun/aliyun.go b/pkg/util/aliyun/aliyun.go index b2011e9cb1..96e97cb0e7 100644 --- a/pkg/util/aliyun/aliyun.go +++ b/pkg/util/aliyun/aliyun.go @@ -193,15 +193,3 @@ func (self *SAliyunClient) GetIStorageById(id string) (cloudprovider.ICloudStora } return nil, cloudprovider.ErrNotFound } - -func (self *SAliyunClient) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { - for i := 0; i < len(self.iregions); i += 1 { - ihost, err := self.iregions[i].GetIStoragecacheById(id) - if err == nil { - return ihost, nil - } else if err != cloudprovider.ErrNotFound { - return nil, err - } - } - return nil, cloudprovider.ErrNotFound -} diff --git a/pkg/util/aliyun/disk.go b/pkg/util/aliyun/disk.go index 4b31697c2e..4cf6ec9361 100644 --- a/pkg/util/aliyun/disk.go +++ b/pkg/util/aliyun/disk.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "context" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -108,17 +109,36 @@ func (self *SDisk) GetId() string { return self.DiskId } -func (self *SDisk) Delete() error { - if _, err := self.storage.zone.region.getDisk(self.DiskId); err == cloudprovider.ErrNotFound { - // 未找到disk, 说明disk已经被删除了. 避免回收站中disk-delete循环删除失败 - log.Errorf("Failed to find disk %s when delete", self.DiskId) - return nil +func (self *SDisk) Delete(ctx context.Context) error { + _, err := self.storage.zone.region.getDisk(self.DiskId) + if err != nil { + if err == cloudprovider.ErrNotFound { + // 未找到disk, 说明disk已经被删除了. 避免回收站中disk-delete循环删除失败 + return nil + } + log.Errorf("Failed to find disk %s when delete: %s", self.DiskId, err) + return err } - return self.storage.zone.region.deleteDisk(self.DiskId) + + for { + err := self.storage.zone.region.DeleteDisk(self.DiskId) + if err != nil { + if isError(err, "IncorrectDiskStatus") { + log.Infof("The disk is initializing, try later ...") + time.Sleep(10 * time.Second) + } else { + log.Errorf("DeleteDisk fail: %s", err) + return err + } + } else { + break + } + } + return cloudprovider.WaitDeleted(self, 10*time.Second, 300*time.Second) // 5minutes } -func (self *SDisk) Resize(size int64) error { - return self.storage.zone.region.resizeDisk(self.DiskId, size) +func (self *SDisk) Resize(ctx context.Context, sizeMb int64) error { + return self.storage.zone.region.resizeDisk(self.DiskId, sizeMb) } func (self *SDisk) GetName() string { @@ -136,8 +156,8 @@ func (self *SDisk) IsEmulated() bool { return false } -func (self *SDisk) GetIStorge() cloudprovider.ICloudStorage { - return self.storage +func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { + return self.storage, nil } func (self *SDisk) GetStatus() string { @@ -241,14 +261,6 @@ func (self *SRegion) getDisk(diskId string) (*SDisk, error) { return &disks[0], nil } -func (self *SRegion) deleteDisk(diskId string) error { - params := make(map[string]string) - params["DiskId"] = diskId - - _, err := self.ecsRequest("DeleteDisk", params) - return err -} - func (self *SRegion) DeleteDisk(diskId string) error { params := make(map[string]string) params["DiskId"] = diskId @@ -257,14 +269,15 @@ func (self *SRegion) DeleteDisk(diskId string) error { return err } -func (self *SRegion) resizeDisk(diskId string, size int64) error { +func (self *SRegion) resizeDisk(diskId string, sizeMb int64) error { + sizeGb := sizeMb / 1024 params := make(map[string]string) params["DiskId"] = diskId - params["NewSize"] = fmt.Sprintf("%d", size) + params["NewSize"] = fmt.Sprintf("%d", sizeGb) _, err := self.ecsRequest("ResizeDisk", params) if err != nil { - log.Errorf("resizing disk (%s) to %d GiB failed: %s", diskId, size, err) + log.Errorf("resizing disk (%s) to %d GiB failed: %s", diskId, sizeGb, err) return err } @@ -284,7 +297,7 @@ func (self *SRegion) resetDisk(diskId, snapshotId string) error { return nil } -func (self *SDisk) CreateISnapshot(name, desc string) (cloudprovider.ICloudSnapshot, error) { +func (self *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) { if snapshotId, err := self.storage.zone.region.CreateSnapshot(self.DiskId, name, desc); err != nil { log.Errorf("createSnapshot fail %s", err) return nil, err @@ -354,7 +367,7 @@ func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { return isnapshots, nil } -func (self *SDisk) Reset(snapshotId string) error { +func (self *SDisk) Reset(ctx context.Context, snapshotId string) error { return self.storage.zone.region.resetDisk(self.DiskId, snapshotId) } @@ -372,3 +385,7 @@ func (self *SDisk) GetBillingType() string { func (self *SDisk) GetExpiredAt() time.Time { return self.ExpiredTime } + +func (self *SDisk) GetAccessPath() string { + return "" +} diff --git a/pkg/util/aliyun/errors.go b/pkg/util/aliyun/errors.go index cb899f91b7..e5dfbd20e9 100644 --- a/pkg/util/aliyun/errors.go +++ b/pkg/util/aliyun/errors.go @@ -1,13 +1,15 @@ package aliyun import ( - aliyunerrors "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" + "fmt" + "strings" ) func isError(err error, code string) bool { - aliyunErr, ok := err.(aliyunerrors.Error) - if !ok { + errStr := fmt.Sprintf("%s", err) + if strings.Index(errStr, code) > 0 { + return true + } else { return false } - return aliyunErr.ErrorCode() == code } diff --git a/pkg/util/aliyun/host.go b/pkg/util/aliyun/host.go index 10f5ad8def..28f68d0287 100644 --- a/pkg/util/aliyun/host.go +++ b/pkg/util/aliyun/host.go @@ -253,3 +253,15 @@ func (self *SHost) _createVM(name string, imgId string, sysDiskSize int, cpu int return "", fmt.Errorf("Failed to create, specification not supported") } + +func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (host *SHost) GetIsMaintenance() bool { + return false +} + +func (host *SHost) GetVersion() string { + return ALIYUN_API_VERSION +} diff --git a/pkg/util/aliyun/image.go b/pkg/util/aliyun/image.go index 3f6947e89e..a34713576f 100644 --- a/pkg/util/aliyun/image.go +++ b/pkg/util/aliyun/image.go @@ -5,6 +5,7 @@ import ( "strings" "time" + "context" "github.com/aliyun/aliyun-oss-go-sdk/oss" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -85,7 +86,7 @@ func (self *SImage) IsEmulated() bool { return false } -func (self *SImage) Delete() error { +func (self *SImage) Delete(ctx context.Context) error { return self.storageCache.region.DeleteImage(self.ImageId) } diff --git a/pkg/util/aliyun/instance.go b/pkg/util/aliyun/instance.go index ce28c48bc8..a8b3b3c255 100644 --- a/pkg/util/aliyun/instance.go +++ b/pkg/util/aliyun/instance.go @@ -10,6 +10,7 @@ import ( "yunion.io/x/pkg/util/seclib" "yunion.io/x/pkg/utils" + "context" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" ) @@ -381,7 +382,7 @@ func (self *SInstance) GetHypervisor() string { return models.HYPERVISOR_ALIYUN } -func (self *SInstance) StartVM() error { +func (self *SInstance) StartVM(ctx context.Context) error { timeout := 300 * time.Second interval := 15 * time.Second @@ -405,7 +406,7 @@ func (self *SInstance) StartVM() error { return cloudprovider.ErrTimeout } -func (self *SInstance) StopVM(isForce bool) error { +func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { err := self.host.zone.region.StopVM(self.InstanceId, isForce) if err != nil { return err @@ -431,11 +432,11 @@ func (self *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) { return ret, nil } -func (self *SInstance) UpdateVM(name string) error { +func (self *SInstance) UpdateVM(ctx context.Context, name string) error { return self.host.zone.region.UpdateVM(self.InstanceId, name) } -func (self *SInstance) DeployVM(name string, password string, publicKey string, deleteKeypair bool, description string) error { +func (self *SInstance) DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error { var keypairName string if len(publicKey) > 0 { var err error @@ -448,7 +449,7 @@ func (self *SInstance) DeployVM(name string, password string, publicKey string, return self.host.zone.region.DeployVM(self.InstanceId, name, password, keypairName, deleteKeypair, description) } -func (self *SInstance) RebuildRoot(imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { +func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { keypair := "" if len(publicKey) > 0 { var err error @@ -465,15 +466,15 @@ func (self *SInstance) RebuildRoot(imageId string, passwd string, publicKey stri return diskId, nil } -func (self *SInstance) ChangeConfig(instanceId string, ncpu int, vmem int) error { +func (self *SInstance) ChangeConfig(ctx context.Context, ncpu int, vmem int) error { return self.host.zone.region.ChangeVMConfig(self.ZoneId, self.InstanceId, ncpu, vmem, nil) } -func (self *SInstance) AttachDisk(diskId string) error { +func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error { return self.host.zone.region.AttachDisk(self.InstanceId, diskId) } -func (self *SInstance) DetachDisk(diskId string) error { +func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error { return self.host.zone.region.DetachDisk(self.InstanceId, diskId) } @@ -692,7 +693,7 @@ func (self *SRegion) DeployVM(instanceId string, name string, password string, k } } -func (self *SInstance) DeleteVM() error { +func (self *SInstance) DeleteVM(ctx context.Context) error { for { err := self.host.zone.region.DeleteVM(self.InstanceId) if err != nil { @@ -700,6 +701,7 @@ func (self *SInstance) DeleteVM() error { log.Infof("The instance is initializing, try later ...") time.Sleep(10 * time.Second) } else { + log.Errorf("DeleteVM fail: %s", err) return err } } else { @@ -837,3 +839,7 @@ func (self *SInstance) GetExpiredAt() time.Time { func (self *SInstance) UpdateUserData(userData string) error { return self.host.zone.region.updateInstance(self.InstanceId, "", "", "", "", userData) } + +func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error { + return cloudprovider.ErrNotSupported +} diff --git a/pkg/util/aliyun/provider/provider.go b/pkg/util/aliyun/provider/provider.go index 5304c8aed5..6b3d2efa99 100644 --- a/pkg/util/aliyun/provider/provider.go +++ b/pkg/util/aliyun/provider/provider.go @@ -55,6 +55,10 @@ func (self *SAliyunProvider) IsPublicCloud() bool { return true } +func (self *SAliyunProvider) IsOnPremiseInfrastructure() bool { + return false +} + func (self *SAliyunProvider) GetId() string { return aliyun.CLOUD_PROVIDER_ALIYUN } @@ -79,24 +83,8 @@ func (self *SAliyunProvider) GetIRegions() []cloudprovider.ICloudRegion { return self.client.GetIRegions() } -func (self *SAliyunProvider) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) { - return self.client.GetIRegionById(id) -} - -func (self *SAliyunProvider) GetIHostById(id string) (cloudprovider.ICloudHost, error) { - return self.client.GetIHostById(id) -} - -func (self *SAliyunProvider) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { - return self.client.GetIVpcById(id) -} - -func (self *SAliyunProvider) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { - return self.client.GetIStorageById(id) -} - -func (self *SAliyunProvider) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { - return self.client.GetIStoragecacheById(id) +func (self *SAliyunProvider) GetIRegionById(extId string) (cloudprovider.ICloudRegion, error) { + return self.client.GetIRegionById(extId) } func (self *SAliyunProvider) GetBalance() (float64, error) { @@ -106,3 +94,7 @@ func (self *SAliyunProvider) GetBalance() (float64, error) { } return balance.AvailableAmount, nil } + +func (self *SAliyunProvider) GetOnPremiseIRegion() (cloudprovider.ICloudRegion, error) { + return nil, cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/aliyun/region.go b/pkg/util/aliyun/region.go index 2c959519db..7aad842f3c 100644 --- a/pkg/util/aliyun/region.go +++ b/pkg/util/aliyun/region.go @@ -551,6 +551,40 @@ func (self *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, er return nil, cloudprovider.ErrNotFound } +func (self *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) { + iHosts := make([]cloudprovider.ICloudHost, 0) + + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + iZoneHost, err := izones[i].GetIHosts() + if err != nil { + return nil, err + } + iHosts = append(iHosts, iZoneHost...) + } + return iHosts, nil +} + +func (self *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + iStores := make([]cloudprovider.ICloudStorage, 0) + + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + iZoneStores, err := izones[i].GetIStorages() + if err != nil { + return nil, err + } + iStores = append(iStores, iZoneStores...) + } + return iStores, nil +} + func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { storageCache := self.getStoragecache() if storageCache.GetGlobalId() == id { diff --git a/pkg/util/aliyun/storage.go b/pkg/util/aliyun/storage.go index d8dec31739..26c1a729c2 100644 --- a/pkg/util/aliyun/storage.go +++ b/pkg/util/aliyun/storage.go @@ -118,7 +118,7 @@ func (self *SStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudpr return disk, nil } -func (self *SStorage) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) { +func (self *SStorage) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) { if disk, err := self.zone.region.getDisk(idStr); err != nil { return nil, err } else { @@ -126,3 +126,7 @@ func (self *SStorage) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) { return disk, nil } } + +func (self *SStorage) GetMountPoint() string { + return "" +} diff --git a/pkg/util/aliyun/storagecache.go b/pkg/util/aliyun/storagecache.go index 231d5d3623..a1318f5bd0 100644 --- a/pkg/util/aliyun/storagecache.go +++ b/pkg/util/aliyun/storagecache.go @@ -87,6 +87,22 @@ func (self *SStoragecache) GetIImages() ([]cloudprovider.ICloudImage, error) { return self.iimages, nil } +func (self *SStoragecache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) { + parts, _, err := self.region.GetImages(ImageStatusType(""), ImageOwnerSelf, []string{extId}, "", 0, 1) + if err != nil { + return nil, err + } + if len(parts) == 0 { + return nil, cloudprovider.ErrNotFound + } + parts[0].storageCache = self + return &parts[0], nil +} + +func (self *SStoragecache) GetPath() string { + return "" +} + func (self *SStoragecache) UploadImage(userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist string, extId string, isForce bool) (string, error) { if len(extId) > 0 { diff --git a/pkg/util/aws/aws.go b/pkg/util/aws/aws.go index cbdfc5bf06..65224a5c37 100644 --- a/pkg/util/aws/aws.go +++ b/pkg/util/aws/aws.go @@ -185,18 +185,6 @@ func (self *SAwsClient) GetIStorageById(id string) (cloudprovider.ICloudStorage, return nil, cloudprovider.ErrNotFound } -func (self *SAwsClient) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { - for i := 0; i < len(self.iregions); i += 1 { - ihost, err := self.iregions[i].GetIStoragecacheById(id) - if err == nil { - return ihost, nil - } else if err != cloudprovider.ErrNotFound { - return nil, err - } - } - return nil, cloudprovider.ErrNotFound -} - type SAccountBalance struct { AvailableAmount float64 AvailableCashAmount float64 diff --git a/pkg/util/aws/disk.go b/pkg/util/aws/disk.go index e4c34ef1cc..20308db644 100644 --- a/pkg/util/aws/disk.go +++ b/pkg/util/aws/disk.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "context" "github.com/aws/aws-sdk-go/service/ec2" "github.com/coredns/coredns/plugin/pkg/log" "yunion.io/x/jsonutils" @@ -117,8 +118,8 @@ func (self *SDisk) GetExpiredAt() time.Time { return self.ExpiredTime } -func (self *SDisk) GetIStorge() cloudprovider.ICloudStorage { - return self.storage +func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { + return self.storage, nil } func (self *SDisk) GetDiskFormat() string { @@ -161,7 +162,7 @@ func (self *SDisk) GetMountpoint() string { return "" } -func (self *SDisk) Delete() error { +func (self *SDisk) Delete(ctx context.Context) error { if _, err := self.storage.zone.region.GetDisk(self.DiskId); err == cloudprovider.ErrNotFound { log.Errorf("Failed to find disk %s when delete", self.DiskId) return nil @@ -169,7 +170,7 @@ func (self *SDisk) Delete() error { return self.storage.zone.region.DeleteDisk(self.DiskId) } -func (self *SDisk) CreateISnapshot(name string, desc string) (cloudprovider.ICloudSnapshot, error) { +func (self *SDisk) CreateISnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudSnapshot, error) { if snapshotId, err := self.storage.zone.region.CreateSnapshot(self.DiskId, name, desc); err != nil { log.Errorf("createSnapshot fail %s", err) return nil, err @@ -214,11 +215,11 @@ func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { return isnapshots, nil } -func (self *SDisk) Resize(newSize int64) error { - return self.storage.zone.region.resizeDisk(self.DiskId, newSize) +func (self *SDisk) Resize(ctx context.Context, newSizeMb int64) error { + return self.storage.zone.region.resizeDisk(self.DiskId, newSizeMb) } -func (self *SDisk) Reset(snapshotId string) error { +func (self *SDisk) Reset(ctx context.Context, snapshotId string) error { return self.storage.zone.region.resetDisk(self.DiskId, snapshotId) } @@ -343,14 +344,15 @@ func (self *SRegion) DeleteDisk(diskId string) error { return err } -func (self *SRegion) resizeDisk(diskId string, size int64) error { +func (self *SRegion) resizeDisk(diskId string, sizeMb int64) error { // https://docs.aws.amazon.com/zh_cn/AWSEC2/latest/UserGuide/volume_constraints.html // MBR -> 2 TiB // GPT -> 16 TiB // size unit GiB + sizeGb := sizeMb / 1024 params := &ec2.ModifyVolumeInput{} - if size > 0 { - params.SetSize(size) + if sizeGb > 0 { + params.SetSize(sizeGb) } else { return fmt.Errorf("size should great than 0") } @@ -439,3 +441,7 @@ func (self *SRegion) CreateDisk(zoneId string, category string, name string, siz } return StrVal(ret.VolumeId), nil } + +func (disk *SDisk) GetAccessPath() string { + return "" +} diff --git a/pkg/util/aws/host.go b/pkg/util/aws/host.go index e6e3992b76..9f1782a931 100644 --- a/pkg/util/aws/host.go +++ b/pkg/util/aws/host.go @@ -268,3 +268,15 @@ func (self *SHost) _createVM(name, imgId string, sysDiskSize, cpu, memMB int, // 创建实例 return "", fmt.Errorf("Failed to create, specification not supported") } + +func (self *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SHost) GetIsMaintenance() bool { + return false +} + +func (self *SHost) GetVersion() string { + return AWS_API_VERSION +} diff --git a/pkg/util/aws/image.go b/pkg/util/aws/image.go index 700e43692b..d77deabae1 100644 --- a/pkg/util/aws/image.go +++ b/pkg/util/aws/image.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "context" "github.com/aws/aws-sdk-go/service/ec2" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -115,7 +116,7 @@ func (self *SImage) GetMetadata() *jsonutils.JSONDict { return data } -func (self *SImage) Delete() error { +func (self *SImage) Delete(ctx context.Context) error { // todo: implement me return self.storageCache.region.DeleteImage(self.ImageId) } diff --git a/pkg/util/aws/instance.go b/pkg/util/aws/instance.go index 306b57e718..be98beb211 100644 --- a/pkg/util/aws/instance.go +++ b/pkg/util/aws/instance.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "context" "github.com/aws/aws-sdk-go/service/ec2" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -276,7 +277,7 @@ func (self *SInstance) GetHypervisor() string { return models.HYPERVISOR_AWS } -func (self *SInstance) StartVM() error { +func (self *SInstance) StartVM(ctx context.Context) error { timeout := 300 * time.Second interval := 15 * time.Second @@ -300,7 +301,7 @@ func (self *SInstance) StartVM() error { return cloudprovider.ErrTimeout } -func (self *SInstance) StopVM(isForce bool) error { +func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { err := self.host.zone.region.StopVM(self.InstanceId, isForce) if err != nil { return err @@ -308,7 +309,7 @@ func (self *SInstance) StopVM(isForce bool) error { return cloudprovider.WaitStatus(self, models.VM_READY, 10*time.Second, 300*time.Second) // 5mintues } -func (self *SInstance) DeleteVM() error { +func (self *SInstance) DeleteVM(ctx context.Context) error { for { err := self.host.zone.region.DeleteVM(self.InstanceId) if err != nil { @@ -321,16 +322,16 @@ func (self *SInstance) DeleteVM() error { } -func (self *SInstance) UpdateVM(name string) error { +func (self *SInstance) UpdateVM(ctx context.Context, name string) error { return self.host.zone.region.UpdateVM(self.InstanceId, name) } -func (self *SInstance) RebuildRoot(imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { +func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { if len(publicKey) > 0 || len(passwd) > 0 { return "", fmt.Errorf("aws rebuild root not support specific publickey/password") } - diskId, err := self.host.zone.region.ReplaceSystemDisk(self.InstanceId, imageId, sysSizeGB) + diskId, err := self.host.zone.region.ReplaceSystemDisk(ctx, self.InstanceId, imageId, sysSizeGB) if err != nil { return "", err } @@ -338,11 +339,11 @@ func (self *SInstance) RebuildRoot(imageId string, passwd string, publicKey stri return diskId, nil } -func (self *SInstance) DeployVM(name string, password string, publicKey string, deleteKeypair bool, description string) error { +func (self *SInstance) DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error { return self.host.zone.region.DeployVM(self.InstanceId, name, password, publicKey, deleteKeypair, description) } -func (self *SInstance) ChangeConfig(instanceId string, ncpu int, vmem int) error { +func (self *SInstance) ChangeConfig(ctx context.Context, ncpu int, vmem int) error { return self.host.zone.region.ChangeVMConfig(self.ZoneId, self.InstanceId, ncpu, vmem, nil) } @@ -350,7 +351,7 @@ func (self *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) { panic("implement me") } -func (self *SInstance) AttachDisk(diskId string) error { +func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error { name, err := NextDeviceName(self.DeviceNames) if err != nil { return err @@ -365,7 +366,7 @@ func (self *SInstance) AttachDisk(diskId string) error { return nil } -func (self *SInstance) DetachDisk(diskId string) error { +func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error { return self.host.zone.region.DetachDisk(self.InstanceId, diskId) } @@ -702,7 +703,7 @@ func (self *SRegion) UpdateVM(instanceId string, hostname string) error { return fmt.Errorf("aws not support change hostname.") } -func (self *SRegion) ReplaceSystemDisk(instanceId string, imageId string, sysDiskSizeGB int) (string, error) { +func (self *SRegion) ReplaceSystemDisk(ctx context.Context, instanceId string, imageId string, sysDiskSizeGB int) (string, error) { instance, err := self.GetInstance(instanceId) if err != nil { return "", err @@ -736,13 +737,13 @@ func (self *SRegion) ReplaceSystemDisk(instanceId string, imageId string, sysDis self.ec2Client.WaitUntilVolumeAvailable(&ec2.DescribeVolumesInput{VolumeIds: []*string{&diskId}}) // todo: 检查instance状态 - err = instance.DetachDisk(rootDisk.DiskId) + err = instance.DetachDisk(ctx, rootDisk.DiskId) if err != nil { return "", err } self.ec2Client.WaitUntilInstanceStopped(&ec2.DescribeInstancesInput{InstanceIds: []*string{&instanceId}}) - err = instance.AttachDisk(diskId) + err = instance.AttachDisk(ctx, diskId) if err != nil { return "", err } @@ -792,3 +793,7 @@ func (self *SRegion) AttachDisk(instanceId string, diskId string, deviceName str _, err := self.ec2Client.AttachVolume(params) return err } + +func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error { + return cloudprovider.ErrNotSupported +} diff --git a/pkg/util/aws/provider/provider.go b/pkg/util/aws/provider/provider.go index c774e51f6c..fc44072ac3 100644 --- a/pkg/util/aws/provider/provider.go +++ b/pkg/util/aws/provider/provider.go @@ -58,26 +58,14 @@ func (self *SAwsProvider) IsPublicCloud() bool { return true } +func (self *SAwsProvider) IsOnPremiseInfrastructure() bool { + return false +} + func (self *SAwsProvider) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) { return self.client.GetIRegionById(id) } -func (self *SAwsProvider) GetIHostById(id string) (cloudprovider.ICloudHost, error) { - return self.client.GetIHostById(id) -} - -func (self *SAwsProvider) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { - return self.client.GetIVpcById(id) -} - -func (self *SAwsProvider) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { - return self.client.GetIStorageById(id) -} - -func (self *SAwsProvider) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { - return self.client.GetIStoragecacheById(id) -} - func (self *SAwsProvider) GetBalance() (float64, error) { balance, err := self.client.QueryAccountBalance() if err != nil { @@ -85,3 +73,7 @@ func (self *SAwsProvider) GetBalance() (float64, error) { } return balance.AvailableAmount, nil } + +func (self *SAwsProvider) GetOnPremiseIRegion() (cloudprovider.ICloudRegion, error) { + return nil, cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/aws/region.go b/pkg/util/aws/region.go index 910bae7767..3206112667 100644 --- a/pkg/util/aws/region.go +++ b/pkg/util/aws/region.go @@ -348,6 +348,40 @@ func (self *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, er return nil, cloudprovider.ErrNotFound } +func (self *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) { + iHosts := make([]cloudprovider.ICloudHost, 0) + + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + iZoneHost, err := izones[i].GetIHosts() + if err != nil { + return nil, err + } + iHosts = append(iHosts, iZoneHost...) + } + return iHosts, nil +} + +func (self *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + iStores := make([]cloudprovider.ICloudStorage, 0) + + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + iZoneStores, err := izones[i].GetIStorages() + if err != nil { + return nil, err + } + iStores = append(iStores, iZoneStores...) + } + return iStores, nil +} + func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { if self.storageCache == nil { self.storageCache = &SStoragecache{region: self} diff --git a/pkg/util/aws/storage.go b/pkg/util/aws/storage.go index 35d7081df8..937598edcc 100644 --- a/pkg/util/aws/storage.go +++ b/pkg/util/aws/storage.go @@ -114,7 +114,7 @@ func (self *SStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudpr return disk, nil } -func (self *SStorage) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) { +func (self *SStorage) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) { if disk, err := self.zone.region.GetDisk(idStr); err != nil { return nil, err } else { @@ -122,3 +122,7 @@ func (self *SStorage) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) { return disk, nil } } + +func (self *SStorage) GetMountPoint() string { + return "" +} diff --git a/pkg/util/aws/storagecache.go b/pkg/util/aws/storagecache.go index b1a42471bd..c43e88ec6f 100644 --- a/pkg/util/aws/storagecache.go +++ b/pkg/util/aws/storagecache.go @@ -61,6 +61,22 @@ func (self *SStoragecache) GetIImages() ([]cloudprovider.ICloudImage, error) { return self.iimages, nil } +func (self *SStoragecache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) { + parts, _, err := self.region.GetImages(ImageStatusType(""), ImageOwnerSelf, []string{extId}, "", 0, 1) + if err != nil { + return nil, err + } + if len(parts) == 0 { + return nil, cloudprovider.ErrNotFound + } + parts[0].storageCache = self + return &parts[0], nil +} + +func (self *SStoragecache) GetPath() string { + return "" +} + func (self *SStoragecache) GetManagerId() string { return self.region.client.providerId } diff --git a/pkg/util/azure/azure.go b/pkg/util/azure/azure.go index af278ffe87..634aac1b9b 100644 --- a/pkg/util/azure/azure.go +++ b/pkg/util/azure/azure.go @@ -745,18 +745,6 @@ func (self *SAzureClient) GetIStorageById(id string) (cloudprovider.ICloudStorag return nil, cloudprovider.ErrNotFound } -func (self *SAzureClient) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { - for i := 0; i < len(self.iregions); i += 1 { - ihost, err := self.iregions[i].GetIStoragecacheById(id) - if err == nil { - return ihost, nil - } else if err != cloudprovider.ErrNotFound { - return nil, err - } - } - return nil, cloudprovider.ErrNotFound -} - type SAccountBalance struct { AvailableAmount float64 AvailableCashAmount float64 diff --git a/pkg/util/azure/classic_disk.go b/pkg/util/azure/classic_disk.go index dd11cf71b7..f41c94d63e 100644 --- a/pkg/util/azure/classic_disk.go +++ b/pkg/util/azure/classic_disk.go @@ -4,6 +4,7 @@ import ( "strings" "time" + "context" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -106,11 +107,11 @@ func (self *SClassicDisk) GetMetadata() *jsonutils.JSONDict { return data } -func (self *SClassicDisk) CreateISnapshot(name, desc string) (cloudprovider.ICloudSnapshot, error) { +func (self *SClassicDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) { return nil, cloudprovider.ErrNotSupported } -func (self *SClassicDisk) Delete() error { +func (self *SClassicDisk) Delete(ctx context.Context) error { return cloudprovider.ErrNotImplemented } @@ -186,8 +187,8 @@ func (self *SClassicDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error return nil, cloudprovider.ErrNotSupported } -func (self *SClassicDisk) GetIStorge() cloudprovider.ICloudStorage { - return self.storage +func (self *SClassicDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { + return self.storage, nil } func (self *SClassicDisk) GetName() string { @@ -206,10 +207,14 @@ func (self *SClassicDisk) Refresh() error { return nil } -func (self *SClassicDisk) Reset(snapshotId string) error { +func (self *SClassicDisk) Reset(ctx context.Context, snapshotId string) error { return cloudprovider.ErrNotSupported } -func (self *SClassicDisk) Resize(size int64) error { +func (self *SClassicDisk) Resize(ctx context.Context, sizeMb int64) error { return cloudprovider.ErrNotSupported } + +func (disk *SClassicDisk) GetAccessPath() string { + return "" +} diff --git a/pkg/util/azure/classic_host.go b/pkg/util/azure/classic_host.go index f2c7aa5654..a7d4349939 100644 --- a/pkg/util/azure/classic_host.go +++ b/pkg/util/azure/classic_host.go @@ -167,3 +167,15 @@ func (self *SClassicHost) GetIWires() ([]cloudprovider.ICloudWire, error) { func (self *SClassicHost) GetManagerId() string { return self.zone.region.client.providerId } + +func (host *SClassicHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (host *SClassicHost) GetIsMaintenance() bool { + return false +} + +func (host *SClassicHost) GetVersion() string { + return AZURE_API_VERSION +} diff --git a/pkg/util/azure/classic_instance.go b/pkg/util/azure/classic_instance.go index 4653c6ca5f..8380810f02 100644 --- a/pkg/util/azure/classic_instance.go +++ b/pkg/util/azure/classic_instance.go @@ -5,6 +5,7 @@ import ( "strings" "time" + "context" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -268,38 +269,35 @@ func (self *SClassicInstance) GetIHost() cloudprovider.ICloudHost { return self.host } -func (self *SClassicInstance) AttachDisk(diskId string) error { +func (self *SClassicInstance) AttachDisk(ctx context.Context, diskId string) error { if err := self.host.zone.region.AttachDisk(self.ID, diskId); err != nil { return err } return cloudprovider.WaitStatus(self, self.GetStatus(), 10*time.Second, 300*time.Second) } -func (self *SClassicInstance) DetachDisk(diskId string) error { +func (self *SClassicInstance) DetachDisk(ctx context.Context, diskId string) error { if err := self.host.zone.region.DetachDisk(self.ID, diskId); err != nil { return err } return cloudprovider.WaitStatus(self, self.GetStatus(), 10*time.Second, 300*time.Second) } -func (self *SClassicInstance) ChangeConfig(instanceId string, ncpu int, vmem int) error { - if err := self.host.zone.region.ChangeVMConfig(instanceId, ncpu, vmem); err != nil { - return err - } - return cloudprovider.WaitStatus(self, self.GetStatus(), 10*time.Second, 300*time.Second) +func (self *SClassicInstance) ChangeConfig(ctx context.Context, ncpu int, vmem int) error { + return cloudprovider.ErrNotImplemented } -func (self *SClassicInstance) DeployVM(name string, password string, publicKey string, deleteKeypair bool, description string) error { +func (self *SClassicInstance) DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error { return cloudprovider.ErrNotImplemented //return self.host.zone.region.DeployVM(self.ID, name, password, publicKey, deleteKeypair, description) } -func (self *SClassicInstance) RebuildRoot(imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { +func (self *SClassicInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { return "", cloudprovider.ErrNotImplemented //return self.host.zone.region.ReplaceSystemDisk(self.ID, imageId, passwd, publicKey, int32(sysSizeGB)) } -func (self *SClassicInstance) UpdateVM(name string) error { +func (self *SClassicInstance) UpdateVM(ctx context.Context, name string) error { return cloudprovider.ErrNotSupported } @@ -315,7 +313,7 @@ func (self *SClassicInstance) GetGlobalId() string { return strings.ToLower(self.ID) } -func (self *SClassicInstance) DeleteVM() error { +func (self *SClassicInstance) DeleteVM(ctx context.Context) error { if err := self.host.zone.region.DeleteVM(self.ID); err != nil { return err } @@ -415,14 +413,14 @@ func (self *SClassicInstance) GetVNCInfo() (jsonutils.JSONObject, error) { return ret, nil } -func (self *SClassicInstance) StartVM() error { +func (self *SClassicInstance) StartVM(ctx context.Context) error { if err := self.host.zone.region.StartVM(self.ID); err != nil { return err } return cloudprovider.WaitStatus(self, models.VM_RUNNING, 10*time.Second, 300*time.Second) } -func (self *SClassicInstance) StopVM(isForce bool) error { +func (self *SClassicInstance) StopVM(ctx context.Context, isForce bool) error { err := self.host.zone.region.StopClassicVM(self.ID, isForce) if err != nil { return err @@ -512,3 +510,7 @@ func (self *SClassicInstance) GetExpiredAt() time.Time { func (self *SClassicInstance) UpdateUserData(userData string) error { return cloudprovider.ErrNotSupported } + +func (self *SClassicInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error { + return cloudprovider.ErrNotSupported +} diff --git a/pkg/util/azure/classic_storage.go b/pkg/util/azure/classic_storage.go index 7f33ce8012..329f965637 100644 --- a/pkg/util/azure/classic_storage.go +++ b/pkg/util/azure/classic_storage.go @@ -66,7 +66,7 @@ func (self *SClassicStorage) CreateIDisk(name string, sizeGb int, desc string) ( return nil, cloudprovider.ErrNotImplemented } -func (self *SClassicStorage) GetIDisk(diskId string) (cloudprovider.ICloudDisk, error) { +func (self *SClassicStorage) GetIDiskById(diskId string) (cloudprovider.ICloudDisk, error) { disks, err := self.GetIDisks() if err != nil { return nil, err @@ -125,3 +125,7 @@ func (self *SClassicStorage) Refresh() error { // do nothing return nil } + +func (self *SClassicStorage) GetMountPoint() string { + return "" +} diff --git a/pkg/util/azure/disk.go b/pkg/util/azure/disk.go index e1e8caa9ae..57b7490183 100644 --- a/pkg/util/azure/disk.go +++ b/pkg/util/azure/disk.go @@ -5,6 +5,7 @@ import ( "strings" "time" + "context" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -168,12 +169,12 @@ func (self *SDisk) Refresh() error { return nil } -func (self *SDisk) Delete() error { +func (self *SDisk) Delete(ctx context.Context) error { return self.storage.zone.region.deleteDisk(self.ID) } -func (self *SDisk) Resize(size int64) error { - return self.storage.zone.region.ResizeDisk(self.ID, int32(size)) +func (self *SDisk) Resize(ctx context.Context, sizeMb int64) error { + return self.storage.zone.region.ResizeDisk(self.ID, int32(sizeMb/1024)) } func (self *SDisk) GetName() string { @@ -191,8 +192,8 @@ func (self *SDisk) IsEmulated() bool { return false } -func (self *SDisk) GetIStorge() cloudprovider.ICloudStorage { - return self.storage +func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { + return self.storage, nil } func (self *SDisk) GetFsFormat() string { @@ -241,7 +242,7 @@ func (self *SDisk) GetDiskType() string { return models.DISK_TYPE_DATA } -func (self *SDisk) CreateISnapshot(name, desc string) (cloudprovider.ICloudSnapshot, error) { +func (self *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) { if snapshot, err := self.storage.zone.region.CreateSnapshot(self.ID, name, desc); err != nil { log.Errorf("createSnapshot fail %s", err) return nil, err @@ -274,7 +275,7 @@ func (self *SDisk) GetBillingType() string { } func (self *SDisk) GetExpiredAt() time.Time { - return time.Now() + return time.Time{} } func (self *SDisk) GetSnapshotDetail(snapshotId string) (*SSnapshot, error) { @@ -313,10 +314,14 @@ func (region *SRegion) GetSnapShots(diskId string) ([]SSnapshot, error) { return result, nil } -func (self *SDisk) Reset(snapshotId string) error { +func (self *SDisk) Reset(ctx context.Context, snapshotId string) error { return self.storage.zone.region.resetDisk(self.ID, snapshotId) } func (self *SRegion) resetDisk(diskId, snapshotId string) error { return cloudprovider.ErrNotSupported } + +func (disk *SDisk) GetAccessPath() string { + return "" +} diff --git a/pkg/util/azure/host.go b/pkg/util/azure/host.go index 970a497cd7..e14e863781 100644 --- a/pkg/util/azure/host.go +++ b/pkg/util/azure/host.go @@ -282,3 +282,15 @@ func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) { func (self *SHost) GetManagerId() string { return self.zone.region.client.providerId } + +func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (host *SHost) GetIsMaintenance() bool { + return false +} + +func (host *SHost) GetVersion() string { + return AZURE_API_VERSION +} diff --git a/pkg/util/azure/image.go b/pkg/util/azure/image.go index a5fe3a2c30..8c53edcefd 100644 --- a/pkg/util/azure/image.go +++ b/pkg/util/azure/image.go @@ -3,6 +3,7 @@ package azure import ( "strings" + "context" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -141,6 +142,20 @@ func (self *SRegion) GetImageByName(name string) (*SImage, error) { return nil, cloudprovider.ErrNotFound } +func (self *SRegion) GetImageById(idstr string) (*SImage, error) { + images := []SImage{} + err := self.client.ListAll("Microsoft.Compute/images", &images) + if err != nil { + return nil, err + } + for i := 0; i < len(images); i++ { + if images[i].ID == idstr { + return &images[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + func (self *SRegion) CreateImageByBlob(imageName, osType, blobURI string, diskSizeGB int32) (*SImage, error) { if diskSizeGB < 1 || diskSizeGB > 4095 { diskSizeGB = 30 @@ -207,7 +222,7 @@ func (self *SImage) GetBlobUri() string { return self.Properties.StorageProfile.OsDisk.BlobURI } -func (self *SImage) Delete() error { +func (self *SImage) Delete(ctx context.Context) error { return self.storageCache.region.DeleteImage(self.ID) } diff --git a/pkg/util/azure/instance.go b/pkg/util/azure/instance.go index 9659ab3f58..c874b3e224 100644 --- a/pkg/util/azure/instance.go +++ b/pkg/util/azure/instance.go @@ -5,6 +5,7 @@ import ( "strings" "time" + "context" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -455,7 +456,7 @@ func (self *SInstance) GetIHost() cloudprovider.ICloudHost { return self.host } -func (self *SInstance) AttachDisk(diskId string) error { +func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error { if err := self.host.zone.region.AttachDisk(self.ID, diskId); err != nil { return err } @@ -497,7 +498,7 @@ func (region *SRegion) AttachDisk(instanceId, diskId string) error { return region.client.Update(jsonutils.Marshal(instance), nil) } -func (self *SInstance) DetachDisk(diskId string) error { +func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error { if err := self.host.zone.region.DetachDisk(self.ID, diskId); err != nil { return err } @@ -525,7 +526,7 @@ func (region *SRegion) DetachDisk(instanceId, diskId string) error { return region.client.Update(jsonutils.Marshal(instance), nil) } -func (self *SInstance) ChangeConfig(instanceId string, ncpu int, vmem int) error { +func (self *SInstance) ChangeConfig(ctx context.Context, ncpu int, vmem int) error { for _, vmSize := range self.host.zone.region.getHardwareProfile(ncpu, vmem) { self.Properties.HardwareProfile.VMSize = vmSize self.Properties.ProvisioningState = "" @@ -539,16 +540,16 @@ func (self *SInstance) ChangeConfig(instanceId string, ncpu int, vmem int) error return fmt.Errorf("Failed to change vm config, specification not supported") } -func (region *SRegion) ChangeVMConfig(instanceId string, ncpu int, vmem int) error { +func (region *SRegion) ChangeVMConfig(ctx context.Context, instanceId string, ncpu int, vmem int) error { instacen, err := region.GetInstance(instanceId) if err != nil { return err } - return instacen.ChangeConfig(instanceId, ncpu, vmem) + return instacen.ChangeConfig(ctx, ncpu, vmem) } -func (self *SInstance) DeployVM(name string, password string, publicKey string, deleteKeypair bool, description string) error { - return self.host.zone.region.DeployVM(self.ID, name, password, publicKey, deleteKeypair, description) +func (self *SInstance) DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error { + return self.host.zone.region.DeployVM(ctx, self.ID, name, password, publicKey, deleteKeypair, description) } type VirtualMachineExtensionProperties struct { @@ -638,7 +639,7 @@ func (region *SRegion) resetPassword(instanceId, username, password string) erro return region.resetLoginInfo(instanceId, setting) } -func (region *SRegion) DeployVM(instanceId, name, password, publicKey string, deleteKeypair bool, description string) error { +func (region *SRegion) DeployVM(ctx context.Context, instanceId, name, password, publicKey string, deleteKeypair bool, description string) error { instance, err := region.GetInstance(instanceId) if err != nil { return err @@ -652,7 +653,7 @@ func (region *SRegion) DeployVM(instanceId, name, password, publicKey string, de return region.resetPassword(instanceId, instance.Properties.OsProfile.AdminUsername, password) } -func (self *SInstance) RebuildRoot(imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { +func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { return self.host.zone.region.ReplaceSystemDisk(self.ID, imageId, passwd, publicKey, int32(sysSizeGB)) } @@ -738,7 +739,7 @@ func (region *SRegion) ReplaceSystemDisk(instanceId, imageId, passwd, publicKey return disk.ID, nil } -func (self *SInstance) UpdateVM(name string) error { +func (self *SInstance) UpdateVM(ctx context.Context, name string) error { return cloudprovider.ErrNotSupported } @@ -758,7 +759,7 @@ func (self *SRegion) DeleteVM(instanceId string) error { return self.doDeleteVM(instanceId) } -func (self *SInstance) DeleteVM() error { +func (self *SInstance) DeleteVM(ctx context.Context) error { sysDiskId := "" if self.Properties.StorageProfile.OsDisk.ManagedDisk != nil { sysDiskId = self.Properties.StorageProfile.OsDisk.ManagedDisk.ID @@ -939,7 +940,7 @@ func (self *SRegion) StartVM(instanceId string) error { return err } -func (self *SInstance) StartVM() error { +func (self *SInstance) StartVM(ctx context.Context) error { if err := self.host.zone.region.StartVM(self.ID); err != nil { return err } @@ -947,7 +948,7 @@ func (self *SInstance) StartVM() error { return cloudprovider.WaitStatus(self, models.VM_RUNNING, 10*time.Second, 300*time.Second) } -func (self *SInstance) StopVM(isForce bool) error { +func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { err := self.host.zone.region.StopVM(self.ID, isForce) if err != nil { return err @@ -991,9 +992,13 @@ func (self *SInstance) GetBillingType() string { } func (self *SInstance) GetExpiredAt() time.Time { - return time.Now() + return time.Time{} } func (self *SInstance) UpdateUserData(userData string) error { return cloudprovider.ErrNotSupported } + +func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error { + return cloudprovider.ErrNotSupported +} diff --git a/pkg/util/azure/provider/provider.go b/pkg/util/azure/provider/provider.go index 5430910e52..a4925b2395 100644 --- a/pkg/util/azure/provider/provider.go +++ b/pkg/util/azure/provider/provider.go @@ -35,6 +35,10 @@ func (self *SAzureProvider) IsPublicCloud() bool { return true } +func (self *SAzureProvider) IsOnPremiseInfrastructure() bool { + return false +} + func (self *SAzureProvider) GetId() string { return azure.CLOUD_PROVIDER_AZURE } @@ -63,22 +67,6 @@ func (self *SAzureProvider) GetIRegionById(id string) (cloudprovider.ICloudRegio return self.client.GetIRegionById(id) } -func (self *SAzureProvider) GetIHostById(id string) (cloudprovider.ICloudHost, error) { - return self.client.GetIHostById(id) -} - -func (self *SAzureProvider) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { - return self.client.GetIVpcById(id) -} - -func (self *SAzureProvider) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { - return self.client.GetIStorageById(id) -} - -func (self *SAzureProvider) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { - return self.client.GetIStoragecacheById(id) -} - func (self *SAzureProvider) GetBalance() (float64, error) { balance, err := self.client.QueryAccountBalance() if err != nil { @@ -86,3 +74,7 @@ func (self *SAzureProvider) GetBalance() (float64, error) { } return balance.AvailableAmount, nil } + +func (self *SAzureProvider) GetOnPremiseIRegion() (cloudprovider.ICloudRegion, error) { + return nil, cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/azure/region.go b/pkg/util/azure/region.go index 014538b020..56f1648de1 100644 --- a/pkg/util/azure/region.go +++ b/pkg/util/azure/region.go @@ -176,6 +176,40 @@ func (self *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, er return nil, cloudprovider.ErrNotFound } +func (self *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) { + iHosts := make([]cloudprovider.ICloudHost, 0) + + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + iZoneHost, err := izones[i].GetIHosts() + if err != nil { + return nil, err + } + iHosts = append(iHosts, iZoneHost...) + } + return iHosts, nil +} + +func (self *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + iStores := make([]cloudprovider.ICloudStorage, 0) + + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + iZoneStores, err := izones[i].GetIStorages() + if err != nil { + return nil, err + } + iStores = append(iStores, iZoneStores...) + } + return iStores, nil +} + func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { storageCache := self.getStoragecache() if storageCache.GetGlobalId() == id { diff --git a/pkg/util/azure/storage.go b/pkg/util/azure/storage.go index 6040a9a0d3..f77052649b 100644 --- a/pkg/util/azure/storage.go +++ b/pkg/util/azure/storage.go @@ -70,7 +70,7 @@ func (self *SStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudpr return disk, nil } -func (self *SStorage) GetIDisk(diskId string) (cloudprovider.ICloudDisk, error) { +func (self *SStorage) GetIDiskById(diskId string) (cloudprovider.ICloudDisk, error) { if disk, err := self.zone.region.GetDisk(diskId); err != nil { return nil, err } else { @@ -159,3 +159,7 @@ func (self *SStorage) Refresh() error { // do nothing return nil } + +func (self *SStorage) GetMountPoint() string { + return "" +} diff --git a/pkg/util/azure/storagecache.go b/pkg/util/azure/storagecache.go index bcaf4036a5..7d3b0e5b34 100644 --- a/pkg/util/azure/storagecache.go +++ b/pkg/util/azure/storagecache.go @@ -84,6 +84,19 @@ func (self *SStoragecache) GetIImages() ([]cloudprovider.ICloudImage, error) { return self.iimages, nil } +func (self *SStoragecache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) { + img, err := self.region.GetImageById(extId) + if err != nil { + return nil, err + } + img.storageCache = self + return img, nil +} + +func (self *SStoragecache) GetPath() string { + return "" +} + func (self *SStoragecache) UploadImage(userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist string, extId string, isForce bool) (string, error) { if len(extId) > 0 { log.Debugf("UploadImage: Image external ID exists %s", extId) diff --git a/pkg/util/esxi/datacenter.go b/pkg/util/esxi/datacenter.go index 7da30e1dfb..1a6a5c0124 100644 --- a/pkg/util/esxi/datacenter.go +++ b/pkg/util/esxi/datacenter.go @@ -1,7 +1,10 @@ package esxi import ( + "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" + "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" ) @@ -26,6 +29,10 @@ func (dc *SDatacenter) getDatacenter() *mo.Datacenter { return dc.object.(*mo.Datacenter) } +func (dc *SDatacenter) getObjectDatacenter() *object.Datacenter { + return object.NewDatacenter(dc.manager.client.Client, dc.object.Reference()) +} + func (dc *SDatacenter) scanHosts() error { if dc.ihosts == nil { var hosts []mo.HostSystem @@ -51,15 +58,11 @@ func (dc *SDatacenter) GetIHosts() ([]cloudprovider.ICloudHost, error) { func (dc *SDatacenter) scanDatastores() error { if dc.istorages == nil { - stores := make([]mo.Datastore, 0) + var stores []mo.Datastore dsList := dc.getDatacenter().Datastore - for i := 0; i < len(dsList); i += 1 { - var ds mo.Datastore - err := dc.manager.reference2Object(dsList[i], DATASTORE_PROPS, &ds) - if err != nil { - return err - } - stores = append(stores, ds) + err := dc.manager.references2Objects(dsList, DATASTORE_PROPS, &stores) + if err != nil { + return err } dc.istorages = make([]cloudprovider.ICloudStorage, len(stores)) for i := 0; i < len(stores); i += 1 { @@ -76,3 +79,48 @@ func (dc *SDatacenter) GetIStorages() ([]cloudprovider.ICloudStorage, error) { } return dc.istorages, nil } + +func (dc *SDatacenter) GetIHostByMoId(idstr string) (cloudprovider.ICloudHost, error) { + ihosts, err := dc.GetIHosts() + if err != nil { + return nil, err + } + for i := 0; i < len(ihosts); i += 1 { + if ihosts[i].GetId() == idstr { + return ihosts[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (dc *SDatacenter) GetIStorageByMoId(idstr string) (cloudprovider.ICloudStorage, error) { + istorages, err := dc.GetIStorages() + if err != nil { + return nil, err + } + for i := 0; i < len(istorages); i += 1 { + if istorages[i].GetId() == idstr { + return istorages[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (dc *SDatacenter) getDcObj() *object.Datacenter { + return object.NewDatacenter(dc.manager.client.Client, dc.object.Reference()) +} + +func (dc *SDatacenter) fetchVms(vmRefs []types.ManagedObjectReference) ([]cloudprovider.ICloudVM, error) { + var vms []mo.VirtualMachine + err := dc.manager.references2Objects(vmRefs, VIRTUAL_MACHINE_PROPS, &vms) + if err != nil { + log.Errorf("references2Objects fail %s", err) + return nil, err + } + + retVms := make([]cloudprovider.ICloudVM, len(vms)) + for i := 0; i < len(vms); i += 1 { + retVms[i] = NewVirtualMachine(dc.manager, &vms[i], dc) + } + return retVms, nil +} diff --git a/pkg/util/esxi/device.go b/pkg/util/esxi/device.go new file mode 100644 index 0000000000..4e2df97367 --- /dev/null +++ b/pkg/util/esxi/device.go @@ -0,0 +1,92 @@ +package esxi + +import ( + "reflect" + "strings" + + "github.com/vmware/govmomi/vim25/types" + // "yunion.io/x/log" +) + +type SVirtualDevice struct { + vm *SVirtualMachine + dev types.BaseVirtualDevice + index int +} + +func NewVirtualDevice(vm *SVirtualMachine, dev types.BaseVirtualDevice, index int) SVirtualDevice { + return SVirtualDevice{ + vm: vm, + dev: dev, + index: index, + } +} + +func (dev *SVirtualDevice) getKey() int32 { + return dev.dev.GetVirtualDevice().Key +} + +func (dev *SVirtualDevice) getControllerKey() int32 { + return dev.dev.GetVirtualDevice().ControllerKey +} + +func (dev *SVirtualDevice) getIndex() int { + return dev.index +} + +func (dev *SVirtualDevice) getLabel() string { + return dev.dev.GetVirtualDevice().DeviceInfo.GetDescription().Label +} + +func (dev *SVirtualDevice) GetDriver() string { + val := reflect.Indirect(reflect.ValueOf(dev.dev)) + driver := strings.ToLower(val.Type().Name()) + if strings.Contains(driver, "virtualmachine") { + return strings.Replace(driver, "virtualmachine", "", -1) + } else if strings.Contains(driver, "virtual") { + return strings.Replace(driver, "virtual", "", -1) + } else { + return driver + } +} + +type SVirtualVGA struct { + SVirtualDevice +} + +func NewVirtualVGA(vm *SVirtualMachine, dev types.BaseVirtualDevice, index int) SVirtualVGA { + return SVirtualVGA{ + NewVirtualDevice(vm, dev, index), + } +} + +func (vga *SVirtualVGA) getVirtualMachineVideoCard() *types.VirtualMachineVideoCard { + return vga.dev.(*types.VirtualMachineVideoCard) +} + +func (vga *SVirtualVGA) GetEnable3D() bool { + p3d := vga.getVirtualMachineVideoCard().Enable3DSupport + return p3d != nil && *p3d +} + +func (vga *SVirtualVGA) GetRamSizeMB() int { + return int(vga.getVirtualMachineVideoCard().VideoRamSizeInKB / 1024) +} + +func (vga *SVirtualVGA) String() string { + return vga.getVirtualMachineVideoCard().DeviceInfo.GetDescription().Summary +} + +type SVirtualCdrom struct { + SVirtualDevice +} + +func NewVirtualCdrom(vm *SVirtualMachine, dev types.BaseVirtualDevice, index int) SVirtualCdrom { + return SVirtualCdrom{ + NewVirtualDevice(vm, dev, index), + } +} + +func (cdrom *SVirtualCdrom) getVirtualCdrom() *types.VirtualCdrom { + return cdrom.dev.(*types.VirtualCdrom) +} diff --git a/pkg/util/esxi/devtools.go b/pkg/util/esxi/devtools.go new file mode 100644 index 0000000000..ec66271fd7 --- /dev/null +++ b/pkg/util/esxi/devtools.go @@ -0,0 +1,43 @@ +package esxi + +import ( + "github.com/vmware/govmomi/vim25/types" +) + +func NewDiskDev(sizeMb int64, templatePath string, uuid string, index int32, key int32, controlKey int32) *types.VirtualDisk { + device := types.VirtualDisk{} + + var backFile *types.VirtualDiskFlatVer2BackingInfo + if len(templatePath) > 0 { + backFile = &types.VirtualDiskFlatVer2BackingInfo{} + backFile.FileName = templatePath + } + + diskFile := types.VirtualDiskFlatVer2BackingInfo{} + diskFile.DiskMode = "persistent" + thinProvisioned := true + diskFile.ThinProvisioned = &thinProvisioned + diskFile.Uuid = uuid + if backFile != nil { + diskFile.Parent = backFile + } + + device.Backing = &diskFile + + if sizeMb > 0 { + device.CapacityInKB = sizeMb * 1024 + } + + device.ControllerKey = controlKey + device.Key = key + index + device.UnitNumber = &index + + return &device +} + +func addDevSpec(device types.BaseVirtualDevice) *types.VirtualDeviceConfigSpec { + spec := types.VirtualDeviceConfigSpec{} + spec.Operation = types.VirtualDeviceConfigSpecOperationAdd + spec.Device = device + return &spec +} diff --git a/pkg/util/esxi/fakeregion.go b/pkg/util/esxi/fakeregion.go new file mode 100644 index 0000000000..da0fe948cd --- /dev/null +++ b/pkg/util/esxi/fakeregion.go @@ -0,0 +1,69 @@ +package esxi + +import ( + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" +) + +func (cli *SESXiClient) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (cli *SESXiClient) GetISnapshotById(snapshotId string) (cloudprovider.ICloudSnapshot, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (cli *SESXiClient) GetIHosts() ([]cloudprovider.ICloudHost, error) { + dcs, err := cli.GetDatacenters() + if err != nil { + return nil, err + } + + ihosts := make([]cloudprovider.ICloudHost, 0) + for i := 0; i < len(dcs); i += 1 { + dcIHosts, err := dcs[i].GetIHosts() + if err != nil { + return nil, err + } + ihosts = append(ihosts, dcIHosts...) + } + return ihosts, nil +} + +func (cli *SESXiClient) GetIHostById(id string) (cloudprovider.ICloudHost, error) { + return cli.FindHostByIp(id) +} + +func (cli *SESXiClient) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + dcs, err := cli.GetDatacenters() + if err != nil { + return nil, err + } + + iStorages := make([]cloudprovider.ICloudStorage, 0) + for i := 0; i < len(dcs); i += 1 { + dcIStorages, err := dcs[i].GetIStorages() + if err != nil { + return nil, err + } + iStorages = append(iStorages, dcIStorages...) + } + return iStorages, nil +} + +func (cli *SESXiClient) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { + iStorages, err := cli.GetIStorages() + if err != nil { + return nil, err + } + for i := 0; i < len(iStorages); i += 1 { + if iStorages[i].GetGlobalId() == id { + return iStorages[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (cli *SESXiClient) GetProvider() string { + return models.CLOUD_PROVIDER_VMWARE +} diff --git a/pkg/util/esxi/guestos.go b/pkg/util/esxi/guestos.go new file mode 100644 index 0000000000..a5c77f185b --- /dev/null +++ b/pkg/util/esxi/guestos.go @@ -0,0 +1,211 @@ +package esxi + +type TOSType string + +type TOSArch string + +const ( + LINUX = TOSType("Linux") + WINDOWS = TOSType("Windows") + MACOS = TOSType("macOS") + FREEBSD = TOSType("FreeBSD") + SOLARIS = TOSType("Solaris") + VMWARE = TOSType("VMware") + + X86 = TOSArch("x86") + X86_64 = TOSArch("x86_64") +) + +type SOsInfo struct { + OsType TOSType + OsDistribution string + OsVersion string + OsArch TOSArch +} + +func asian(ver string, arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "Asianux Server", ver, arch} +} + +func centos(arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "CentOS", "4/5", arch} +} + +func macos(ver string, arch TOSArch) SOsInfo { + return SOsInfo{MACOS, "Mac OS", ver, arch} +} + +func debian(ver string, arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "Debian", ver, arch} + +} + +func fedora(ver string, arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "Fedora", ver, arch} +} + +func freebsd(arch TOSArch) SOsInfo { + return SOsInfo{FREEBSD, "FreeBSD", "", arch} +} + +func rhel(ver string, arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "RedHat Enterprise Linux", ver, arch} + +} + +func suse(ver string, arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "SuSE", ver, arch} +} + +func opensuse(arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "OpenSuSE", "?", arch} + +} + +func oracle(ver string, arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "Oracle", ver, arch} +} + +func linux(ver string, arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "Generic", ver, arch} +} + +func windows(dist string, arch TOSArch) SOsInfo { + return SOsInfo{WINDOWS, dist, "", arch} +} + +func solaris(ver string, arch TOSArch) SOsInfo { + return SOsInfo{SOLARIS, "Solaris", ver, arch} +} + +func turbo(arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "Turbo Linux", "?", arch} +} + +func ubuntu(arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "Ubuntu", "?", arch} +} + +func mandriva(arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "Mandriva", "?", arch} +} + +func mandrake(arch TOSArch) SOsInfo { + return SOsInfo{LINUX, "Mandrake", "?", arch} +} + +func vmware(ver string) SOsInfo { + return SOsInfo{VMWARE, "ESX", ver, X86_64} +} + +var ( + GuestOsInfo = map[string]SOsInfo{ + "asianux3_64Guest": asian("3", X86_64), + "asianux3Guest": asian("3", X86), + "asianux4_64Guest": asian("4", X86_64), + "asianux4Guest": asian("4", X86), + "centos64Guest": centos(X86_64), + "centosGuest": centos(X86), + "darwin10_64Guest": macos("10.6", X86_64), + "darwin10Guest": macos("10.6", X86), + "darwin11_64Guest": macos("10.7", X86_64), + "darwin11Guest": macos("10.7", X86), + "darwin12_64Guest": macos("10.8", X86_64), + "darwin13_64Guest": macos("10.9", X86_64), + "darwin64Guest": macos("10.5", X86_64), + "darwinGuest": macos("10.5", X86), + "debian4_64Guest": debian("4", X86_64), + "debian4Guest": debian("4", X86), + "debian5_64Guest": debian("5", X86_64), + "debian5Guest": debian("5", X86), + "debian6_64Guest": debian("6", X86_64), + "debian6Guest": debian("6", X86), + "debian7_64Guest": debian("7", X86_64), + "debian7Guest": debian("7", X86), + "fedora64Guest": fedora("?", X86_64), + "fedoraGuest": fedora("?", X86), + "freebsd64Guest": freebsd(X86_64), + "freebsdGuest": freebsd(X86), + "genericLinuxGuest": linux("?", X86), + "mandrakeGuest": mandrake(X86), + "mandriva64Guest": mandriva(X86_64), + "mandrivaGuest": mandriva(X86), + "opensuse64Guest": opensuse(X86_64), + "opensuseGuest": opensuse(X86), + "oracleLinux64Guest": oracle("4/5", X86_64), + "oracleLinuxGuest": oracle("4/5", X86), + "other24xLinux64Guest": linux("2.4", X86_64), + "other24xLinuxGuest": linux("2.4", X86), + "other26xLinux64Guest": linux("2.6", X86_64), + "other26xLinuxGuest": linux("2.6", X86), + "other3xLinux64Guest": linux("3.x", X86_64), + "other3xLinuxGuest": linux("3.x", X86_64), + "otherLinux64Guest": linux("2.2", X86_64), + "otherLinuxGuest": linux("2.2", X86), + "redhatGuest": rhel("2.1", X86), + "rhel2Guest": rhel("2", X86), + "rhel3_64Guest": rhel("3", X86_64), + "rhel3Guest": rhel("3", X86), + "rhel4_64Guest": rhel("4", X86_64), + "rhel4Guest": rhel("4", X86), + "rhel5_64Guest": rhel("5", X86_64), + "rhel5Guest": rhel("5", X86), + "rhel6_64Guest": rhel("6", X86_64), + "rhel6Guest": rhel("6", X86), + "rhel7_64Guest": rhel("7", X86_64), + "rhel7Guest": rhel("7", X86), + "sles10_64Guest": suse("10", X86_64), + "sles10Guest": suse("10", X86), + "sles11_64Guest": suse("11", X86_64), + "sles11Guest": suse("11", X86), + "sles12_64Guest": suse("12", X86_64), + "sles12Guest": suse("12", X86), + "sles64Guest": suse("9", X86_64), + "slesGuest": suse("9", X86), + "solaris10_64Guest": solaris("10", X86_64), + "solaris10Guest": solaris("10", X86), + "solaris11_64Guest": solaris("11", X86_64), + "solaris6Guest": solaris("6", X86), + "solaris7Guest": solaris("7", X86), + "solaris8Guest": solaris("8", X86), + "solaris9Guest": solaris("9", X86), + "suse64Guest": suse("?", X86_64), + "suseGuest": suse("?", X86), + "turboLinux64Guest": turbo(X86_64), + "turboLinuxGuest": turbo(X86), + "ubuntu64Guest": ubuntu(X86_64), + "ubuntuGuest": ubuntu(X86), + "vmkernel5Guest": vmware("5"), + "vmkernelGuest": vmware("4"), + "win2000AdvServGuest": windows("Windows 2000 Advanced Server", X86), + "win2000ProGuest": windows("Windows 2000 Professional", X86), + "win2000ServGuest": windows("Windows 2000 Server", X86), + "win31Guest": windows("Windows 3.1", X86), + "win95Guest": windows("Windows 95", X86), + "win98Guest": windows("Windows 98", X86), + "windows7_64Guest": windows("Windows 7", X86_64), + "windows7Guest": windows("Windows 7", X86_64), + "windows7Server64Guest": windows("Windows Server 2008 R2", X86_64), + "windows8_64Guest": windows("Windows 8", X86_64), + "windows8Guest": windows("Windows 8", X86), + "windows8Server64Guest": windows("Windows 8 Server", X86_64), + "windowsHyperVGuest": windows("Windows Hyper-V", X86_64), + "winLonghorn64Guest": windows("Windows Longhorn", X86_64), + "winLonghornGuest": windows("Windows Longhorn", X86), + "winMeGuest": windows("Windows Millenium Edition", X86), + "winNetBusinessGuest": windows("Windows Small Business Server 2003", X86), + "winNetDatacenter64Guest": windows("Windows Server 2003 Datacenter Edition", X86_64), + "winNetDatacenterGuest": windows("Windows Server 2003 Datacenter Edition", X86), + "winNetEnterprise64Guest": windows("Windows Server 2003 Enterprise Edition", X86_64), + "winNetEnterpriseGuest": windows("Windows Server 2003 Enterprise Edition", X86), + "winNetStandard64Guest": windows("Windows Server 2003 Standard Edition", X86_64), + "winNetStandardGuest": windows("Windows Server 2003 Standard Edition", X86), + "winNetWebGuest": windows("Windows Server 2003 Web Edition", X86), + "winNTGuest": windows("Windows NT 4", X86), + "winVista64Guest": windows("Windows Vista", X86_64), + "winVistaGuest": windows("Windows Vista", X86), + "winXPHomeGuest": windows("Windows XP Home Edition", X86), + "winXPPro64Guest": windows("Windows XP Professional Edition", X86_64), + "winXPProGuest": windows("Windows XP Professional", X86), + } +) diff --git a/pkg/util/esxi/host.go b/pkg/util/esxi/host.go index 2ab2bd5350..b387b8306e 100644 --- a/pkg/util/esxi/host.go +++ b/pkg/util/esxi/host.go @@ -1,36 +1,29 @@ package esxi import ( + "context" + "fmt" + "strings" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" "yunion.io/x/jsonutils" - - "github.com/vmware/govmomi/vim25/types" "yunion.io/x/log" + "yunion.io/x/pkg/util/netutils" + "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" - "yunion.io/x/pkg/util/netutils" ) -var HOST_SYSTEM_PROPS = []string{"name", "parent", "summary", "config", "hardware", "vm"} - -type SHostNicInfo struct { - Dev string - Driver string - Mac string - Index int - LinkUp bool - IpAddr string - Mtu int - NicType string -} +var HOST_SYSTEM_PROPS = []string{"name", "parent", "summary", "config", "hardware", "vm", "datastore"} type SHostStorageAdapterInfo struct { Device string Model string Driver string Pci string - Drivers []SHostStorageDriverInfo + Drivers []*SHostStorageDriverInfo Enclosure int } @@ -44,6 +37,7 @@ type SHostStorageDriverInfo struct { SSD bool Dev string Size int + Slot int } type SHostStorageEnclosureInfo struct { @@ -55,11 +49,25 @@ type SHostStorageEnclosureInfo struct { Status string } +type SHostStorageInfo struct { + Adapter int + Driver string + Index int + Model string + Rotate bool + Status string + Size int +} + type SHost struct { SManagedObject nicInfo []SHostNicInfo - storageInfo []SHostStorageAdapterInfo + storageInfo []SHostStorageInfo + + datastores []cloudprovider.ICloudStorage + + storageCache *SDatastoreImageCache vms []cloudprovider.ICloudVM } @@ -98,7 +106,16 @@ func (self *SHost) GetStatus() string { } func (self *SHost) Refresh() error { - return cloudprovider.ErrNotImplemented + base := self.SManagedObject + var moObj mo.HostSystem + err := self.manager.reference2Object(self.object.Reference(), HOST_SYSTEM_PROPS, &moObj) + if err != nil { + return err + } + base.object = &moObj + *self = SHost{} + self.SManagedObject = base + return nil } func (self *SHost) IsEmulated() bool { @@ -109,21 +126,23 @@ func (self *SHost) fetchVMs() error { if self.vms != nil { return nil } - var vms []mo.VirtualMachine - err := self.manager.references2Objects(self.getHostSystem().Vm, VIRTUAL_MACHINE_PROPS, &vms) - if err != nil { - return err - } dc, err := self.GetDatacenter() if err != nil { return err } - self.vms = make([]cloudprovider.ICloudVM, len(vms)) - for i := 0; i < len(vms); i += 1 { - self.vms[i] = NewVirtualMachine(self.manager, &vms[i], dc, self) + hostVms := self.getHostSystem().Vm + if len(hostVms) == 0 { + // log.Errorf("host VMs are nil!!!!!") + return nil } + + vms, err := dc.fetchVms(hostVms) + if err != nil { + return err + } + self.vms = vms return nil } @@ -155,11 +174,29 @@ func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) { } func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) { - return nil, cloudprovider.ErrNotImplemented + moHost := self.getHostSystem() + istorages := make([]cloudprovider.ICloudStorage, len(moHost.Datastore)) + for i := 0; i < len(moHost.Datastore); i += 1 { + storage, err := self.datacenter.GetIStorageByMoId(moRefId(moHost.Datastore[i])) + if err != nil { + return nil, err + } + istorages[i] = storage + } + return istorages, nil } func (self *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { - return nil, cloudprovider.ErrNotImplemented + istorages, err := self.GetIStorages() + if err != nil { + return nil, err + } + for i := 0; i < len(istorages); i += 1 { + if istorages[i].GetGlobalId() == id { + return istorages[i], nil + } + } + return nil, cloudprovider.ErrNotFound } func (self *SHost) GetEnabled() bool { @@ -221,7 +258,7 @@ func (self *SHost) fetchNicInfo() []SHostNicInfo { info.Dev = nic.Device info.Driver = nic.Driver info.Mac = netutils.FormatMacAddr(nic.Mac) - info.Index = i + info.Index = int8(i) info.LinkUp = false nicInfoList = append(nicInfoList, info) } @@ -295,58 +332,150 @@ func (self *SHost) GetMemSizeMB() int { return int(self.getHostSystem().Summary.Hardware.MemorySize / 1024 / 1024) } -/*func (self *SHost) fetchStorageInfo() { - adapterList := make([]SHostStorageAdapterInfo, 0) - driversTable := make(map[string]SHostStorageDriverInfo, 0) - enclosuresTable := make(map[string]SHostStorageEnclosureInfo, 0) +func (self *SHost) GetStorageInfo() []SHostStorageInfo { + if self.storageInfo == nil { + self.storageInfo = self.getStorageInfo() + } + return self.storageInfo +} + +func (self *SHost) getStorageInfo() []SHostStorageInfo { + diskSlots := make(map[int]SHostStorageInfo) + list := self.getStorages() + for i := 0; i < len(list); i += 1 { + for j := 0; j < len(list[i].Drivers); j += 1 { + drv := list[i].Drivers[j] + info := SHostStorageInfo{ + Adapter: 0, + Driver: "Linux", + Index: drv.Slot, + Model: strings.TrimSpace(fmt.Sprintf("%s %s", drv.Vendor, drv.Model)), + Rotate: !drv.SSD, + Status: drv.Status, + Size: drv.Size, + } + diskSlots[info.Index] = info + } + } + disks := make([]SHostStorageInfo, 0) + idx := 0 + for { + if info, ok := diskSlots[idx]; ok { + disks = append(disks, info) + idx += 1 + } else { + break + } + } + return disks +} + +func (self *SHost) getStorages() []*SHostStorageAdapterInfo { + adapterList := make([]*SHostStorageAdapterInfo, 0) + adapterTable := make(map[string]*SHostStorageAdapterInfo) + driversTable := make(map[string]*SHostStorageDriverInfo, 0) + enclosuresTable := make(map[string]*SHostStorageEnclosureInfo, 0) moHost := self.getHostSystem() - for i, ad := range moHost.Config.StorageDevice.HostBusAdapter { + for i := 0; i < len(moHost.Config.StorageDevice.HostBusAdapter); i += 1 { + ad := moHost.Config.StorageDevice.HostBusAdapter[i] adinfo := ad.GetHostHostBusAdapter() if adinfo == nil { - log.Errorf("Fail to GetHostHostBusAdapter") + log.Errorf("fail to GetHostHostBusAdapter") continue } info := SHostStorageAdapterInfo{} info.Device = adinfo.Device - info.Model = adinfo.Model + info.Model = strings.TrimSpace(adinfo.Model) info.Driver = adinfo.Driver info.Pci = adinfo.Pci - info.Drivers = make([]SHostStorageDriverInfo, 0) + info.Drivers = make([]*SHostStorageDriverInfo, 0) info.Enclosure = -1 - adapterList = append(adapterList, info) + + adapterTable[adinfo.Key] = &info + adapterList = append(adapterList, &info) } - for i, drv := range moHost.Config.StorageDevice.ScsiLun { + for i := 0; i < len(moHost.Config.StorageDevice.ScsiLun); i += 1 { + drv := moHost.Config.StorageDevice.ScsiLun[i] lunInfo := drv.GetScsiLun() if lunInfo == nil { log.Errorf("fail to GetScsiLun") continue } - if lunInfo.DeviceType == "disk" { - info := SHostStorageDriverInfo{} - info.CN = lunInfo.CanonicalName - info.Name = lunInfo.DisplayName - info.Model = lunInfo.Model - info.Vendor = lunInfo.Vendor - info.Revision = lunInfo.Revision - info.Status = lunInfo.OperationalState[0] - // info.SSD = lunInfo. - // info.Dev = - // info.Size = lunInfo.S - } else if lunInfo.DeviceType == "enclosure" { + if lunInfo.DeviceType == "disk" { + scsiDisk := drv.(*types.HostScsiDisk) + info := SHostStorageDriverInfo{} + info.CN = scsiDisk.CanonicalName + info.Name = scsiDisk.DisplayName + info.Model = strings.TrimSpace(scsiDisk.Model) + info.Vendor = strings.TrimSpace(scsiDisk.Vendor) + info.Revision = scsiDisk.Revision + info.Status = scsiDisk.OperationalState[0] + if scsiDisk.Ssd != nil && *scsiDisk.Ssd { + info.SSD = true + } + info.Dev = scsiDisk.DevicePath + info.Size = int(int64(scsiDisk.Capacity.BlockSize) * scsiDisk.Capacity.Block / 1024 / 1024) + + driversTable[scsiDisk.Key] = &info + } else if lunInfo.DeviceType == "enclosure" { + enclosuresTable[lunInfo.Key] = &SHostStorageEnclosureInfo{ + CN: lunInfo.CanonicalName, + Name: lunInfo.DisplayName, + Model: strings.TrimSpace(lunInfo.Model), + Vendor: strings.TrimSpace(lunInfo.Vendor), + Revision: lunInfo.Revision, + Status: lunInfo.OperationalState[0], + } } } + for i := 0; i < len(moHost.Config.StorageDevice.ScsiTopology.Adapter); i += 1 { + ad := moHost.Config.StorageDevice.ScsiTopology.Adapter[i] + adapter := adapterTable[ad.Adapter] + for j := 0; j < len(ad.Target); j += 1 { + t := ad.Target[j] + key := t.Lun[0].ScsiLun + if _, ok := enclosuresTable[key]; ok { + adapter.Enclosure = int(t.Target) + } else if _, ok := driversTable[key]; ok { + driver := driversTable[key] + driver.Slot = int(t.Target) + adapter.Drivers = append(adapter.Drivers, driver) + } + } + } + return adapterList } -*/ func (self *SHost) GetStorageSizeMB() int { - return 0 + size := 0 + storages := self.GetStorageInfo() + for i := 0; i < len(storages); i += 1 { + size += storages[i].Size + } + return size } func (self *SHost) GetStorageType() string { - return "" + ssd := 0 + rotate := 0 + storages := self.GetStorageInfo() + for i := 0; i < len(storages); i += 1 { + if storages[i].Rotate { + rotate += 1 + } else { + ssd += 1 + } + } + if ssd == 0 && rotate > 0 { + return models.DISK_TYPE_ROTATE + } else if ssd > 0 && rotate == 0 { + return models.DISK_TYPE_SSD + } else { + return models.DISK_TYPE_HYBRID + } } func (self *SHost) GetHostType() string { @@ -357,8 +486,79 @@ func (self *SHost) GetManagerId() string { return self.manager.providerId } +func (self *SHost) GetIsMaintenance() bool { + moHost := self.getHostSystem() + return moHost.Summary.Runtime.InMaintenanceMode +} + +func (self *SHost) GetVersion() string { + moHost := self.getHostSystem() + about := moHost.Summary.Config.Product + return fmt.Sprintf("%s-%s", about.Version, about.Build) +} + func (self *SHost) CreateVM(name string, imgId string, sysDiskSize int, cpu int, memMB int, vswitchId string, ipAddr string, desc string, passwd string, storageType string, diskSizes []int, publicKey string, secGrpId string, userData string) (cloudprovider.ICloudVM, error) { log.Debugf("CreateVM") return nil, cloudprovider.ErrNotImplemented } + +func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) { + nics := host.getNicInfo() + inics := make([]cloudprovider.ICloudHostNetInterface, len(nics)) + for i := 0; i < len(nics); i += 1 { + inics[i] = &nics[i] + } + return inics, nil +} + +func (host *SHost) getLocalStorageCache() (*SDatastoreImageCache, error) { + if host.storageCache == nil { + sc, err := host.newLocalStorageCache() + if err != nil { + return nil, err + } + host.storageCache = sc + } + return host.storageCache, nil +} + +func (host *SHost) newLocalStorageCache() (*SDatastoreImageCache, error) { + ctx := context.Background() + + istorages, err := host.GetIStorages() + if err != nil { + return nil, err + } + var cacheDs *SDatastore + var maxDs *SDatastore + var maxCapacity int + for i := 0; i < len(istorages); i += 1 { + ds := istorages[i].(*SDatastore) + if !ds.isLocalVMFS() { + continue + } + _, err := ds.CheckFile(ctx, IMAGE_CACHE_DIR_NAME) + if err != nil { + if err != cloudprovider.ErrNotFound { + return nil, err + } + if maxCapacity < ds.GetCapacityMB() { + maxCapacity = ds.GetCapacityMB() + maxDs = ds + } + } else { + cacheDs = ds + break + } + } + if cacheDs == nil { + // if no existing image cache dir found, use the one with maximal capacilty + cacheDs = maxDs + } + + return &SDatastoreImageCache{ + datastore: cacheDs, + host: host, + }, nil +} diff --git a/pkg/util/esxi/hostnic.go b/pkg/util/esxi/hostnic.go new file mode 100644 index 0000000000..242c7ae76b --- /dev/null +++ b/pkg/util/esxi/hostnic.go @@ -0,0 +1,44 @@ +package esxi + +type SHostNicInfo struct { + Dev string + Driver string + Mac string + Index int8 + LinkUp bool + IpAddr string + Mtu int16 + NicType string +} + +func (nic *SHostNicInfo) GetDevice() string { + return nic.Dev +} + +func (nic *SHostNicInfo) GetDriver() string { + return nic.Driver +} + +func (nic *SHostNicInfo) GetMac() string { + return nic.Mac +} + +func (nic *SHostNicInfo) GetIndex() int8 { + return nic.Index +} + +func (nic *SHostNicInfo) IsLinkUp() bool { + return nic.LinkUp +} + +func (nic *SHostNicInfo) GetIpAddr() string { + return nic.IpAddr +} + +func (nic *SHostNicInfo) GetMtu() int16 { + return nic.Mtu +} + +func (nic *SHostNicInfo) GetNicType() string { + return nic.NicType +} diff --git a/pkg/util/esxi/image.go b/pkg/util/esxi/image.go new file mode 100644 index 0000000000..378c2d2c5b --- /dev/null +++ b/pkg/util/esxi/image.go @@ -0,0 +1,71 @@ +package esxi + +import ( + "context" + "path" + + "github.com/vmware/govmomi/object" + + "strings" + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SImage struct { + cache *SDatastoreImageCache + filename string +} + +func (self *SImage) getDatacenter() *object.Datacenter { + return self.cache.datastore.datacenter.getDcObj() +} + +func (self *SImage) getFullFilename() string { + return self.cache.datastore.getPathString(self.filename) +} + +func (self *SImage) GetId() string { + idstr := path.Base(self.filename) + if strings.HasSuffix(idstr, ".vmdk") { + idstr = idstr[:len(idstr)-5] + } + return strings.ToLower(idstr) +} + +func (self *SImage) GetName() string { + return path.Base(self.filename) +} + +func (self *SImage) GetGlobalId() string { + return self.GetId() +} + +func (self *SImage) GetStatus() string { + dm := object.NewVirtualDiskManager(self.cache.datastore.manager.client.Client) + ctx := context.Background() + _, err := dm.QueryVirtualDiskInfo(ctx, self.getFullFilename(), self.getDatacenter(), true) + if err != nil { + return "saving" + } + return "active" +} + +func (self *SImage) Refresh() error { + return nil +} + +func (self *SImage) IsEmulated() bool { + return false +} + +func (self *SImage) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SImage) Delete(ctx context.Context) error { + return self.cache.datastore.DeleteVmdk(ctx, self.filename) +} + +func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache { + return self.cache +} diff --git a/pkg/util/esxi/manager.go b/pkg/util/esxi/manager.go index 678d8510e7..e3ef4f4252 100644 --- a/pkg/util/esxi/manager.go +++ b/pkg/util/esxi/manager.go @@ -27,6 +27,8 @@ const ( ) type SESXiClient struct { + cloudprovider.SFakeOnPremiseRegion + providerId string providerName string host string @@ -50,14 +52,18 @@ func NewESXiClient(providerId string, providerName string, host string, port int return cli, nil } -func (cli *SESXiClient) url() string { +func (cli *SESXiClient) getUrl() string { if cli.port == 443 || cli.port == 0 { - return fmt.Sprintf("https://%s/sdk", cli.host) + return fmt.Sprintf("https://%s", cli.host) } else { - return fmt.Sprintf("https://%s:%d/sdk", cli.host, cli.port) + return fmt.Sprintf("https://%s:%d", cli.host, cli.port) } } +func (cli *SESXiClient) url() string { + return fmt.Sprintf("%s/sdk", cli.getUrl()) +} + func (cli *SESXiClient) connect() error { u, err := url.Parse(cli.url()) if err != nil { @@ -101,7 +107,18 @@ func (cli *SESXiClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { } func (cli *SESXiClient) About() jsonutils.JSONObject { - return jsonutils.Marshal(&cli.client.ServiceContent.About) + about := jsonutils.Marshal(&cli.client.ServiceContent.About) + aboutDict := about.(*jsonutils.JSONDict) + aboutDict.Add(jsonutils.NewString(cli.getEndpointType()), "endpoint_type") + return aboutDict +} + +func (cli *SESXiClient) getEndpointType() string { + if cli.IsVCenter() { + return "VCenter" + } else { + return "ESXi" + } } func (cli *SESXiClient) GetUUID() string { @@ -118,6 +135,16 @@ func (cli *SESXiClient) fetchDatacenters() error { cli.datacenters = make([]*SDatacenter, len(dcs)) for i := 0; i < len(dcs); i += 1 { cli.datacenters[i] = newDatacenter(cli, &dcs[i]) + + err = cli.datacenters[i].scanHosts() + if err != nil { + return err + } + + err = cli.datacenters[i].scanDatastores() + if err != nil { + return err + } } return nil } @@ -151,37 +178,11 @@ func (cli *SESXiClient) scanMObjects(folder types.ManagedObjectReference, props return nil } -/* -func getStructFields(dst interface{}) []string { - dataValue := reflect.Indirect(reflect.ValueOf(dst)) - dataType := dataValue.Type() - if dataType.Kind() != reflect.Struct { - log.Warningf("GetStructFeilds for non-struct data") - return nil - } - return _getStructFields(dataType) -} - -func _getStructFields(dataType reflect.Type) []string { - ret := make([]string, 0) - for i := 1; i < dataType.NumField(); i += 1 { - field := dataType.Field(i) - if field.Type.Kind() == reflect.Struct && field.Anonymous { - subfields := _getStructFields(field.Type) - ret = append(ret, subfields...) - } else if gotypes.IsFieldExportable(field.Name) { - log.Debugf("%s: %s", field.Name, field.Type.Name()) - ret = append(ret, utils.CamelSplit(field.Name, "_")) - } - } - return ret -} -*/ - func (cli *SESXiClient) references2Objects(refs []types.ManagedObjectReference, props []string, dst interface{}) error { pc := property.DefaultCollector(cli.client.Client) - err := pc.Retrieve(cli.context, refs, []string{"name", "config", "summary"}, dst) + err := pc.Retrieve(cli.context, refs, props, dst) if err != nil { + log.Errorf("pc.Retrieve fail %s", err) return err } return nil @@ -189,7 +190,12 @@ func (cli *SESXiClient) references2Objects(refs []types.ManagedObjectReference, func (cli *SESXiClient) reference2Object(ref types.ManagedObjectReference, props []string, dst interface{}) error { pc := property.DefaultCollector(cli.client.Client) - return pc.RetrieveOne(cli.context, ref, props, dst) + err := pc.RetrieveOne(cli.context, ref, props, dst) + if err != nil { + log.Errorf("pc.RetrieveOne fail %s", err) + return err + } + return nil } func (cli *SESXiClient) GetDatacenters() ([]*SDatacenter, error) { @@ -202,7 +208,7 @@ func (cli *SESXiClient) GetDatacenters() ([]*SDatacenter, error) { return cli.datacenters, nil } -func (cli *SESXiClient) FindDatacenterById(dcId string) (*SDatacenter, error) { +func (cli *SESXiClient) FindDatacenterByMoId(dcId string) (*SDatacenter, error) { dcs, err := cli.GetDatacenters() if err != nil { return nil, err @@ -215,8 +221,22 @@ func (cli *SESXiClient) FindDatacenterById(dcId string) (*SDatacenter, error) { return nil, cloudprovider.ErrNotFound } +func (cli *SESXiClient) FindHostByMoId(moId string) (cloudprovider.ICloudHost, error) { + dcs, err := cli.GetDatacenters() + if err != nil { + return nil, err + } + for i := 0; i < len(dcs); i += 1 { + ihost, err := dcs[i].GetIHostByMoId(moId) + if err == nil { + return ihost, nil + } + } + return nil, cloudprovider.ErrNotFound +} + func (cli *SESXiClient) getPrivateId(idStr string) string { - if strings.HasPrefix(idStr, cli.providerId) { + if len(cli.providerId) > 0 && strings.HasPrefix(idStr, cli.providerId) { idStr = idStr[len(cli.providerId)+1:] } return idStr @@ -224,12 +244,17 @@ func (cli *SESXiClient) getPrivateId(idStr string) string { func (cli *SESXiClient) FindHostByIp(hostIp string) (*SHost, error) { searchIndex := object.NewSearchIndex(cli.client.Client) + hostRef, err := searchIndex.FindByIp(cli.context, nil, cli.getPrivateId(hostIp), false) if err != nil { log.Errorf("searchIndex.FindByIp fail %s", err) return nil, err } + if hostRef == nil { + return nil, fmt.Errorf("cannot find %s", cli.getPrivateId(hostIp)) + } + var host mo.HostSystem err = cli.reference2Object(hostRef.Reference(), HOST_SYSTEM_PROPS, &host) if err != nil { @@ -244,3 +269,11 @@ func (cli *SESXiClient) acquireCloneTicket() (string, error) { manager := session.NewManager(cli.client.Client) return manager.AcquireCloneTicket(cli.context) } + +func (cli *SESXiClient) IsVCenter() bool { + return cli.client.Client.IsVC() +} + +func (cli *SESXiClient) IsValid() bool { + return cli.client.Client.Valid() +} diff --git a/pkg/util/esxi/mobase.go b/pkg/util/esxi/mobase.go index c60498ebc5..fe8a71d0d6 100644 --- a/pkg/util/esxi/mobase.go +++ b/pkg/util/esxi/mobase.go @@ -1,7 +1,7 @@ package esxi import ( - "reflect" + "strings" "github.com/vmware/govmomi/vim25/mo" @@ -27,11 +27,11 @@ func (self *SManagedObject) GetName() string { } func (self *SManagedObject) GetId() string { - return self.object.Entity().Self.Value + return moRefId(self.object.Entity().Self) } func (self *SManagedObject) GetType() string { - return self.object.Entity().Self.Type + return moRefType(self.object.Entity().Self) } func (self *SManagedObject) getCurrentParentEntity() *mo.ManagedEntity { @@ -47,28 +47,12 @@ func (self *SManagedObject) getParentEntity(obj *mo.ManagedEntity) *mo.ManagedEn log.Errorf("%s", err) return nil } - log.Debugf("getParentEntity %s %s %s", entity.Self.Type, entity.Self.Value, entity.Name) + // log.Debugf("getParentEntity %s %s %s", entity.Self.Type, entity.Self.Value, entity.Name) return &entity } return nil } -func reverseArray(array interface{}) { - arrayValue := reflect.Indirect(reflect.ValueOf(array)) - if arrayValue.Kind() != reflect.Slice && arrayValue.Kind() != reflect.Array { - log.Errorf("reverse non array or slice") - return - } - tmp := reflect.Indirect(reflect.New(arrayValue.Type().Elem())) - for i, j := 0, arrayValue.Len()-1; i < j; i, j = i+1, j-1 { - tmpi := arrayValue.Index(i) - tmpj := arrayValue.Index(j) - tmp.Set(tmpi) - tmpi.Set(tmpj) - tmpj.Set(tmp) - } -} - func (self *SManagedObject) fetchPath() []string { path := make([]string, 0) obj := self.object.Entity() @@ -91,6 +75,7 @@ func (self *SManagedObject) findInParents(objType string) *mo.ManagedEntity { obj := self.object.Entity() for obj != nil && obj.Self.Type != objType { + // log.Debugf("find %s want %s", obj.Self.Type, objType) obj = self.getParentEntity(obj) } @@ -102,7 +87,7 @@ func (self *SManagedObject) fetchDatacenter() (*SDatacenter, error) { if me == nil { return nil, cloudprovider.ErrNotFound } - return self.manager.FindDatacenterById(me.Self.Value) + return self.manager.FindDatacenterByMoId(me.Self.Value) } func (self *SManagedObject) GetDatacenter() (*SDatacenter, error) { @@ -125,3 +110,15 @@ func (self *SManagedObject) GetDatacenterPath() []string { path := dc.GetPath() return path[1:] } + +func (self *SManagedObject) GetDatacenterPathString() string { + path := self.GetDatacenterPath() + if path != nil { + return strings.Join(path, "/") + } + return "" +} + +func (self *SManagedObject) getManagerUri() string { + return self.manager.getUrl() +} diff --git a/pkg/util/esxi/provider/provider.go b/pkg/util/esxi/provider/provider.go index 8b292054f9..f294f864f5 100644 --- a/pkg/util/esxi/provider/provider.go +++ b/pkg/util/esxi/provider/provider.go @@ -13,7 +13,6 @@ import ( ) type SESXiProviderFactory struct { - providerTable map[string]*SESXiProvider } func (self *SESXiProviderFactory) GetId() string { @@ -39,10 +38,6 @@ func parseHostPort(host string, defPort int) (string, int, error) { } func (self *SESXiProviderFactory) GetProvider(providerId, providerName, urlStr, account, secret string) (cloudprovider.ICloudProvider, error) { - provider, ok := self.providerTable[providerId] - if ok { - return provider, nil - } parts, err := url.Parse(urlStr) if err != nil { return nil, err @@ -56,14 +51,11 @@ func (self *SESXiProviderFactory) GetProvider(providerId, providerName, urlStr, if err != nil { return nil, err } - self.providerTable[providerId] = &SESXiProvider{client: client} - return self.providerTable[providerId], nil + return &SESXiProvider{client: client}, nil } func init() { - factory := SESXiProviderFactory{ - providerTable: make(map[string]*SESXiProvider), - } + factory := SESXiProviderFactory{} cloudprovider.RegisterFactory(&factory) } @@ -75,6 +67,10 @@ func (self *SESXiProvider) IsPublicCloud() bool { return false } +func (self *SESXiProvider) IsOnPremiseInfrastructure() bool { + return true +} + func (self *SESXiProvider) GetId() string { return esxi.CLOUD_PROVIDER_VMWARE } @@ -99,27 +95,10 @@ func (self *SESXiProvider) GetIRegionById(id string) (cloudprovider.ICloudRegion return nil, cloudprovider.ErrNotSupported } -func (self *SESXiProvider) GetIHostById(id string) (cloudprovider.ICloudHost, error) { - host, err := self.client.FindHostByIp(id) - if err != nil { - return nil, err - } else { - return host, nil - } -} - -func (self *SESXiProvider) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { - return nil, cloudprovider.ErrNotSupported -} - -func (self *SESXiProvider) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (self *SESXiProvider) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { - return nil, cloudprovider.ErrNotImplemented -} - func (self *SESXiProvider) GetBalance() (float64, error) { return 0.0, nil } + +func (self *SESXiProvider) GetOnPremiseIRegion() (cloudprovider.ICloudRegion, error) { + return self.client, nil +} diff --git a/pkg/util/esxi/shell/host.go b/pkg/util/esxi/shell/host.go index 2807ab0607..86deb3792d 100644 --- a/pkg/util/esxi/shell/host.go +++ b/pkg/util/esxi/shell/host.go @@ -10,7 +10,7 @@ func init() { DATACENTER string `help:"List hosts in datacenter"` } shellutils.R(&HostListOptions{}, "host-list", "List hosts in datacenter", func(cli *esxi.SESXiClient, args *HostListOptions) error { - dc, err := cli.FindDatacenterById(args.DATACENTER) + dc, err := cli.FindDatacenterByMoId(args.DATACENTER) if err != nil { return err } diff --git a/pkg/util/esxi/shell/store.go b/pkg/util/esxi/shell/store.go index 6dcd6e6a7f..068931368c 100644 --- a/pkg/util/esxi/shell/store.go +++ b/pkg/util/esxi/shell/store.go @@ -1,16 +1,32 @@ package shell import ( + "context" + "fmt" + "os" "yunion.io/x/onecloud/pkg/util/esxi" + "yunion.io/x/onecloud/pkg/util/printutils" "yunion.io/x/onecloud/pkg/util/shellutils" ) +func getDatastore(cli *esxi.SESXiClient, dcId string, dsId string) (*esxi.SDatastore, error) { + dc, err := cli.FindDatacenterByMoId(dcId) + if err != nil { + return nil, err + } + ds, err := dc.GetIStorageByMoId(dsId) + if err != nil { + return nil, err + } + return ds.(*esxi.SDatastore), nil +} + func init() { type DatastoreListOptions struct { DATACENTER string `help:"List datastores in datacenter"` } shellutils.R(&DatastoreListOptions{}, "ds-list", "List datastores in datacenter", func(cli *esxi.SESXiClient, args *DatastoreListOptions) error { - dc, err := cli.FindDatacenterById(args.DATACENTER) + dc, err := cli.FindDatacenterByMoId(args.DATACENTER) if err != nil { return err } @@ -21,4 +37,203 @@ func init() { printList(ds, nil) return nil }) + + type DatastoreShowOptions struct { + DATACENTER string `help:"Datacenter"` + DSID string `help:"Datastore ID""` + } + shellutils.R(&DatastoreShowOptions{}, "ds-show", "Show details of a datastore", func(cli *esxi.SESXiClient, args *DatastoreShowOptions) error { + ds, err := getDatastore(cli, args.DATACENTER, args.DSID) + if err != nil { + return err + } + printObject(ds) + return nil + }) + + shellutils.R(&DatastoreShowOptions{}, "ds-cache-show", "Show details of a datastore image cache", func(cli *esxi.SESXiClient, args *DatastoreShowOptions) error { + ds, err := getDatastore(cli, args.DATACENTER, args.DSID) + if err != nil { + return err + } + cache := ds.GetIStoragecache() + printObject(cache) + return nil + }) + + shellutils.R(&DatastoreShowOptions{}, "ds-cache-list", "Show image list of a datastore image cache", func(cli *esxi.SESXiClient, args *DatastoreShowOptions) error { + ds, err := getDatastore(cli, args.DATACENTER, args.DSID) + if err != nil { + return err + } + cache := ds.GetIStoragecache() + images, err := cache.GetIImages() + if err != nil { + return err + } + printList(images, []string{}) + return nil + }) + + type DatastoreListDirOptions struct { + DATACENTER string `help:"Datacenter"` + DSID string `help:"Datastore ID""` + DIR string `help:"directory"` + } + shellutils.R(&DatastoreListDirOptions{}, "ds-list-dir", "List directory of a datastore", func(cli *esxi.SESXiClient, args *DatastoreListDirOptions) error { + dsObj, err := getDatastore(cli, args.DATACENTER, args.DSID) + if err != nil { + return err + } + ctx := context.Background() + fileList, err := dsObj.ListDir(ctx, args.DIR) + if err != nil { + return err + } + printutils.PrintInterfaceList(fileList, 0, 0, 0, []string{"Name", "Date", "Size"}) + return nil + }) + + shellutils.R(&DatastoreListDirOptions{}, "ds-check-file", "Check file status in a datastore", func(cli *esxi.SESXiClient, args *DatastoreListDirOptions) error { + dsObj, err := getDatastore(cli, args.DATACENTER, args.DSID) + if err != nil { + return err + } + ctx := context.Background() + file, err := dsObj.CheckFile(ctx, args.DIR) + if err != nil { + return err + } + printutils.PrintInterfaceObject(file) + return nil + }) + + shellutils.R(&DatastoreListDirOptions{}, "ds-delete-file", "Delete file in a datastore", func(cli *esxi.SESXiClient, args *DatastoreListDirOptions) error { + dsObj, err := getDatastore(cli, args.DATACENTER, args.DSID) + if err != nil { + return err + } + ctx := context.Background() + err = dsObj.Delete(ctx, args.DIR) + if err != nil { + return err + } + fmt.Println("success") + return nil + }) + + shellutils.R(&DatastoreListDirOptions{}, "ds-check-vmdk", "Check vmdk file status in a datastore", func(cli *esxi.SESXiClient, args *DatastoreListDirOptions) error { + dsObj, err := getDatastore(cli, args.DATACENTER, args.DSID) + if err != nil { + return err + } + ctx := context.Background() + err = dsObj.CheckVmdk(ctx, args.DIR) + if err != nil { + return err + } + fmt.Println("valid") + return nil + }) + + shellutils.R(&DatastoreListDirOptions{}, "ds-delete-vmdk", "Delete vmdk file from a datastore", func(cli *esxi.SESXiClient, args *DatastoreListDirOptions) error { + dsObj, err := getDatastore(cli, args.DATACENTER, args.DSID) + if err != nil { + return err + } + ctx := context.Background() + err = dsObj.DeleteVmdk(ctx, args.DIR) + if err != nil { + return err + } + fmt.Println("success") + return nil + }) + + shellutils.R(&DatastoreListDirOptions{}, "ds-mkdir", "Delete vmdk directory from a datastore", func(cli *esxi.SESXiClient, args *DatastoreListDirOptions) error { + dsObj, err := getDatastore(cli, args.DATACENTER, args.DSID) + if err != nil { + return err + } + ctx := context.Background() + path, err := dsObj.MakeDir(ctx, args.DIR) + if err != nil { + return err + } + fmt.Println("Make dir success", path) + return nil + }) + + shellutils.R(&DatastoreListDirOptions{}, "ds-rmdir", "Remove vmdk directory from a datastore", func(cli *esxi.SESXiClient, args *DatastoreListDirOptions) error { + dsObj, err := getDatastore(cli, args.DATACENTER, args.DSID) + if err != nil { + return err + } + ctx := context.Background() + err = dsObj.RemoveDir(ctx, args.DIR) + if err != nil { + return err + } + fmt.Println("Remove dir success") + return nil + }) + + type DatastoreDownloadOptions struct { + DATACENTER string `help:"Datacenter"` + DSID string `help:"Datastore ID""` + DIR string `help:"directory"` + LOCAL string `help:"local file"` + } + shellutils.R(&DatastoreDownloadOptions{}, "ds-download", "Download file from a datastore", func(cli *esxi.SESXiClient, args *DatastoreDownloadOptions) error { + dc, err := cli.FindDatacenterByMoId(args.DATACENTER) + if err != nil { + return err + } + ds, err := dc.GetIStorageByMoId(args.DSID) + if err != nil { + return err + } + ctx := context.Background() + dsObj := ds.(*esxi.SDatastore) + + file, err := os.Create(args.LOCAL) + if err != nil { + return err + } + defer file.Close() + + err = dsObj.Download(ctx, args.DIR, file) + if err != nil { + return err + } + + return nil + }) + + shellutils.R(&DatastoreDownloadOptions{}, "ds-upload", "Upload local file to datastore", func(cli *esxi.SESXiClient, args *DatastoreDownloadOptions) error { + dc, err := cli.FindDatacenterByMoId(args.DATACENTER) + if err != nil { + return err + } + ds, err := dc.GetIStorageByMoId(args.DSID) + if err != nil { + return err + } + ctx := context.Background() + dsObj := ds.(*esxi.SDatastore) + + file, err := os.Open(args.LOCAL) + if err != nil { + return err + } + defer file.Close() + + err = dsObj.Upload(ctx, args.DIR, file) + if err != nil { + return err + } + + return nil + }) + } diff --git a/pkg/util/esxi/shell/virtualmachine.go b/pkg/util/esxi/shell/virtualmachine.go index e1c9519054..0313cad264 100644 --- a/pkg/util/esxi/shell/virtualmachine.go +++ b/pkg/util/esxi/shell/virtualmachine.go @@ -1,6 +1,8 @@ package shell import ( + "context" + "fmt" "yunion.io/x/onecloud/pkg/util/esxi" "yunion.io/x/onecloud/pkg/util/printutils" "yunion.io/x/onecloud/pkg/util/shellutils" @@ -40,6 +42,67 @@ func init() { return nil }) + shellutils.R(&VirtualMachineShowOptions{}, "vm-nics", "Show vm nics details", func(cli *esxi.SESXiClient, args *VirtualMachineShowOptions) error { + host, err := cli.FindHostByIp(args.HOSTIP) + if err != nil { + return err + } + vm, err := host.GetIVMById(args.VMID) + if err != nil { + return err + } + vmnics, err := vm.GetINics() + if err != nil { + return err + } + printList(vmnics, []string{}) + return nil + }) + + shellutils.R(&VirtualMachineShowOptions{}, "vm-disks", "Show vm disks details", func(cli *esxi.SESXiClient, args *VirtualMachineShowOptions) error { + host, err := cli.FindHostByIp(args.HOSTIP) + if err != nil { + return err + } + vm, err := host.GetIVMById(args.VMID) + if err != nil { + return err + } + vmdisks, err := vm.GetIDisks() + if err != nil { + return err + } + printList(vmdisks, []string{}) + return nil + }) + + type VirtualMachineDiskResizeOptions struct { + HOSTIP string `help:"host ip"` + VMID string `help:"virtual machine UUID"` + DISKIDX int `help:"disk index"` + SIZEGB int64 `help:"new size of disk"` + } + shellutils.R(&VirtualMachineDiskResizeOptions{}, "vm-disk-resize", "Resize a vm disk", func(cli *esxi.SESXiClient, args *VirtualMachineDiskResizeOptions) error { + host, err := cli.FindHostByIp(args.HOSTIP) + if err != nil { + return err + } + vm, err := host.GetIVMById(args.VMID) + if err != nil { + return err + } + vmdisks, err := vm.GetIDisks() + if err != nil { + return err + } + if args.DISKIDX < 0 || args.DISKIDX >= len(vmdisks) { + return fmt.Errorf("Out of index: %d", args.DISKIDX) + } + disk := vmdisks[args.DISKIDX] + ctx := context.Background() + return disk.Resize(ctx, args.SIZEGB*1024) + }) + shellutils.R(&VirtualMachineShowOptions{}, "vm-vnc", "Show vm VNC details", func(cli *esxi.SESXiClient, args *VirtualMachineShowOptions) error { host, err := cli.FindHostByIp(args.HOSTIP) if err != nil { @@ -56,4 +119,5 @@ func init() { printutils.PrintJSONObject(info) return nil }) + } diff --git a/pkg/util/esxi/storage.go b/pkg/util/esxi/storage.go index b64c57ce1f..34c786b937 100644 --- a/pkg/util/esxi/storage.go +++ b/pkg/util/esxi/storage.go @@ -3,15 +3,36 @@ package esxi import ( "github.com/vmware/govmomi/vim25/mo" + "context" + "fmt" + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/types" + "io" + "io/ioutil" + "net/http" + "net/url" + "path" + "regexp" + "strconv" + "strings" + "time" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/util/vmdkutils" ) -var DATASTORE_PROPS = []string{"name", "parent"} +var DATASTORE_PROPS = []string{"name", "parent", "info", "summary", "host", "vm"} type SDatastore struct { SManagedObject + + // vms []cloudprovider.ICloudVM + + ihosts []cloudprovider.ICloudHost + + storageCache *SDatastoreImageCache } func NewDatastore(manager *SESXiClient, ds *mo.Datastore, dc *SDatacenter) *SDatastore { @@ -27,7 +48,28 @@ func (self *SDatastore) getDatastore() *mo.Datastore { } func (self *SDatastore) GetGlobalId() string { - return "" + volId, err := self.getVolumeId() + if err != nil { + log.Fatalf("datastore global ID error %s", err) + } + return volId +} + +func (self *SDatastore) GetName() string { + volName, err := self.getVolumeName() + if err != nil { + log.Fatalf("datastore get name error %s", err) + } + return fmt.Sprintf("%s-%s", self.getVolumeType(), volName) +} + +func (self *SDatastore) GetCapacityMB() int { + moStore := self.getDatastore() + return int(moStore.Summary.Capacity / 1024 / 1024) +} + +func (self *SDatastore) GetEnabled() bool { + return true } func (self *SDatastore) GetStatus() string { @@ -39,58 +81,587 @@ func (self *SDatastore) GetStatus() string { } func (self *SDatastore) Refresh() error { - return cloudprovider.ErrNotImplemented + base := self.SManagedObject + var moObj mo.Datastore + err := self.manager.reference2Object(self.object.Reference(), DATASTORE_PROPS, &moObj) + if err != nil { + return err + } + base.object = &moObj + *self = SDatastore{} + self.SManagedObject = base + return nil } func (self *SDatastore) IsEmulated() bool { return false } -func (self *SDatastore) getVolumeId() string { +func (self *SDatastore) getVolumeId() (string, error) { + moStore := self.getDatastore() + vmfsInfo, ok := moStore.Info.(*types.VmfsDatastoreInfo) + if ok { + if vmfsInfo.Vmfs.Local != nil && *vmfsInfo.Vmfs.Local { + host, err := self.getLocalHost() + if err != nil { + return "", err + } + return fmt.Sprintf("%s:%s", host.GetAccessIp(), vmfsInfo.Vmfs.Uuid), nil + } else { + return vmfsInfo.Vmfs.Uuid, nil + } + } + nasInfo, ok := moStore.Info.(*types.NasDatastoreInfo) + if ok { + return fmt.Sprintf("%s:%s", nasInfo.Nas.RemoteHost, nasInfo.Nas.RemotePath), nil + } + if moStore.Summary.Type == "vsan" { + vsanId := moStore.Summary.Url + vsanId = vsanId[strings.Index(vsanId, "vsan:"):] + + endIdx := len(vsanId) + for ; vsanId[endIdx-1] == '/'; endIdx -= 1 { + } + + return vsanId[:endIdx], nil + } + log.Fatalf("unsupported volume type %#v", moStore.Info) + return "", cloudprovider.ErrNotImplemented +} + +func (self *SDatastore) getVolumeType() string { return self.getDatastore().Summary.Type } +func (self *SDatastore) getVolumeName() (string, error) { + moStore := self.getDatastore() + + if self.isLocalVMFS() { + host, err := self.getLocalHost() + if err != nil { + return "", err + } + return fmt.Sprintf("%s-%s", host.GetAccessIp(), moStore.Info.GetDatastoreInfo().Name), nil + } + dc, err := self.GetDatacenter() + if err != nil { + return "", nil + } + return fmt.Sprintf("%s-%s", dc.GetName(), moStore.Info.GetDatastoreInfo().Name), nil +} + +func (self *SDatastore) getAttachedHosts() ([]cloudprovider.ICloudHost, error) { + ihosts := make([]cloudprovider.ICloudHost, 0) + + moStore := self.getDatastore() + for i := 0; i < len(moStore.Host); i += 1 { + idstr := moRefId(moStore.Host[i].Key) + host, err := self.datacenter.GetIHostByMoId(idstr) + if err != nil { + return nil, err + } + ihosts = append(ihosts, host) + } + + return ihosts, nil +} + +func (self *SDatastore) getCachedAttachedHosts() ([]cloudprovider.ICloudHost, error) { + if self.ihosts == nil { + var err error + self.ihosts, err = self.getAttachedHosts() + if err != nil { + return nil, err + } + } + return self.ihosts, nil +} + +func (self *SDatastore) GetAttachedHosts() ([]cloudprovider.ICloudHost, error) { + return self.getCachedAttachedHosts() +} + +func (self *SDatastore) getLocalHost() (cloudprovider.ICloudHost, error) { + hosts, err := self.GetAttachedHosts() + if err != nil { + return nil, err + } + if len(hosts) == 1 { + return hosts[0], nil + } + return nil, cloudprovider.ErrInvalidStatus +} + func (self *SDatastore) GetIStoragecache() cloudprovider.ICloudStoragecache { - return nil + if self.isLocalVMFS() { + ihost, err := self.getLocalHost() + if err != nil { + log.Errorf("GetIStoragecache getLocalHost fail %s", err) + return nil + } + host := ihost.(*SHost) + sc, err := host.getLocalStorageCache() + if err != nil { + log.Errorf("GetIStoragecache getLocalStorageCache fail %s", err) + return nil + } + return sc + } else { + return self.getStorageCache() + } +} + +func (self *SDatastore) getStorageCache() *SDatastoreImageCache { + if self.storageCache == nil { + self.storageCache = &SDatastoreImageCache{ + datastore: self, + } + } + return self.storageCache } func (self *SDatastore) GetIZone() cloudprovider.ICloudZone { return nil } -func (self *SDatastore) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) { - return nil, cloudprovider.ErrNotImplemented +func (self *SDatastore) getVMs() ([]cloudprovider.ICloudVM, error) { + dc, err := self.GetDatacenter() + if err != nil { + log.Errorf("SDatastore GetDatacenter fail %s", err) + return nil, err + } + vms := self.getDatastore().Vm + if len(vms) == 0 { + return nil, nil + } + return dc.fetchVms(vms) +} + +func (self *SDatastore) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) { + vms, err := self.getVMs() + if err != nil { + log.Errorf("self.getVMs fail %s", err) + return nil, err + } + for i := 0; i < len(vms); i += 1 { + vm := vms[i].(*SVirtualMachine) + disk, err := vm.GetIDiskById(idStr) + if err == nil { + return disk, nil + } + } + return nil, cloudprovider.ErrNotFound } func (self *SDatastore) GetIDisks() ([]cloudprovider.ICloudDisk, error) { - return nil, cloudprovider.ErrNotImplemented + vms, err := self.getVMs() + if err != nil { + return nil, err + } + allDisks := make([]cloudprovider.ICloudDisk, 0) + for i := 0; i < len(vms); i += 1 { + disks, err := vms[i].GetIDisks() + if err != nil { + return nil, err + } + allDisks = append(allDisks, disks...) + } + return allDisks, nil +} + +func (self *SDatastore) isLocalVMFS() bool { + moStore := self.getDatastore() + vmfsInfo, ok := moStore.Info.(*types.VmfsDatastoreInfo) + if ok && vmfsInfo.Vmfs.Local != nil && *vmfsInfo.Vmfs.Local { + return true + } + return false } func (self *SDatastore) GetStorageType() string { - return self.getDatastore().Summary.Type + moStore := self.getDatastore() + switch moStore.Summary.Type { + case "VMFS": + if self.isLocalVMFS() { + return models.STORAGE_LOCAL + } else { + return models.STORAGE_NAS + } + case "NFS", "NFS41", "CIFS", "vsan": + return models.STORAGE_NAS + default: + log.Fatalf("unsupported datastore type %s", moStore.Summary.Type) + return "" + } } func (self *SDatastore) GetMediumType() string { - return "" -} - -func (self *SDatastore) GetCapacityMB() int { - return 0 + moStore := self.getDatastore() + vmfsInfo, ok := moStore.Info.(*types.VmfsDatastoreInfo) + if ok && vmfsInfo.Vmfs.Ssd != nil && *vmfsInfo.Vmfs.Ssd { + return models.DISK_TYPE_SSD + } + return models.DISK_TYPE_ROTATE } func (self *SDatastore) GetStorageConf() jsonutils.JSONObject { conf := jsonutils.NewDict() - return conf -} + conf.Add(jsonutils.NewString(self.GetName()), "name") + conf.Add(jsonutils.NewString(self.GetGlobalId()), "id") + conf.Add(jsonutils.NewString(self.GetDatacenterPathString()), "dc_path") + volId, err := self.getVolumeId() + if err != nil { + log.Errorf("getVaolumeId fail %s", err) + } + conf.Add(jsonutils.NewString(volId), "volume_id") -func (self *SDatastore) GetEnabled() bool { - return true + volType := self.getVolumeType() + conf.Add(jsonutils.NewString(volType), "volume_type") + + volName, err := self.getVolumeName() + if err != nil { + log.Errorf("getVaolumeName fail %s", err) + } + conf.Add(jsonutils.NewString(volName), "volume_name") + return conf } func (self *SDatastore) GetManagerId() string { return self.manager.providerId } +func (self *SDatastore) GetUrl() string { + return self.getDatastore().Info.GetDatastoreInfo().Url +} + +func (self *SDatastore) GetMountPoint() string { + return self.GetUrl() +} + +func (self *SDatastore) cleanPath(remotePath string) string { + dsName := fmt.Sprintf("[%s]", self.SManagedObject.GetName()) + dsUrl := self.GetUrl() + if strings.HasPrefix(remotePath, dsName) { + remotePath = remotePath[len(dsName):] + } else if strings.HasPrefix(remotePath, dsUrl) { + remotePath = remotePath[len(dsUrl):] + } + return strings.TrimSpace(remotePath) +} + +func pathEscape(path string) string { + segs := strings.Split(path, "/") + for i := 0; i < len(segs); i += 1 { + segs[i] = url.PathEscape(segs[i]) + } + return strings.Join(segs, "/") +} + +func (self *SDatastore) GetPathUrl(remotePath string) string { + remotePath = self.cleanPath(remotePath) + if len(remotePath) == 0 || remotePath[0] != '/' { + remotePath = fmt.Sprintf("/%s", remotePath) + } + httpUrl := fmt.Sprintf("%s/folder%s", self.getManagerUri(), pathEscape(remotePath)) + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(self.SManagedObject.GetName()), "dsName") + params.Add(jsonutils.NewString(self.GetDatacenterPathString()), "dcPath") + + return fmt.Sprintf("%s?%s", httpUrl, params.QueryString()) +} + +func (self *SDatastore) getPathString(path string) string { + for len(path) > 0 && path[0] == '/' { + path = path[1:] + } + return fmt.Sprintf("[%s] %s", self.SManagedObject.GetName(), path) +} + +func (self *SDatastore) getFullPath(remotePath string) string { + remotePath = self.cleanPath(remotePath) + return path.Join(self.GetUrl(), remotePath) +} + func (self *SDatastore) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) { return nil, cloudprovider.ErrNotImplemented } + +func (self *SDatastore) FileGetContent(ctx context.Context, remotePath string) ([]byte, error) { + url := self.GetPathUrl(remotePath) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + var bytes []byte + + err = self.manager.client.Do(ctx, req, func(resp *http.Response) error { + if resp.StatusCode >= 400 { + return fmt.Errorf("%s", resp.Status) + } + cont, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + bytes = cont + return nil + }) + + return bytes, err +} + +type SDatastoreFileInfo struct { + Url string + Name string + Date time.Time + FileType string + Size uint64 +} + +const ( + fileListPattern = `(?P[^<]+)<\/a>(?P[^<]+)(?P[^<]+)` + fileDateFormat = "02-Jan-2006 15:04" + fileDateFormat2 = "Mon, 2 Jan 2006 15:04:05 GMT" +) + +var ( + fileListRegexp = regexp.MustCompile(fileListPattern) +) + +func (self *SDatastore) ListDir(ctx context.Context, remotePath string) ([]SDatastoreFileInfo, error) { + listContent, err := self.FileGetContent(ctx, remotePath) + if err != nil { + return nil, err + } + ret := make([]SDatastoreFileInfo, 0) + matches := fileListRegexp.FindAllStringSubmatch(string(listContent), -1) + for r := 0; r < len(matches); r += 1 { + url := strings.TrimSpace(matches[r][1]) + name := strings.TrimSpace(matches[r][2]) + dateStr := strings.TrimSpace(matches[r][3]) + sizeStr := strings.TrimSpace(matches[r][4]) + var ftype string + var size uint64 + if sizeStr == "-" { + ftype = "dir" + size = 0 + } else { + ftype = "file" + size, _ = strconv.ParseUint(sizeStr, 10, 64) + } + date, err := time.Parse(fileDateFormat, dateStr) + if err != nil { + return nil, err + } + info := SDatastoreFileInfo{ + Url: url, + Name: name, + FileType: ftype, + Size: size, + Date: date, + } + ret = append(ret, info) + } + + return ret, nil +} + +func (self *SDatastore) CheckFile(ctx context.Context, remotePath string) (*SDatastoreFileInfo, error) { + url := self.GetPathUrl(remotePath) + + req, err := http.NewRequest("HEAD", url, nil) + if err != nil { + return nil, err + } + + var size uint64 + var date time.Time + + err = self.manager.client.Do(ctx, req, func(resp *http.Response) error { + if resp.StatusCode >= 400 { + if resp.StatusCode == 404 { + return cloudprovider.ErrNotFound + } + return fmt.Errorf("%s", resp.Status) + } + sizeStr := resp.Header.Get("Content-Length") + size, _ = strconv.ParseUint(sizeStr, 10, 64) + + dateStr := resp.Header.Get("Date") + date, _ = time.Parse(fileDateFormat2, dateStr) + return nil + }) + + if err != nil { + return nil, err + } + return &SDatastoreFileInfo{Date: date, Size: size}, nil +} + +func (self *SDatastore) Download(ctx context.Context, remotePath string, writer io.Writer) error { + url := self.GetPathUrl(remotePath) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + + err = self.manager.client.Do(ctx, req, func(resp *http.Response) error { + if resp.StatusCode >= 400 { + return fmt.Errorf("%s", resp.Status) + } + buffer := make([]byte, 4096) + for { + rn, re := resp.Body.Read(buffer) + if rn > 0 { + wo := 0 + for wo < rn { + wn, we := writer.Write(buffer[wo:rn]) + if we != nil { + return we + } + wo += wn + } + } + if re != nil { + if re != io.EOF { + return re + } else { + break + } + } + } + return nil + }) + + return err +} + +func (self *SDatastore) Upload(ctx context.Context, remotePath string, body io.Reader) error { + url := self.GetPathUrl(remotePath) + + req, err := http.NewRequest("PUT", url, body) + if err != nil { + return err + } + + err = self.manager.client.Do(ctx, req, func(resp *http.Response) error { + if resp.StatusCode >= 400 { + return fmt.Errorf("%s", resp.Status) + } + _, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + // log.Debugf("upload respose %s", buffer) + return nil + }) + + return err +} + +func (self *SDatastore) FilePutContent(ctx context.Context, remotePath string, content string) error { + return self.Upload(ctx, remotePath, strings.NewReader(content)) +} + +func (self *SDatastore) Delete(ctx context.Context, remotePath string) error { + url := self.GetPathUrl(remotePath) + + req, err := http.NewRequest("DELETE", url, nil) + if err != nil { + return err + } + + err = self.manager.client.Do(ctx, req, func(resp *http.Response) error { + if resp.StatusCode >= 400 { + return fmt.Errorf("%s", resp.Status) + } + _, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + // log.Debugf("delete respose %s", buffer) + return nil + }) + + return err +} + +func (self *SDatastore) DeleteVmdk(ctx context.Context, remotePath string) error { + info, err := self.CheckFile(ctx, remotePath) + if err != nil { + return err + } + if info.Size > 4096 { + return fmt.Errorf("not a valid vmdk file") + } + vmdkContent, err := self.FileGetContent(ctx, remotePath) + if err != nil { + return err + } + vmdkInfo, err := vmdkutils.Parse(string(vmdkContent)) + if err != nil { + return err + } + err = self.Delete(ctx, remotePath) + if err != nil { + return err + } + if len(vmdkInfo.ExtentFile) > 0 { + err = self.Delete(ctx, path.Join(path.Dir(remotePath), vmdkInfo.ExtentFile)) + if err != nil { + return err + } + } + return nil +} + +func (self *SDatastore) CheckVmdk(ctx context.Context, remotePath string) error { + dm := object.NewVirtualDiskManager(self.manager.client.Client) + defer dm.Destroy(ctx) + + dc, err := self.GetDatacenter() + if err != nil { + return err + } + + dcObj := dc.getObjectDatacenter() + + infoList, err := dm.QueryVirtualDiskInfo(ctx, self.getPathString(remotePath), dcObj, true) + if err != nil { + return err + } + + log.Debugf("%#v", infoList) + return nil +} + +func (self *SDatastore) getDatastoreObj() *object.Datastore { + return object.NewDatastore(self.manager.client.Client, self.getDatastore().Self) +} + +func (self *SDatastore) MakeDir(ctx context.Context, remotePath string) (string, error) { + dnm := object.NewDatastoreNamespaceManager(self.manager.client.Client) + + remotePath = self.cleanPath(remotePath) + + objDS := self.getDatastoreObj() + + return dnm.CreateDirectory(ctx, objDS, remotePath, "") +} + +func (self *SDatastore) RemoveDir(ctx context.Context, remotePath string) error { + dnm := object.NewDatastoreNamespaceManager(self.manager.client.Client) + + remotePath = self.getFullPath(remotePath) + + dc, err := self.GetDatacenter() + if err != nil { + return err + } + + dcObj := dc.getObjectDatacenter() + + return dnm.DeleteDirectory(ctx, dcObj, remotePath) +} diff --git a/pkg/util/esxi/storagecache.go b/pkg/util/esxi/storagecache.go new file mode 100644 index 0000000000..a2268b27a9 --- /dev/null +++ b/pkg/util/esxi/storagecache.go @@ -0,0 +1,132 @@ +package esxi + +import ( + "context" + "fmt" + "path" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/mcclient" +) + +const ( + IMAGE_CACHE_DIR_NAME = "image_cache" +) + +type SDatastoreImageCache struct { + datastore *SDatastore + host *SHost +} + +func (self *SDatastoreImageCache) GetId() string { + if self.host != nil { + return self.host.GetGlobalId() + } else { + return self.datastore.GetGlobalId() + } +} + +func (self *SDatastoreImageCache) GetName() string { + if self.host != nil { + return fmt.Sprintf("storage-cache-%s", self.host.GetName()) + } else { + return fmt.Sprintf("storage-cache-%s", self.datastore.GetName()) + } +} + +func (self *SDatastoreImageCache) GetGlobalId() string { + return self.GetId() +} + +func (self *SDatastoreImageCache) GetStatus() string { + return "available" +} + +func (self *SDatastoreImageCache) Refresh() error { + return nil +} + +func (self *SDatastoreImageCache) IsEmulated() bool { + return false +} + +func (self *SDatastoreImageCache) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SDatastoreImageCache) GetPath() string { + return path.Join(self.datastore.GetMountPoint(), IMAGE_CACHE_DIR_NAME) +} + +func (self *SDatastoreImageCache) GetIImages() ([]cloudprovider.ICloudImage, error) { + ctx := context.Background() + + files, err := self.datastore.ListDir(ctx, IMAGE_CACHE_DIR_NAME) + if err != nil { + log.Errorf("GetIImages ListDir fail %s", err) + return nil, err + } + + ret := make([]cloudprovider.ICloudImage, 0) + + validFilenames := make(map[string]bool) + + for i := 0; i < len(files); i += 1 { + filename := path.Join(IMAGE_CACHE_DIR_NAME, files[i].Name) + if err := self.datastore.CheckVmdk(ctx, filename); err != nil { + continue + } + image := SImage{ + cache: self, + filename: filename, + } + ret = append(ret, &image) + vmdkName := files[i].Name + vmdkExtName := fmt.Sprintf("%s-flat.vmdk", vmdkName[:len(vmdkName)-5]) + validFilenames[vmdkName] = true + validFilenames[vmdkExtName] = true + } + + log.Debugf("storage cache contains %#v", validFilenames) + // cleanup storage cache!!! + for i := 0; i < len(files); i += 1 { + if _, ok := validFilenames[files[i].Name]; !ok { + log.Debugf("delete invalid vmdk file %s!!!", files[i].Name) + self.datastore.Delete(ctx, path.Join(IMAGE_CACHE_DIR_NAME, files[i].Name)) + } + } + + return ret, nil +} + +func (self *SDatastoreImageCache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) { + images, err := self.GetIImages() + if err != nil { + return nil, err + } + for i := 0; i < len(images); i += 1 { + if images[i].GetGlobalId() == extId { + return images[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SDatastoreImageCache) GetManagerId() string { + return self.datastore.manager.providerId +} + +func (self *SDatastoreImageCache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SDatastoreImageCache) DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SDatastoreImageCache) UploadImage(userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist string, extId string, isForce bool) (string, error) { + return "", cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/esxi/utils.go b/pkg/util/esxi/utils.go new file mode 100644 index 0000000000..1cb913dd04 --- /dev/null +++ b/pkg/util/esxi/utils.go @@ -0,0 +1,83 @@ +package esxi + +import ( + "context" + "reflect" + + "github.com/vmware/govmomi/property" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/types" + + "yunion.io/x/log" +) + +func Properties(c *vim25.Client, ctx context.Context, r types.ManagedObjectReference, ps []string, dst interface{}) error { + return property.DefaultCollector(c).RetrieveOne(ctx, r, ps, dst) +} + +func moRefId(ref types.ManagedObjectReference) string { + return ref.Value +} + +func moRefType(ref types.ManagedObjectReference) string { + return ref.Type +} + +func FetchAnonymousFieldValue(val interface{}, target interface{}) bool { + return fetchAnonymousFieldValue(reflect.Indirect(reflect.ValueOf(val)), + reflect.Indirect(reflect.ValueOf(target))) +} + +func fetchAnonymousFieldValue(value reflect.Value, target reflect.Value) bool { + for i := 0; i < value.NumField(); i += 1 { + fieldValue := value.Field(i) + fieldStruct := value.Type().Field(i) + if fieldStruct.Anonymous && fieldStruct.Type.Kind() == reflect.Struct { + if fieldStruct.Type == target.Type() { + target.Set(fieldValue) + return true + } + succ := fetchAnonymousFieldValue(fieldValue, target) + if succ { + return true + } + } + } + return false +} + +func StructContains(type1 reflect.Type, type2 reflect.Type) bool { + if type1.Kind() != reflect.Struct || type2.Kind() != reflect.Struct { + log.Errorf("types should be struct!") + return false + } + if type1 == type2 { + return true + } + for i := 0; i < type1.NumField(); i += 1 { + field := type1.Field(i) + if field.Anonymous && field.Type.Kind() == reflect.Struct { + contains := StructContains(field.Type, type2) + if contains { + return true + } + } + } + return false +} + +func reverseArray(array interface{}) { + arrayValue := reflect.Indirect(reflect.ValueOf(array)) + if arrayValue.Kind() != reflect.Slice && arrayValue.Kind() != reflect.Array { + log.Errorf("reverse non array or slice") + return + } + tmp := reflect.Indirect(reflect.New(arrayValue.Type().Elem())) + for i, j := 0, arrayValue.Len()-1; i < j; i, j = i+1, j-1 { + tmpi := arrayValue.Index(i) + tmpj := arrayValue.Index(j) + tmp.Set(tmpi) + tmpi.Set(tmpj) + tmpj.Set(tmp) + } +} diff --git a/pkg/util/esxi/utils_test.go b/pkg/util/esxi/utils_test.go new file mode 100644 index 0000000000..93d0c83405 --- /dev/null +++ b/pkg/util/esxi/utils_test.go @@ -0,0 +1,31 @@ +package esxi + +import "testing" + +func TestInitValue(t *testing.T) { + type testStruct struct { + member1 int + member2 string + Member3 int + Member4 string + } + + dst := testStruct{} + + dst.member1 = 1 + dst.member2 = "2" + dst.Member3 = 3 + dst.Member4 = "4" + + pDst := &dst + t.Logf("%p %#v", pDst, pDst) + + *pDst = testStruct{} + + t.Logf("%p %#v", pDst, pDst) + + new := testStruct{} + if dst != new { + t.Errorf("dst != new") + } +} diff --git a/pkg/util/esxi/vdisk.go b/pkg/util/esxi/vdisk.go new file mode 100644 index 0000000000..0d0c63b97b --- /dev/null +++ b/pkg/util/esxi/vdisk.go @@ -0,0 +1,242 @@ +package esxi + +import ( + "context" + "fmt" + "path" + "strings" + "time" + + "github.com/vmware/govmomi/vim25/types" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type SVirtualDisk struct { + SVirtualDevice +} + +func NewVirtualDisk(vm *SVirtualMachine, dev types.BaseVirtualDevice, index int) SVirtualDisk { + return SVirtualDisk{ + NewVirtualDevice(vm, dev, index), + } +} + +func (disk *SVirtualDisk) getVirtualDisk() *types.VirtualDisk { + return disk.dev.(*types.VirtualDisk) +} + +func (disk *SVirtualDisk) getBackingInfo() *types.VirtualDiskFlatVer2BackingInfo { + backing := disk.getVirtualDisk().Backing + switch backing.(type) { + case *types.VirtualDiskFlatVer2BackingInfo: + return backing.(*types.VirtualDiskFlatVer2BackingInfo) + case *types.VirtualDeviceFileBackingInfo: + case *types.VirtualDiskFlatVer1BackingInfo: + case *types.VirtualDiskLocalPMemBackingInfo: + case *types.VirtualDiskRawDiskMappingVer1BackingInfo: + case *types.VirtualDiskSeSparseBackingInfo: + case *types.VirtualDiskSparseVer1BackingInfo: + case *types.VirtualDiskSparseVer2BackingInfo: + case *types.VirtualFloppyImageBackingInfo: + case *types.VirtualNVDIMMBackingInfo: + case *types.VirtualParallelPortFileBackingInfo: + case *types.VirtualSerialPortFileBackingInfo: + case *types.VirtualCdromIsoBackingInfo: + } + log.Fatalf("unsupported backing info %T", backing) + return nil +} + +func (disk *SVirtualDisk) GetId() string { + backing := disk.getBackingInfo() + return backing.Uuid +} + +func (disk *SVirtualDisk) GetName() string { + backing := disk.getBackingInfo() + return path.Base(backing.FileName) +} + +func (disk *SVirtualDisk) GetGlobalId() string { + return fmt.Sprintf("%s-%s", disk.vm.GetGlobalId(), disk.GetId()) +} + +func (disk *SVirtualDisk) GetStatus() string { + return models.DISK_READY +} + +func (disk *SVirtualDisk) Refresh() error { + return nil +} + +func (disk *SVirtualDisk) IsEmulated() bool { + return false +} + +func (disk *SVirtualDisk) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (disk *SVirtualDisk) GetDiskSizeMB() int { + capa := disk.getVirtualDisk().CapacityInBytes + if capa == 0 { + capa = disk.getVirtualDisk().CapacityInKB * 1024 + } + return int(capa / 1024 / 1024) +} + +func (disk *SVirtualDisk) GetAccessPath() string { + istore, err := disk.GetIStorage() + if err != nil { + log.Errorf("disk.GetIStorage fail %s", err) + return "" + } + ds := istore.(*SDatastore) + return ds.getFullPath(disk.getBackingInfo().FileName) +} + +func (disk *SVirtualDisk) GetDiskFormat() string { + return "vmdk" +} + +func (disk *SVirtualDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { + dsObj := disk.getBackingInfo().Datastore + dc, err := disk.vm.GetDatacenter() + if err != nil { + log.Errorf("fail to find datacenter %s", err) + return nil, err + } + istorage, err := dc.GetIStorageByMoId(moRefId(*dsObj)) + if err != nil { + return nil, err + } + return istorage, nil +} + +func (disk *SVirtualDisk) GetIsAutoDelete() bool { + return true +} + +func (disk *SVirtualDisk) GetTemplateId() string { + backing := disk.getBackingInfo() + if backing.Parent != nil { + return path.Base(backing.Parent.FileName) + } + return "" +} + +func (disk *SVirtualDisk) GetDiskType() string { + backing := disk.getBackingInfo() + if backing.Parent != nil { + return models.DISK_TYPE_SYS + } + return models.DISK_TYPE_DATA +} + +func (disk *SVirtualDisk) GetFsFormat() string { + return "" +} + +func (disk *SVirtualDisk) getDiskMode() string { + backing := disk.getBackingInfo() + return backing.DiskMode +} + +func (disk *SVirtualDisk) GetIsNonPersistent() bool { + return disk.getDiskMode() == "persistent" +} + +func (disk *SVirtualDisk) GetDriver() string { + controller := disk.vm.getVdev(disk.getControllerKey()) + name := controller.GetDriver() + name = strings.Replace(name, "controller", "", -1) + mapping := map[string]string{ + "ahci": "sata", + "parascsi": "pvscsi", + "buslogic": "scsi", + "lsilogic": "scsi", + "lsilogicsas": "scsi", + } + return mapping[name] +} + +func (disk *SVirtualDisk) GetCacheMode() string { + backing := disk.getBackingInfo() + if backing.WriteThrough != nil && *backing.WriteThrough { + return "writethrough" + } else { + return "none" + } +} + +func (disk *SVirtualDisk) GetMountpoint() string { + return "" +} + +func (disk *SVirtualDisk) Delete(ctx context.Context) error { + istorage, err := disk.GetIStorage() + if err != nil { + log.Errorf("disk.GetIStorage() fail %s", err) + return err + } + ds := istorage.(*SDatastore) + return ds.DeleteVmdk(ctx, disk.getBackingInfo().FileName) +} + +func (disk *SVirtualDisk) CreateISnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudSnapshot, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (disk *SVirtualDisk) GetISnapshot(idStr string) (cloudprovider.ICloudSnapshot, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (disk *SVirtualDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (disk *SVirtualDisk) Resize(ctx context.Context, newSizeMb int64) error { + ndisk := disk.getVirtualDisk() + ndisk.CapacityInKB = newSizeMb * 1024 + + devSpec := types.VirtualDeviceConfigSpec{} + devSpec.Device = ndisk + devSpec.Operation = types.VirtualDeviceConfigSpecOperationEdit + + spec := types.VirtualMachineConfigSpec{} + spec.DeviceChange = []types.BaseVirtualDeviceConfigSpec{&devSpec} + + vm := disk.vm.getVmObj() + + task, err := vm.Reconfigure(ctx, spec) + + if err != nil { + log.Errorf("vm.Reconfigure fail %s", err) + return err + } + + err = task.Wait(ctx) + if err != nil { + log.Errorf("task.Wait fail %s", err) + return err + } + + return err +} + +func (disk *SVirtualDisk) Reset(ctx context.Context, snapshotId string) error { + return cloudprovider.ErrNotImplemented +} + +func (disk *SVirtualDisk) GetBillingType() string { + return "" +} + +func (disk *SVirtualDisk) GetExpiredAt() time.Time { + return time.Time{} +} diff --git a/pkg/util/esxi/virtualmachine.go b/pkg/util/esxi/virtualmachine.go index e9dd20138a..acc7a54154 100644 --- a/pkg/util/esxi/virtualmachine.go +++ b/pkg/util/esxi/virtualmachine.go @@ -1,7 +1,9 @@ package esxi import ( + "context" "fmt" + "reflect" "time" "github.com/vmware/govmomi/object" @@ -10,20 +12,33 @@ import ( "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/pkg/util/netutils" + "yunion.io/x/pkg/util/regutils" + + "strings" ) -var VIRTUAL_MACHINE_PROPS = []string{"name", "parent", "runtime", "summary"} +var VIRTUAL_MACHINE_PROPS = []string{"name", "parent", "runtime", "summary", "config", "guest"} type SVirtualMachine struct { SManagedObject - host *SHost + vnics []SVirtualNIC + vdisks []SVirtualDisk + vga SVirtualVGA + cdroms []SVirtualCdrom + devs map[int32]SVirtualDevice + + guestIps map[string]string } -func NewVirtualMachine(manager *SESXiClient, vm *mo.VirtualMachine, dc *SDatacenter, host *SHost) *SVirtualMachine { - return &SVirtualMachine{SManagedObject: newManagedObject(manager, vm, dc), host: host} +func NewVirtualMachine(manager *SESXiClient, vm *mo.VirtualMachine, dc *SDatacenter) *SVirtualMachine { + svm := &SVirtualMachine{SManagedObject: newManagedObject(manager, vm, dc)} + svm.fetchHardwareInfo() + return svm } func (self *SVirtualMachine) GetMetadata() *jsonutils.JSONDict { @@ -57,30 +72,40 @@ func (self *SVirtualMachine) GetStatus() string { } func (self *SVirtualMachine) Refresh() error { - return cloudprovider.ErrNotImplemented + base := self.SManagedObject + var moObj mo.VirtualMachine + err := self.manager.reference2Object(self.object.Reference(), VIRTUAL_MACHINE_PROPS, &moObj) + if err != nil { + return err + } + base.object = &moObj + *self = SVirtualMachine{} + self.SManagedObject = base + self.fetchHardwareInfo() + return nil } func (self *SVirtualMachine) IsEmulated() bool { return false } -func (self *SVirtualMachine) DeployVM(name string, password string, publicKey string, deleteKeypair bool, description string) error { +func (self *SVirtualMachine) DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error { return cloudprovider.ErrNotImplemented } -func (self *SVirtualMachine) RebuildRoot(imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { +func (self *SVirtualMachine) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { return "", cloudprovider.ErrNotImplemented } -func (self *SVirtualMachine) UpdateVM(name string) error { +func (self *SVirtualMachine) UpdateVM(ctx context.Context, name string) error { return cloudprovider.ErrNotImplemented } -func (self *SVirtualMachine) DetachDisk(diskId string) error { +func (self *SVirtualMachine) DetachDisk(ctx context.Context, diskId string) error { return cloudprovider.ErrNotImplemented } -func (self *SVirtualMachine) AttachDisk(diskId string) error { +func (self *SVirtualMachine) AttachDisk(ctx context.Context, diskId string) error { return cloudprovider.ErrNotImplemented } @@ -89,19 +114,54 @@ func (self *SVirtualMachine) getUuid() string { } func (self *SVirtualMachine) GetCreateTime() time.Time { - return time.Time{} + moVM := self.getVirtualMachine() + ctm := moVM.Config.CreateDate + if ctm != nil { + return *ctm + } else { + return time.Time{} + } } func (self *SVirtualMachine) GetIHost() cloudprovider.ICloudHost { - return self.host + vm := self.getVmObj() + + hostsys, err := vm.HostSystem(self.manager.context) + if err != nil { + log.Errorf("fail to find host system for vm %s", err) + return nil + } + ihost, err := self.manager.FindHostByMoId(moRefId(hostsys.Reference())) + if err != nil { + log.Errorf("fail to find host %s for vm %s???", hostsys.Name(), self.GetName()) + return nil + } + return ihost } func (self *SVirtualMachine) GetIDisks() ([]cloudprovider.ICloudDisk, error) { - return nil, cloudprovider.ErrNotImplemented + idisks := make([]cloudprovider.ICloudDisk, len(self.vdisks)) + for i := 0; i < len(self.vdisks); i += 1 { + idisks[i] = &(self.vdisks[i]) + } + return idisks, nil +} + +func (self *SVirtualMachine) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) { + for i := 0; i < len(self.vdisks); i += 1 { + if self.vdisks[i].GetGlobalId() == idStr { + return &self.vdisks[i], nil + } + } + return nil, cloudprovider.ErrNotFound } func (self *SVirtualMachine) GetINics() ([]cloudprovider.ICloudNic, error) { - return nil, cloudprovider.ErrNotImplemented + inics := make([]cloudprovider.ICloudNic, len(self.vnics)) + for i := 0; i < len(self.vnics); i += 1 { + inics[i] = &(self.vnics[i]) + } + return inics, nil } func (self *SVirtualMachine) GetIEIP() (cloudprovider.ICloudEIP, error) { @@ -109,12 +169,10 @@ func (self *SVirtualMachine) GetIEIP() (cloudprovider.ICloudEIP, error) { } func (self *SVirtualMachine) GetVcpuCount() int8 { - // ret = self.obj.summary.config.numCpu return int8(self.getVirtualMachine().Summary.Config.NumCpu) } func (self *SVirtualMachine) GetVmemSizeMB() int { - // self.obj.summary.config.memorySizeMB return int(self.getVirtualMachine().Summary.Config.MemorySizeMB) } @@ -130,11 +188,57 @@ func (self *SVirtualMachine) GetVdi() string { return "vmrc" } +func (self *SVirtualMachine) GetGuestFamily() string { + moVM := self.getVirtualMachine() + return moVM.Config.AlternateGuestName +} + +func (self *SVirtualMachine) GetGuestId() string { + moVM := self.getVirtualMachine() + return moVM.Config.GuestId +} + +func (self *SVirtualMachine) GetGuestFullName() string { + moVM := self.getVirtualMachine() + return moVM.Config.GuestFullName +} + +func (self *SVirtualMachine) GetGuestState() string { + moVM := self.getVirtualMachine() + return moVM.Guest.GuestState +} + +func (self *SVirtualMachine) GetGuestToolsStatus() string { + moVM := self.getVirtualMachine() + return string(moVM.Guest.ToolsStatus) +} + +func (self *SVirtualMachine) isToolsOk() bool { + switch self.getVirtualMachine().Guest.ToolsStatus { + case types.VirtualMachineToolsStatusToolsNotInstalled: + return false + case types.VirtualMachineToolsStatusToolsNotRunning: + return false + } + return true +} + +func (self *SVirtualMachine) GetGuestToolsRunningStatus() string { + moVM := self.getVirtualMachine() + return string(moVM.Guest.ToolsRunningStatus) +} + func (self *SVirtualMachine) GetOSType() string { + if osInfo, ok := GuestOsInfo[self.GetGuestId()]; ok { + return string(osInfo.OsType) + } return "" } func (self *SVirtualMachine) GetOSName() string { + if osInfo, ok := GuestOsInfo[self.GetGuestId()]; ok { + return string(osInfo.OsDistribution) + } return "" } @@ -158,18 +262,142 @@ func (self *SVirtualMachine) GetHypervisor() string { return models.HYPERVISOR_ESXI } -// GetSecurityGroup() ICloudSecurityGroup - -func (self *SVirtualMachine) StartVM() error { - return cloudprovider.ErrNotImplemented +func (self *SVirtualMachine) getVmObj() *object.VirtualMachine { + return object.NewVirtualMachine(self.manager.client.Client, self.getVirtualMachine().Self) } -func (self *SVirtualMachine) StopVM(isForce bool) error { - return cloudprovider.ErrNotImplemented +// ideopotent start +func (self *SVirtualMachine) StartVM(ctx context.Context) error { + if self.GetStatus() == models.VM_RUNNING { + return nil + } + return self.startVM(ctx) } -func (self *SVirtualMachine) DeleteVM() error { - return cloudprovider.ErrNotImplemented +func (self *SVirtualMachine) startVM(ctx context.Context) error { + err := self.makeNicsStartConnected(ctx) + if err != nil { + return err + } + + vm := self.getVmObj() + + task, err := vm.PowerOn(ctx) + if err != nil { + return err + } + return task.Wait(ctx) +} + +func (self *SVirtualMachine) makeNicsStartConnected(ctx context.Context) error { + spec := types.VirtualMachineConfigSpec{} + spec.DeviceChange = make([]types.BaseVirtualDeviceConfigSpec, len(self.vnics)) + for i := 0; i < len(self.vnics); i += 1 { + spec.DeviceChange[i] = makeNicStartConnected(&self.vnics[i]) + } + + vm := self.getVmObj() + + task, err := vm.Reconfigure(ctx, spec) + if err != nil { + return err + } + return task.Wait(ctx) +} + +func makeNicStartConnected(nic *SVirtualNIC) *types.VirtualDeviceConfigSpec { + editSpec := types.VirtualDeviceConfigSpec{} + editSpec.Operation = types.VirtualDeviceConfigSpecOperationEdit + editSpec.FileOperation = "" + editSpec.Device = nic.dev + editSpec.Device.GetVirtualDevice().Connectable.StartConnected = true + return &editSpec +} + +func (self *SVirtualMachine) StopVM(ctx context.Context, isForce bool) error { + if self.GetStatus() == models.VM_READY { + return nil + } + if !isForce && self.isToolsOk() { + return self.shutdownVM(ctx) + } else { + return self.poweroffVM(ctx) + } +} + +func (self *SVirtualMachine) poweroffVM(ctx context.Context) error { + vm := self.getVmObj() + + task, err := vm.PowerOff(ctx) + if err != nil { + return err + } + return task.Wait(ctx) +} + +func (self *SVirtualMachine) shutdownVM(ctx context.Context) error { + vm := self.getVmObj() + + err := vm.ShutdownGuest(ctx) + if err != nil { + return err + } + return err +} + +func (self *SVirtualMachine) doDelete(ctx context.Context) error { + vm := self.getVmObj() + + task, err := vm.Destroy(ctx) + if err != nil { + log.Errorf("vm.Destroy(ctx) fail %s", err) + return err + } + return task.Wait(ctx) +} + +func (self *SVirtualMachine) DeleteVM(ctx context.Context) error { + for i := 0; i < len(self.vdisks); i += 1 { + err := self.doDetachAndDeleteDisk(ctx, &self.vdisks[i]) + if err != nil { + log.Errorf("self.doDetachAndDeleteDisk(ctx, &self.vdisks[i]) fail %s", err) + return err + } + } + return self.doDelete(ctx) +} + +func (self *SVirtualMachine) doDetachAndDeleteDisk(ctx context.Context, vdisk *SVirtualDisk) error { + return self.doDetachDisk(ctx, vdisk, true) +} + +func (self *SVirtualMachine) doDetachDisk(ctx context.Context, vdisk *SVirtualDisk, remove bool) error { + removeSpec := types.VirtualDeviceConfigSpec{} + removeSpec.Operation = types.VirtualDeviceConfigSpecOperationRemove + removeSpec.Device = vdisk.dev + + spec := types.VirtualMachineConfigSpec{} + spec.DeviceChange = []types.BaseVirtualDeviceConfigSpec{&removeSpec} + + vm := self.getVmObj() + + task, err := vm.Reconfigure(ctx, spec) + if err != nil { + log.Errorf("vm.Reconfigure fail %s", err) + return err + } + + err = task.Wait(ctx) + if err != nil { + log.Errorf("task.Wait(ctx) fail %s", err) + return err + } + + if !remove { + return nil + } + + return vdisk.Delete(ctx) } func (self *SVirtualMachine) GetVNCInfo() (jsonutils.JSONObject, error) { @@ -229,8 +457,48 @@ func (self *SVirtualMachine) acquireVmrcUrl() (jsonutils.JSONObject, error) { return ret, nil } -func (dc *SVirtualMachine) ChangeConfig(instanceId string, ncpu int, vmem int) error { - return cloudprovider.ErrNotImplemented +func (self *SVirtualMachine) ChangeConfig(ctx context.Context, ncpu int, vmem int) error { + return self.doChangeConfig(ctx, int32(ncpu), int64(vmem), "", "") +} + +func (self *SVirtualMachine) GetVersion() string { + return self.getVirtualMachine().Config.Version +} + +func (self *SVirtualMachine) doChangeConfig(ctx context.Context, ncpu int32, vmemMB int64, guestId string, version string) error { + changed := false + configSpec := types.VirtualMachineConfigSpec{} + if int8(ncpu) != self.GetVcpuCount() { + configSpec.NumCPUs = ncpu + changed = true + } + if int(vmemMB) != self.GetVmemSizeMB() { + configSpec.MemoryMB = vmemMB + changed = true + } + if len(guestId) > 0 && guestId != self.GetGuestId() { + configSpec.GuestId = guestId + changed = true + } + if len(version) > 0 && version != self.GetVersion() { + configSpec.Version = version + changed = true + } + if !changed { + return nil + } + + vm := self.getVmObj() + + task, err := vm.Reconfigure(ctx, configSpec) + if err != nil { + return err + } + err = task.Wait(ctx) + if err != nil { + return err + } + return self.Refresh() } func (self *SVirtualMachine) AssignSecurityGroup(secgroupId string) error { @@ -248,3 +516,169 @@ func (self *SVirtualMachine) GetExpiredAt() time.Time { func (self *SVirtualMachine) UpdateUserData(userData string) error { return nil } + +func (self *SVirtualMachine) fetchHardwareInfo() { + self.vnics = make([]SVirtualNIC, 0) + self.vdisks = make([]SVirtualDisk, 0) + self.cdroms = make([]SVirtualCdrom, 0) + self.devs = make(map[int32]SVirtualDevice) + + moVM := self.getVirtualMachine() + + for i := 0; i < len(moVM.Config.Hardware.Device); i += 1 { + dev := moVM.Config.Hardware.Device[i] + devType := reflect.Indirect(reflect.ValueOf(dev)).Type() + + etherType := reflect.TypeOf((*types.VirtualEthernetCard)(nil)).Elem() + diskType := reflect.TypeOf((*types.VirtualDisk)(nil)).Elem() + vgaType := reflect.TypeOf((*types.VirtualMachineVideoCard)(nil)).Elem() + cdromType := reflect.TypeOf((*types.VirtualCdrom)(nil)).Elem() + + if StructContains(devType, etherType) { + self.vnics = append(self.vnics, NewVirtualNIC(self, dev, len(self.vnics))) + } else if StructContains(devType, diskType) { + self.vdisks = append(self.vdisks, NewVirtualDisk(self, dev, len(self.vnics))) + } else if StructContains(devType, vgaType) { + self.vga = NewVirtualVGA(self, dev, 0) + } else if StructContains(devType, cdromType) { + self.cdroms = append(self.cdroms, NewVirtualCdrom(self, dev, len(self.cdroms))) + } + vdev := NewVirtualDevice(self, dev, 0) + self.devs[vdev.getKey()] = vdev + } +} + +func (self *SVirtualMachine) getVdev(key int32) SVirtualDevice { + return self.devs[key] +} + +func (self *SVirtualMachine) fetchGuestIps() map[string]string { + guestIps := make(map[string]string) + moVM := self.getVirtualMachine() + for _, net := range moVM.Guest.Net { + mac := netutils.FormatMacAddr(net.MacAddress) + for _, ip := range net.IpAddress { + if regutils.MatchIP4Addr(ip) { + guestIps[mac] = ip + break + } + } + } + return guestIps +} + +func (self *SVirtualMachine) getGuestIps() map[string]string { + if self.guestIps == nil { + self.guestIps = self.fetchGuestIps() + } + return self.guestIps +} + +func (self *SVirtualMachine) GetIps() []string { + ips := make([]string, 0) + for _, ip := range self.getGuestIps() { + ips = append(ips, ip) + } + return ips +} + +func (self *SVirtualMachine) GetVGADevice() string { + return fmt.Sprintf("%s", self.vga.String()) +} + +var ( + driverTable = map[string][]string{ + "sata": {"ahci"}, + "scsi": {"lsilogic", "lsilogicsas", "buslogic"}, + "pvscsi": {"parascsi"}, + "ide": {"ide"}, + } +) + +func (self *SVirtualMachine) getDevsByDriver(driver string) []SVirtualDevice { + devs := make([]SVirtualDevice, 0) + for _, drv := range self.devs { + if strings.HasSuffix(drv.GetDriver(), fmt.Sprintf("%scontroller", driver)) { + devs = append(devs, drv) + } + } + return devs +} + +func minDevKey(devs []SVirtualDevice) int32 { + var minKey int32 = -1 + for i := 0; i < len(devs); i += 1 { + if minKey < 0 || minKey > devs[i].getKey() { + minKey = devs[i].getKey() + } + } + return minKey +} + +func minDiskKey(devs []SVirtualDisk) int32 { + var minKey int32 = -1 + for i := 0; i < len(devs); i += 1 { + if minKey < 0 || minKey > devs[i].getKey() { + minKey = devs[i].getKey() + } + } + return minKey +} + +func (self *SVirtualMachine) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error { + aliasDrivers, ok := driverTable[driver] + if !ok { + return fmt.Errorf("Unsupported disk driver %s", driver) + } + var devs []SVirtualDevice + for _, alias := range aliasDrivers { + devs = self.getDevsByDriver(alias) + if len(devs) > 0 { + break + } + } + if len(devs) == 0 { + return fmt.Errorf("Driver %s not found", driver) + } + ctlKey := minDevKey(devs) + sameDisks := make([]SVirtualDisk, 0) + for i := 0; i < len(self.vdisks); i += 1 { + if self.vdisks[i].GetDriver() == driver { + sameDisks = append(sameDisks, self.vdisks[i]) + } + } + var diskKey int32 = 2000 + if len(sameDisks) == 0 { + diskKey = minDiskKey(sameDisks) + } + index := len(sameDisks) + if driver == "ide" { + ctlKey += int32(index / 2) + } + devSpec := NewDiskDev(int64(sizeMb), "", uuid, int32(index), diskKey, ctlKey) + spec := addDevSpec(devSpec) + spec.FileOperation = types.VirtualDeviceConfigSpecFileOperationCreate + configSpec := types.VirtualMachineConfigSpec{} + configSpec.DeviceChange = []types.BaseVirtualDeviceConfigSpec{spec} + + vmObj := self.getVmObj() + + task, err := vmObj.Reconfigure(ctx, configSpec) + if err != nil { + return err + } + err = task.Wait(ctx) + if err != nil { + return err + } + oldDiskCnt := len(self.vdisks) + maxTries := 60 + for tried := 0; tried < maxTries; tried += 1 { + time.Sleep(time.Second) + self.Refresh() + if len(self.vdisks) > oldDiskCnt { + return nil + } + } + return cloudprovider.ErrTimeout +} diff --git a/pkg/util/esxi/vnic.go b/pkg/util/esxi/vnic.go new file mode 100644 index 0000000000..ae8196ea95 --- /dev/null +++ b/pkg/util/esxi/vnic.go @@ -0,0 +1,44 @@ +package esxi + +import ( + "github.com/vmware/govmomi/vim25/types" + + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/pkg/util/netutils" +) + +type SVirtualNIC struct { + SVirtualDevice +} + +func NewVirtualNIC(vm *SVirtualMachine, dev types.BaseVirtualDevice, index int) SVirtualNIC { + return SVirtualNIC{ + NewVirtualDevice(vm, dev, index), + } +} + +func (nic *SVirtualNIC) getVirtualEthernetCard() *types.VirtualEthernetCard { + card := types.VirtualEthernetCard{} + if FetchAnonymousFieldValue(nic.dev, &card) { + return &card + } + return nil +} + +func (nic *SVirtualNIC) GetIP() string { + guestIps := nic.vm.getGuestIps() + if ip, ok := guestIps[nic.GetMAC()]; ok { + return ip + } + log.Warningf("cannot find ip for mac %s", nic.GetMAC()) + return "" +} + +func (nic *SVirtualNIC) GetMAC() string { + return netutils.FormatMacAddr(nic.getVirtualEthernetCard().MacAddress) +} + +func (nic *SVirtualNIC) GetINetwork() cloudprovider.ICloudNetwork { + return nil +} diff --git a/pkg/util/printutils/printgetter.go b/pkg/util/printutils/printgetter.go index 1cca6b48c4..1765efd1ee 100644 --- a/pkg/util/printutils/printgetter.go +++ b/pkg/util/printutils/printgetter.go @@ -7,6 +7,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/utils" ) @@ -24,11 +25,18 @@ func getter2json(obj interface{}) jsonutils.JSONObject { methodName := method.Name methodType := methodValue.Type() - if strings.HasPrefix(methodName, "Get") && methodType.NumIn() == 0 && methodType.NumOut() == 1 { + if strings.HasPrefix(methodName, "Get") && methodType.NumIn() == 0 && methodType.NumOut() >= 1 { fieldName := utils.CamelSplit(methodName[3:], "_") out := methodValue.Call([]reflect.Value{}) - if len(out) == 1 { + if len(out) == 1 && !gotypes.IsNil(out[0].Interface()) { jsonDict.Add(jsonutils.Marshal(out[0].Interface()), fieldName) + } else if len(out) == 2 { + err, ok := out[1].Interface().(error) + if ok { + if err != nil && !gotypes.IsNil(out[0].Interface()) { + jsonDict.Add(jsonutils.Marshal(out[0].Interface()), fieldName) + } + } } } } diff --git a/pkg/util/qcloud/disk.go b/pkg/util/qcloud/disk.go index 61377bc936..1f3636074f 100644 --- a/pkg/util/qcloud/disk.go +++ b/pkg/util/qcloud/disk.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "context" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -150,11 +151,11 @@ func (self *SRegion) DeleteDisk(diskId string) error { return err } -func (self *SDisk) Delete() error { +func (self *SDisk) Delete(ctx context.Context) error { return self.storage.zone.region.DeleteDisk(self.DiskId) } -func (self *SRegion) ResizeDisk(diskId string, sizeGb int64) error { +func (self *SRegion) ResizeDisk(ctx context.Context, diskId string, sizeGb int64) error { params := make(map[string]string) params["DiskId"] = diskId params["DiskSize"] = fmt.Sprintf("%d", sizeGb) @@ -175,8 +176,8 @@ func (self *SRegion) ResizeDisk(diskId string, sizeGb int64) error { } } -func (self *SDisk) Resize(size int64) error { - return self.storage.zone.region.ResizeDisk(self.DiskId, size) +func (self *SDisk) Resize(ctx context.Context, sizeMb int64) error { + return self.storage.zone.region.ResizeDisk(ctx, self.DiskId, sizeMb/1024) } func (self *SDisk) GetName() string { @@ -194,8 +195,8 @@ func (self *SDisk) IsEmulated() bool { return false } -func (self *SDisk) GetIStorge() cloudprovider.ICloudStorage { - return self.storage +func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { + return self.storage, nil } func (self *SDisk) GetStatus() string { @@ -215,7 +216,7 @@ func (self *SDisk) Refresh() error { return jsonutils.Update(self, new) } -func (self *SDisk) CreateISnapshot(name, desc string) (cloudprovider.ICloudSnapshot, error) { +func (self *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) { snapshotId, err := self.storage.zone.region.CreateSnapshot(self.DiskId, name, desc) if err != nil { log.Errorf("createSnapshot fail %s", err) @@ -344,7 +345,7 @@ func (self *SRegion) ResetDisk(diskId, snapshotId string) error { return nil } -func (self *SDisk) Reset(snapshotId string) error { +func (self *SDisk) Reset(ctx context.Context, snapshotId string) error { return self.storage.zone.region.ResetDisk(self.DiskId, snapshotId) } @@ -373,3 +374,7 @@ func (self *SRegion) CreateDisk(zoneId string, category string, name string, siz } return diskIDSet[0], nil } + +func (disk *SDisk) GetAccessPath() string { + return "" +} diff --git a/pkg/util/qcloud/host.go b/pkg/util/qcloud/host.go index 67f362deb4..53ed257cb1 100644 --- a/pkg/util/qcloud/host.go +++ b/pkg/util/qcloud/host.go @@ -270,3 +270,15 @@ func (self *SHost) GetSysInfo() jsonutils.JSONObject { func (self *SHost) IsEmulated() bool { return true } + +func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (host *SHost) GetIsMaintenance() bool { + return false +} + +func (host *SHost) GetVersion() string { + return QCLOUD_API_VERSION +} diff --git a/pkg/util/qcloud/image.go b/pkg/util/qcloud/image.go index 2d481ab146..e368758ae9 100644 --- a/pkg/util/qcloud/image.go +++ b/pkg/util/qcloud/image.go @@ -5,6 +5,7 @@ import ( "strconv" "time" + "context" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -104,7 +105,7 @@ func (self *SImage) GetGlobalId() string { return fmt.Sprintf("%s-%s") } -func (self *SImage) Delete() error { +func (self *SImage) Delete(ctx context.Context) error { return self.storageCache.region.DeleteImage(self.ImageId) } diff --git a/pkg/util/qcloud/instance.go b/pkg/util/qcloud/instance.go index 294ad1fef1..497fe81c9c 100644 --- a/pkg/util/qcloud/instance.go +++ b/pkg/util/qcloud/instance.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "context" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -315,7 +316,7 @@ func (self *SInstance) GetHypervisor() string { return models.HYPERVISOR_QCLOUD } -func (self *SInstance) StartVM() error { +func (self *SInstance) StartVM(ctx context.Context) error { timeout := 300 * time.Second interval := 15 * time.Second @@ -340,7 +341,7 @@ func (self *SInstance) StartVM() error { return cloudprovider.ErrTimeout } -func (self *SInstance) StopVM(isForce bool) error { +func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { err := self.host.zone.region.StopVM(self.InstanceId, isForce) if err != nil { return err @@ -360,11 +361,11 @@ func (self *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) { return ret, nil } -func (self *SInstance) UpdateVM(name string) error { +func (self *SInstance) UpdateVM(ctx context.Context, name string) error { return self.host.zone.region.UpdateVM(self.InstanceId, name) } -func (self *SInstance) DeployVM(name string, password string, publicKey string, deleteKeypair bool, description string) error { +func (self *SInstance) DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error { var keypairName string if len(publicKey) > 0 { var err error @@ -377,7 +378,7 @@ func (self *SInstance) DeployVM(name string, password string, publicKey string, return self.host.zone.region.DeployVM(self.InstanceId, name, password, keypairName, deleteKeypair, description) } -func (self *SInstance) RebuildRoot(imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { +func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { keypair := "" if len(publicKey) > 0 { var err error @@ -390,7 +391,7 @@ func (self *SInstance) RebuildRoot(imageId string, passwd string, publicKey stri if err != nil { return "", err } - self.StopVM(true) + self.StopVM(ctx, true) instance, err := self.host.zone.region.GetInstance(self.InstanceId) if err != nil { return "", err @@ -398,15 +399,15 @@ func (self *SInstance) RebuildRoot(imageId string, passwd string, publicKey stri return instance.SystemDisk.DiskId, nil } -func (self *SInstance) ChangeConfig(instanceId string, ncpu int, vmem int) error { +func (self *SInstance) ChangeConfig(ctx context.Context, ncpu int, vmem int) error { return self.host.zone.region.ChangeVMConfig(self.Placement.Zone, self.InstanceId, ncpu, vmem, nil) } -func (self *SInstance) AttachDisk(diskId string) error { +func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error { return self.host.zone.region.AttachDisk(self.InstanceId, diskId) } -func (self *SInstance) DetachDisk(diskId string) error { +func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error { return self.host.zone.region.DetachDisk(self.InstanceId, diskId) } @@ -527,7 +528,7 @@ func (self *SRegion) StopVM(instanceId string, isForce bool) error { return self.doStopVM(instanceId, isForce) } -func (self *SRegion) DeleteVM(instanceId string) error { +func (self *SRegion) DeleteVM(ctx context.Context, instanceId string) error { status, err := self.GetInstanceStatus(instanceId) if err != nil { log.Errorf("Fail to get instance status on DeleteVM: %s", err) @@ -581,9 +582,9 @@ func (self *SRegion) DeployVM(instanceId string, name string, password string, k return nil } -func (self *SInstance) DeleteVM() error { +func (self *SInstance) DeleteVM(ctx context.Context) error { for { - err := self.host.zone.region.DeleteVM(self.InstanceId) + err := self.host.zone.region.DeleteVM(ctx, self.InstanceId) if err != nil { // if isError(err, "IncorrectInstanceStatus.Initializing") { // log.Infof("The instance is initializing, try later ...") @@ -712,3 +713,7 @@ func (self *SInstance) GetExpiredAt() time.Time { func (self *SInstance) UpdateUserData(userData string) error { return cloudprovider.ErrNotSupported } + +func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error { + return cloudprovider.ErrNotSupported +} diff --git a/pkg/util/qcloud/localdisk.go b/pkg/util/qcloud/localdisk.go index ad91c8a764..5d1e724d0d 100644 --- a/pkg/util/qcloud/localdisk.go +++ b/pkg/util/qcloud/localdisk.go @@ -3,6 +3,7 @@ package qcloud import ( "time" + "context" "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" @@ -20,11 +21,11 @@ func (self *SLocalDisk) GetMetadata() *jsonutils.JSONDict { return nil } -func (self *SLocalDisk) CreateISnapshot(name, desc string) (cloudprovider.ICloudSnapshot, error) { +func (self *SLocalDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) { return nil, cloudprovider.ErrNotSupported } -func (self *SLocalDisk) Delete() error { +func (self *SLocalDisk) Delete(ctx context.Context) error { return nil } @@ -83,7 +84,7 @@ func (self *SLocalDisk) Refresh() error { return nil } -func (self *SLocalDisk) Reset(snapshotId string) error { +func (self *SLocalDisk) Reset(ctx context.Context, snapshotId string) error { return cloudprovider.ErrNotSupported } @@ -119,10 +120,14 @@ func (self *SLocalDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) return nil, nil } -func (self *SLocalDisk) GetIStorge() cloudprovider.ICloudStorage { - return self.storage +func (self *SLocalDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { + return self.storage, nil } -func (self *SLocalDisk) Resize(size int64) error { +func (self *SLocalDisk) Resize(ctx context.Context, size int64) error { return cloudprovider.ErrNotSupported } + +func (disk *SLocalDisk) GetAccessPath() string { + return "" +} diff --git a/pkg/util/qcloud/localstorage.go b/pkg/util/qcloud/localstorage.go index 38d57186c1..5d12e21564 100644 --- a/pkg/util/qcloud/localstorage.go +++ b/pkg/util/qcloud/localstorage.go @@ -93,6 +93,10 @@ func (self *SLocalStorage) CreateIDisk(name string, sizeGb int, desc string) (cl return nil, cloudprovider.ErrNotSupported } -func (self *SLocalStorage) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) { +func (self *SLocalStorage) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) { return &SLocalDisk{storage: self, DiskId: idStr}, nil } + +func (self *SLocalStorage) GetMountPoint() string { + return "" +} diff --git a/pkg/util/qcloud/provider/provider.go b/pkg/util/qcloud/provider/provider.go index 206fb909bf..932b441855 100644 --- a/pkg/util/qcloud/provider/provider.go +++ b/pkg/util/qcloud/provider/provider.go @@ -35,6 +35,10 @@ func (self *SQcloudProvider) IsPublicCloud() bool { return true } +func (self *SQcloudProvider) IsOnPremiseInfrastructure() bool { + return false +} + func (self *SQcloudProvider) GetId() string { return qcloud.CLOUD_PROVIDER_QCLOUD } @@ -63,22 +67,10 @@ func (self *SQcloudProvider) GetIRegionById(id string) (cloudprovider.ICloudRegi return self.client.GetIRegionById(id) } -func (self *SQcloudProvider) GetIHostById(id string) (cloudprovider.ICloudHost, error) { - return self.client.GetIHostById(id) -} - -func (self *SQcloudProvider) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { - return self.client.GetIVpcById(id) -} - -func (self *SQcloudProvider) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { - return self.client.GetIStorageById(id) -} - -func (self *SQcloudProvider) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { - return self.client.GetIStoragecacheById(id) -} - func (self *SQcloudProvider) GetBalance() (float64, error) { return 0.0, nil } + +func (self *SQcloudProvider) GetOnPremiseIRegion() (cloudprovider.ICloudRegion, error) { + return nil, cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/qcloud/qcloud.go b/pkg/util/qcloud/qcloud.go index 02fb2fd061..492ab5ab6d 100644 --- a/pkg/util/qcloud/qcloud.go +++ b/pkg/util/qcloud/qcloud.go @@ -250,18 +250,6 @@ func (client *SQcloudClient) GetIStorageById(id string) (cloudprovider.ICloudSto return nil, cloudprovider.ErrNotFound } -func (client *SQcloudClient) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { - for i := 0; i < len(client.iregions); i++ { - ihost, err := client.iregions[i].GetIStoragecacheById(id) - if err == nil { - return ihost, nil - } else if err != cloudprovider.ErrNotFound { - return nil, err - } - } - return nil, cloudprovider.ErrNotFound -} - type SAccountBalance struct { AvailableAmount float64 AvailableCashAmount float64 diff --git a/pkg/util/qcloud/region.go b/pkg/util/qcloud/region.go index 86fc26df65..015e8a4ffa 100644 --- a/pkg/util/qcloud/region.go +++ b/pkg/util/qcloud/region.go @@ -156,6 +156,40 @@ func (self *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, er return nil, cloudprovider.ErrNotFound } +func (self *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) { + iHosts := make([]cloudprovider.ICloudHost, 0) + + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + iZoneHost, err := izones[i].GetIHosts() + if err != nil { + return nil, err + } + iHosts = append(iHosts, iZoneHost...) + } + return iHosts, nil +} + +func (self *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + iStores := make([]cloudprovider.ICloudStorage, 0) + + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + iZoneStores, err := izones[i].GetIStorages() + if err != nil { + return nil, err + } + iStores = append(iStores, iZoneStores...) + } + return iStores, nil +} + func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { storageCache := self.getStoragecache() if storageCache.GetGlobalId() == id { diff --git a/pkg/util/qcloud/storage.go b/pkg/util/qcloud/storage.go index a9ebca1bc4..065210f5df 100644 --- a/pkg/util/qcloud/storage.go +++ b/pkg/util/qcloud/storage.go @@ -121,7 +121,7 @@ func (self *SStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudpr return nil, cloudprovider.ErrNotFound } -func (self *SStorage) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) { +func (self *SStorage) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) { disk, err := self.zone.region.GetDisk(idStr) if err != nil { return nil, err @@ -129,3 +129,7 @@ func (self *SStorage) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) { disk.storage = self return disk, nil } + +func (self *SStorage) GetMountPoint() string { + return "" +} diff --git a/pkg/util/qcloud/storagecache.go b/pkg/util/qcloud/storagecache.go index f51281d605..c1d4e64571 100644 --- a/pkg/util/qcloud/storagecache.go +++ b/pkg/util/qcloud/storagecache.go @@ -109,6 +109,22 @@ func (self *SStoragecache) GetIImages() ([]cloudprovider.ICloudImage, error) { return self.iimages, nil } +func (self *SStoragecache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) { + parts, _, err := self.region.GetImages("", "PRIVATE_IMAGE", []string{extId}, "", 0, 1) + if err != nil { + return nil, err + } + if len(parts) == 0 { + return nil, cloudprovider.ErrNotFound + } + parts[1].storageCache = self + return &parts[0], nil +} + +func (self *SStoragecache) GetPath() string { + return "" +} + func (self *SStoragecache) UploadImage(userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist string, extId string, isForce bool) (string, error) { if len(extId) > 0 { log.Debugf("UploadImage: Image external ID exists %s", extId) diff --git a/pkg/util/vmdkutils/vmdkutils.go b/pkg/util/vmdkutils/vmdkutils.go new file mode 100644 index 0000000000..b08b45ecf9 --- /dev/null +++ b/pkg/util/vmdkutils/vmdkutils.go @@ -0,0 +1,79 @@ +package vmdkutils + +import ( + "bufio" + "io" + "regexp" + "strings" + + "fmt" + "strconv" + "yunion.io/x/pkg/utils" +) + +type SVMDKInfo struct { + ExtentFile string + Heads int64 + Sectors int64 + Cylinders int64 + CID string + LongCID string + UUID string + AdapterType string + VirtualHWVersion string +} + +const ( + extentPatternString = `^RW \d+ VMFS\w* \"(?P[^"]+)` +) + +var ( + extentPatternRegexp = regexp.MustCompile(extentPatternString) +) + +func Parse(content string) (*SVMDKInfo, error) { + return ParseStream(strings.NewReader(content)) +} + +func ParseStream(stream io.Reader) (*SVMDKInfo, error) { + info := SVMDKInfo{} + scanner := bufio.NewScanner(stream) + findExtent := false + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + matches := extentPatternRegexp.FindStringSubmatch(line) + if len(matches) > 0 { + // log.Debugf("%#v", matches) + info.ExtentFile = matches[1] + findExtent = true + } else { + equalPos := strings.IndexByte(line, '=') + if equalPos > 0 { + key := strings.TrimSpace(line[:equalPos]) + value := utils.Unquote(strings.TrimSpace(line[equalPos+1:])) + switch key { + case "CID": + info.CID = value + case "ddb.uuid": + info.UUID = value + case "ddb.geometry.cylinders": + info.Cylinders, _ = strconv.ParseInt(value, 10, 64) + case "ddb.geometry.heads": + info.Heads, _ = strconv.ParseInt(value, 10, 64) + case "ddb.geometry.sectors": + info.Sectors, _ = strconv.ParseInt(value, 10, 64) + case "ddb.longContentID": + info.LongCID = value + case "ddb.adapterType": + info.AdapterType = value + case "ddb.virtualHWVersion": + info.VirtualHWVersion = value + } + } + } + } + if !findExtent { + return nil, fmt.Errorf("not a vmdk file") + } + return &info, nil +} diff --git a/pkg/util/vmdkutils/vmdkutils_test.go b/pkg/util/vmdkutils/vmdkutils_test.go new file mode 100644 index 0000000000..8fee91aeef --- /dev/null +++ b/pkg/util/vmdkutils/vmdkutils_test.go @@ -0,0 +1,44 @@ +package vmdkutils + +import "testing" + +const ( + VMDKContent = ` version=1 + encoding="UTF-8" + CID=ab798ecb + parentCID=ffffffff + isNativeSnapshot="no" + createType="vmfs" + + # Extent description + RW 62914560 VMFS "c8336ec2-3885-4205-a2f1-095e48228a56-flat.vmdk" + + # The Disk Data Base + #DDB + + ddb.adapterType = "lsilogic" + ddb.deletable = "false" + ddb.geometry.cylinders = "62415" + ddb.geometry.heads = "16" + ddb.geometry.sectors = "63" + ddb.longContentID = "4d1762d22407e6b7e3e63557ab798ecb" + ddb.thinProvisioned = "1" + ddb.uuid = "60 00 C2 97 32 7d ac 2b-ac 97 a4 95 5b a6 0b c0" + ddb.virtualHWVersion = "13" +` +) + +func TestParseStream(t *testing.T) { + info, err := Parse(VMDKContent) + if err != nil { + t.Errorf("parse error %s", err) + } + t.Logf("%#v", info) + + _, err = Parse("") + if err != nil { + t.Logf("parse error %s", err) + } else { + t.Errorf("should parse error") + } +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/client.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/client.go index 12548c409f..7e9a0e4204 100644 --- a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/client.go +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/client.go @@ -22,6 +22,7 @@ import ( "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" "net/http" + "net" ) // this value will be replaced while build: -ldflags="-X sdk.version=x.x.x" @@ -185,14 +186,35 @@ func (client *Client) DoActionWithSigner(request requests.AcsRequest, response r for key, value := range request.GetHeaders() { httpRequest.Header[key] = []string{value} } - httpResponse, err := client.httpClient.Do(httpRequest) - if err != nil { - return + var httpResponse *http.Response + for retryTimes := 0; retryTimes < client.config.MaxRetryTime; retryTimes++ { + httpResponse, err = client.httpClient.Do(httpRequest) + // if status code >= 500 or timeout, will trigger retry + if client.config.AutoRetry && isNeedRetry(httpResponse, err){ + continue + } + // receive error but not timeout + if err != nil { + return + } + break } err = responses.Unmarshal(response, httpResponse, request.GetAcceptFormat()) return } +func isNeedRetry(response *http.Response, err error) bool { + if response.StatusCode >= http.StatusInternalServerError { + // internal server error + return true + }else if err, ok := err.(net.Error); ok && err.Timeout() { + // timeout + return true + }else{ + return false + } +} + func (client *Client) AddAsyncTask(task func()) (err error) { if client.asyncTaskQueue != nil { client.asyncTaskQueue <- task diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/config.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/config.go index 52a2384987..9e9727ec57 100644 --- a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/config.go +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/config.go @@ -43,6 +43,41 @@ func (c *Config) WithTimeout(timeout time.Duration) *Config { return c } +func (c *Config) WithAutoRetry(isAutoRetry bool) *Config { + c.AutoRetry = isAutoRetry + return c +} + +func (c *Config) WithMaxRetryTime(maxRetryTime int) *Config { + c.MaxRetryTime = maxRetryTime + return c +} + +func (c *Config) WithUserAgent(userAgent string) *Config { + c.UserAgent = userAgent + return c +} + +func (c *Config) WithHttpTransport(httpTransport *http.Transport) *Config { + c.HttpTransport = httpTransport + return c +} + +func (c *Config) WithEnableAsync(isEnableAsync bool) *Config { + c.EnableAsync = isEnableAsync + return c +} + +func (c *Config) WithMaxTaskQueueSize(maxTaskQueueSize int) *Config { + c.MaxTaskQueueSize = maxTaskQueueSize + return c +} + +func (c *Config) WithGoRoutinePoolSize(goRoutinePoolSize int) *Config { + c.GoRoutinePoolSize = goRoutinePoolSize + return c +} + func (c *Config) WithDebug(isDebug bool) *Config { c.Debug = isDebug return c diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/client_error.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/client_error.go index e48f7dd0f3..d4c680f253 100644 --- a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/client_error.go +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/client_error.go @@ -14,6 +14,8 @@ package errors +import "fmt" + const ( DefaultClientErrorStatus = 400 DefaultClientErrorCode = "SDK.ClientError" @@ -54,9 +56,9 @@ func NewClientError(errorCode, message string, originErr error) Error { func (err *ClientError) Error() string { if err.originError != nil { return err.originError.Error() - } else { - return "" } + + return fmt.Sprintf("[%s] %s", err.errorCode, err.message) } func (err *ClientError) OriginError() error { diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/server_error.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/server_error.go index 9addf8a2e8..f1212ccd07 100644 --- a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/server_error.go +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/server_error.go @@ -14,6 +14,8 @@ package errors +import "fmt" + type ServerError struct { httpStatus int errorCode string @@ -41,7 +43,7 @@ func (err *ServerError) Message() string { } func (err *ServerError) Error() string { - return "SDK.ServerError" + return fmt.Sprintf("SDK.ServerError %s %s", err.errorCode, err.message) } func (err *ServerError) OriginError() error { diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/json_parser.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/json_parser.go new file mode 100644 index 0000000000..baa7da037f --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/json_parser.go @@ -0,0 +1,341 @@ +package responses + +import ( + "github.com/json-iterator/go" + "sync" + "unsafe" + "encoding/json" + "strconv" + "strings" + "io" + "math" +) + +const maxUint = ^uint(0) +const maxInt = int(maxUint >> 1) +const minInt = -maxInt - 1 + +var jsonParser jsoniter.API +var initJson = &sync.Once{} + +func initJsonParserOnce() { + initJson.Do(func() { + registerBetterFuzzyDecoder() + jsonParser = jsoniter.ConfigCompatibleWithStandardLibrary + }) +} + +func registerBetterFuzzyDecoder() { + jsoniter.RegisterTypeDecoder("string", &nullableFuzzyStringDecoder{}) + jsoniter.RegisterTypeDecoder("bool", &fuzzyBoolDecoder{}) + jsoniter.RegisterTypeDecoder("float32", &nullableFuzzyFloat32Decoder{}) + jsoniter.RegisterTypeDecoder("float64", &nullableFuzzyFloat64Decoder{}) + jsoniter.RegisterTypeDecoder("int", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(maxInt) || val < float64(minInt) { + iter.ReportError("fuzzy decode int", "exceed range") + return + } + *((*int)(ptr)) = int(val) + } else { + *((*int)(ptr)) = iter.ReadInt() + } + }}) + jsoniter.RegisterTypeDecoder("uint", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(maxUint) || val < 0 { + iter.ReportError("fuzzy decode uint", "exceed range") + return + } + *((*uint)(ptr)) = uint(val) + } else { + *((*uint)(ptr)) = iter.ReadUint() + } + }}) + jsoniter.RegisterTypeDecoder("int8", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxInt8) || val < float64(math.MinInt8) { + iter.ReportError("fuzzy decode int8", "exceed range") + return + } + *((*int8)(ptr)) = int8(val) + } else { + *((*int8)(ptr)) = iter.ReadInt8() + } + }}) + jsoniter.RegisterTypeDecoder("uint8", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxUint8) || val < 0 { + iter.ReportError("fuzzy decode uint8", "exceed range") + return + } + *((*uint8)(ptr)) = uint8(val) + } else { + *((*uint8)(ptr)) = iter.ReadUint8() + } + }}) + jsoniter.RegisterTypeDecoder("int16", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxInt16) || val < float64(math.MinInt16) { + iter.ReportError("fuzzy decode int16", "exceed range") + return + } + *((*int16)(ptr)) = int16(val) + } else { + *((*int16)(ptr)) = iter.ReadInt16() + } + }}) + jsoniter.RegisterTypeDecoder("uint16", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxUint16) || val < 0 { + iter.ReportError("fuzzy decode uint16", "exceed range") + return + } + *((*uint16)(ptr)) = uint16(val) + } else { + *((*uint16)(ptr)) = iter.ReadUint16() + } + }}) + jsoniter.RegisterTypeDecoder("int32", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxInt32) || val < float64(math.MinInt32) { + iter.ReportError("fuzzy decode int32", "exceed range") + return + } + *((*int32)(ptr)) = int32(val) + } else { + *((*int32)(ptr)) = iter.ReadInt32() + } + }}) + jsoniter.RegisterTypeDecoder("uint32", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxUint32) || val < 0 { + iter.ReportError("fuzzy decode uint32", "exceed range") + return + } + *((*uint32)(ptr)) = uint32(val) + } else { + *((*uint32)(ptr)) = iter.ReadUint32() + } + }}) + jsoniter.RegisterTypeDecoder("int64", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxInt64) || val < float64(math.MinInt64) { + iter.ReportError("fuzzy decode int64", "exceed range") + return + } + *((*int64)(ptr)) = int64(val) + } else { + *((*int64)(ptr)) = iter.ReadInt64() + } + }}) + jsoniter.RegisterTypeDecoder("uint64", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxUint64) || val < 0 { + iter.ReportError("fuzzy decode uint64", "exceed range") + return + } + *((*uint64)(ptr)) = uint64(val) + } else { + *((*uint64)(ptr)) = iter.ReadUint64() + } + }}) +} + +type nullableFuzzyStringDecoder struct { +} + +func (decoder *nullableFuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + switch valueType { + case jsoniter.NumberValue: + var number json.Number + iter.ReadVal(&number) + *((*string)(ptr)) = string(number) + case jsoniter.StringValue: + *((*string)(ptr)) = iter.ReadString() + case jsoniter.BoolValue: + *((*string)(ptr)) = strconv.FormatBool(iter.ReadBool()) + case jsoniter.NilValue: + iter.ReadNil() + *((*string)(ptr)) = "" + default: + iter.ReportError("fuzzyStringDecoder", "not number or string or bool") + } +} + +type fuzzyBoolDecoder struct { +} + +func (decoder *fuzzyBoolDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + switch valueType { + case jsoniter.BoolValue: + *((*bool)(ptr)) = iter.ReadBool() + case jsoniter.NumberValue: + var number json.Number + iter.ReadVal(&number) + num, err := number.Int64() + if err != nil { + iter.ReportError("fuzzyBoolDecoder", "get value from json.number failed") + } + if num == 0 { + *((*bool)(ptr)) = false + } else { + *((*bool)(ptr)) = true + } + case jsoniter.StringValue: + strValue := strings.ToLower(iter.ReadString()) + if strValue == "true" { + *((*bool)(ptr)) = true + } else if strValue == "false" || strValue == "" { + *((*bool)(ptr)) = false + } else { + iter.ReportError("fuzzyBoolDecoder", "unsupported bool value: "+strValue) + } + case jsoniter.NilValue: + iter.ReadNil() + *((*bool)(ptr)) = false + default: + iter.ReportError("fuzzyBoolDecoder", "not number or string or nil") + } +} + +type tolerateEmptyArrayDecoder struct { + valDecoder jsoniter.ValDecoder +} + +func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if iter.WhatIsNext() == jsoniter.ArrayValue { + iter.Skip() + newIter := iter.Pool().BorrowIterator([]byte("{}")) + defer iter.Pool().ReturnIterator(newIter) + decoder.valDecoder.Decode(ptr, newIter) + } else { + decoder.valDecoder.Decode(ptr, iter) + } +} + +type nullableFuzzyIntegerDecoder struct { + fun func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) +} + +func (decoder *nullableFuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + var str string + switch valueType { + case jsoniter.NumberValue: + var number json.Number + iter.ReadVal(&number) + str = string(number) + case jsoniter.StringValue: + str = iter.ReadString() + // support empty string + if str == ""{ + str = "0" + } + case jsoniter.BoolValue: + if iter.ReadBool() { + str = "1" + } else { + str = "0" + } + case jsoniter.NilValue: + iter.ReadNil() + str = "0" + default: + iter.ReportError("fuzzyIntegerDecoder", "not number or string") + } + newIter := iter.Pool().BorrowIterator([]byte(str)) + defer iter.Pool().ReturnIterator(newIter) + isFloat := strings.IndexByte(str, '.') != -1 + decoder.fun(isFloat, ptr, newIter) + if newIter.Error != nil && newIter.Error != io.EOF { + iter.Error = newIter.Error + } +} + +type nullableFuzzyFloat32Decoder struct { +} + +func (decoder *nullableFuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + var str string + switch valueType { + case jsoniter.NumberValue: + *((*float32)(ptr)) = iter.ReadFloat32() + case jsoniter.StringValue: + str = iter.ReadString() + // support empty string + if str == ""{ + *((*float32)(ptr)) = 0 + return + } + newIter := iter.Pool().BorrowIterator([]byte(str)) + defer iter.Pool().ReturnIterator(newIter) + *((*float32)(ptr)) = newIter.ReadFloat32() + if newIter.Error != nil && newIter.Error != io.EOF { + iter.Error = newIter.Error + } + case jsoniter.BoolValue: + // support bool to float32 + if iter.ReadBool() { + *((*float32)(ptr)) = 1 + } else { + *((*float32)(ptr)) = 0 + } + case jsoniter.NilValue: + iter.ReadNil() + *((*float32)(ptr)) = 0 + default: + iter.ReportError("nullableFuzzyFloat32Decoder", "not number or string") + } +} + +type nullableFuzzyFloat64Decoder struct { +} + +func (decoder *nullableFuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + var str string + switch valueType { + case jsoniter.NumberValue: + *((*float64)(ptr)) = iter.ReadFloat64() + case jsoniter.StringValue: + str = iter.ReadString() + // support empty string + if str == ""{ + *((*float64)(ptr)) = 0 + return + } + newIter := iter.Pool().BorrowIterator([]byte(str)) + defer iter.Pool().ReturnIterator(newIter) + *((*float64)(ptr)) = newIter.ReadFloat64() + if newIter.Error != nil && newIter.Error != io.EOF { + iter.Error = newIter.Error + } + case jsoniter.BoolValue: + // support bool to float64 + if iter.ReadBool() { + *((*float64)(ptr)) = 1 + } else { + *((*float64)(ptr)) = 0 + } + case jsoniter.NilValue: + // support empty string + iter.ReadNil() + *((*float64)(ptr)) = 0 + default: + iter.ReportError("nullableFuzzyFloat32Decoder", "not number or string") + } +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/response.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/response.go index 108efaaff0..2ad8412e03 100644 --- a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/response.go +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/response.go @@ -16,8 +16,8 @@ package responses import ( "bytes" - "encoding/json" "encoding/xml" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" "io/ioutil" "net/http" "strconv" @@ -25,6 +25,7 @@ import ( ) type AcsResponse interface { + IsSuccess() bool GetHttpStatus() int GetHttpHeaders() map[string][]string GetHttpContentString() string @@ -38,8 +39,13 @@ func Unmarshal(response AcsResponse, httpResponse *http.Response, format string) if err != nil { return } + if !response.IsSuccess() { + err = errors.NewServerError(response.GetHttpStatus(), response.GetOriginHttpResponse().Status, response.GetHttpContentString()) + return + } if strings.ToUpper(format) == "JSON" { - err = json.Unmarshal(response.GetHttpContentBytes(), response) + initJsonParserOnce() + err = jsonParser.Unmarshal(response.GetHttpContentBytes(), response) } else if strings.ToUpper(format) == "XML" { err = xml.Unmarshal(response.GetHttpContentBytes(), response) } diff --git a/vendor/github.com/davecgh/go-spew/LICENSE b/vendor/github.com/davecgh/go-spew/LICENSE index bc52e96f2b..c836416192 100644 --- a/vendor/github.com/davecgh/go-spew/LICENSE +++ b/vendor/github.com/davecgh/go-spew/LICENSE @@ -2,7 +2,7 @@ ISC License Copyright (c) 2012-2016 Dave Collins -Permission to use, copy, modify, and/or distribute this software for any +Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. diff --git a/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/davecgh/go-spew/spew/bypass.go index 792994785e..8a4a6589a2 100644 --- a/vendor/github.com/davecgh/go-spew/spew/bypass.go +++ b/vendor/github.com/davecgh/go-spew/spew/bypass.go @@ -16,9 +16,7 @@ // when the code is not running on Google App Engine, compiled by GopherJS, and // "-tags safe" is not added to the go build command line. The "disableunsafe" // tag is deprecated and thus should not be used. -// Go versions prior to 1.4 are disabled because they use a different layout -// for interfaces which make the implementation of unsafeReflectValue more complex. -// +build !js,!appengine,!safe,!disableunsafe,go1.4 +// +build !js,!appengine,!safe,!disableunsafe package spew @@ -36,49 +34,80 @@ const ( ptrSize = unsafe.Sizeof((*byte)(nil)) ) -type flag uintptr - var ( - // flagRO indicates whether the value field of a reflect.Value - // is read-only. - flagRO flag + // offsetPtr, offsetScalar, and offsetFlag are the offsets for the + // internal reflect.Value fields. These values are valid before golang + // commit ecccf07e7f9d which changed the format. The are also valid + // after commit 82f48826c6c7 which changed the format again to mirror + // the original format. Code in the init function updates these offsets + // as necessary. + offsetPtr = uintptr(ptrSize) + offsetScalar = uintptr(0) + offsetFlag = uintptr(ptrSize * 2) - // flagAddr indicates whether the address of the reflect.Value's - // value may be taken. - flagAddr flag + // flagKindWidth and flagKindShift indicate various bits that the + // reflect package uses internally to track kind information. + // + // flagRO indicates whether or not the value field of a reflect.Value is + // read-only. + // + // flagIndir indicates whether the value field of a reflect.Value is + // the actual data or a pointer to the data. + // + // These values are valid before golang commit 90a7c3c86944 which + // changed their positions. Code in the init function updates these + // flags as necessary. + flagKindWidth = uintptr(5) + flagKindShift = uintptr(flagKindWidth - 1) + flagRO = uintptr(1 << 0) + flagIndir = uintptr(1 << 1) ) -// flagKindMask holds the bits that make up the kind -// part of the flags field. In all the supported versions, -// it is in the lower 5 bits. -const flagKindMask = flag(0x1f) - -// Different versions of Go have used different -// bit layouts for the flags type. This table -// records the known combinations. -var okFlags = []struct { - ro, addr flag -}{{ - // From Go 1.4 to 1.5 - ro: 1 << 5, - addr: 1 << 7, -}, { - // Up to Go tip. - ro: 1<<5 | 1<<6, - addr: 1 << 8, -}} - -var flagValOffset = func() uintptr { - field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") - if !ok { - panic("reflect.Value has no flag field") +func init() { + // Older versions of reflect.Value stored small integers directly in the + // ptr field (which is named val in the older versions). Versions + // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named + // scalar for this purpose which unfortunately came before the flag + // field, so the offset of the flag field is different for those + // versions. + // + // This code constructs a new reflect.Value from a known small integer + // and checks if the size of the reflect.Value struct indicates it has + // the scalar field. When it does, the offsets are updated accordingly. + vv := reflect.ValueOf(0xf00) + if unsafe.Sizeof(vv) == (ptrSize * 4) { + offsetScalar = ptrSize * 2 + offsetFlag = ptrSize * 3 } - return field.Offset -}() -// flagField returns a pointer to the flag field of a reflect.Value. -func flagField(v *reflect.Value) *flag { - return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) + // Commit 90a7c3c86944 changed the flag positions such that the low + // order bits are the kind. This code extracts the kind from the flags + // field and ensures it's the correct type. When it's not, the flag + // order has been changed to the newer format, so the flags are updated + // accordingly. + upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) + upfv := *(*uintptr)(upf) + flagKindMask := uintptr((1<>flagKindShift != uintptr(reflect.Int) { + flagKindShift = 0 + flagRO = 1 << 5 + flagIndir = 1 << 6 + + // Commit adf9b30e5594 modified the flags to separate the + // flagRO flag into two bits which specifies whether or not the + // field is embedded. This causes flagIndir to move over a bit + // and means that flagRO is the combination of either of the + // original flagRO bit and the new bit. + // + // This code detects the change by extracting what used to be + // the indirect bit to ensure it's set. When it's not, the flag + // order has been changed to the newer format, so the flags are + // updated accordingly. + if upfv&flagIndir == 0 { + flagRO = 3 << 5 + flagIndir = 1 << 7 + } + } } // unsafeReflectValue converts the passed reflect.Value into a one that bypasses @@ -90,56 +119,34 @@ func flagField(v *reflect.Value) *flag { // This allows us to check for implementations of the Stringer and error // interfaces to be used for pretty printing ordinarily unaddressable and // inaccessible values such as unexported struct fields. -func unsafeReflectValue(v reflect.Value) reflect.Value { - if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { - return v - } - flagFieldPtr := flagField(&v) - *flagFieldPtr &^= flagRO - *flagFieldPtr |= flagAddr - return v -} - -// Sanity checks against future reflect package changes -// to the type or semantics of the Value.flag field. -func init() { - field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") - if !ok { - panic("reflect.Value has no flag field") - } - if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { - panic("reflect.Value flag field has changed kind") - } - type t0 int - var t struct { - A t0 - // t0 will have flagEmbedRO set. - t0 - // a will have flagStickyRO set - a t0 - } - vA := reflect.ValueOf(t).FieldByName("A") - va := reflect.ValueOf(t).FieldByName("a") - vt0 := reflect.ValueOf(t).FieldByName("t0") - - // Infer flagRO from the difference between the flags - // for the (otherwise identical) fields in t. - flagPublic := *flagField(&vA) - flagWithRO := *flagField(&va) | *flagField(&vt0) - flagRO = flagPublic ^ flagWithRO - - // Infer flagAddr from the difference between a value - // taken from a pointer and not. - vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") - flagNoPtr := *flagField(&vA) - flagPtr := *flagField(&vPtrA) - flagAddr = flagNoPtr ^ flagPtr - - // Check that the inferred flags tally with one of the known versions. - for _, f := range okFlags { - if flagRO == f.ro && flagAddr == f.addr { - return +func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { + indirects := 1 + vt := v.Type() + upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) + rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) + if rvf&flagIndir != 0 { + vt = reflect.PtrTo(v.Type()) + indirects++ + } else if offsetScalar != 0 { + // The value is in the scalar field when it's not one of the + // reference types. + switch vt.Kind() { + case reflect.Uintptr: + case reflect.Chan: + case reflect.Func: + case reflect.Map: + case reflect.Ptr: + case reflect.UnsafePointer: + default: + upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + + offsetScalar) } } - panic("reflect.Value read-only flag has changed semantics") + + pv := reflect.NewAt(vt, upv) + rv = pv + for i := 0; i < indirects; i++ { + rv = rv.Elem() + } + return rv } diff --git a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go index 205c28d68c..1fe3cf3d5d 100644 --- a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go +++ b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go @@ -16,7 +16,7 @@ // when the code is running on Google App Engine, compiled by GopherJS, or // "-tags safe" is added to the go build command line. The "disableunsafe" // tag is deprecated and thus should not be used. -// +build js appengine safe disableunsafe !go1.4 +// +build js appengine safe disableunsafe package spew diff --git a/vendor/github.com/davecgh/go-spew/spew/common.go b/vendor/github.com/davecgh/go-spew/spew/common.go index 1be8ce9457..7c519ff47a 100644 --- a/vendor/github.com/davecgh/go-spew/spew/common.go +++ b/vendor/github.com/davecgh/go-spew/spew/common.go @@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) { w.Write(closeParenBytes) } -// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' +// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x' // prefix to Writer w. func printHexPtr(w io.Writer, p uintptr) { // Null pointer. diff --git a/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/davecgh/go-spew/spew/dump.go index f78d89fc1f..df1d582a72 100644 --- a/vendor/github.com/davecgh/go-spew/spew/dump.go +++ b/vendor/github.com/davecgh/go-spew/spew/dump.go @@ -35,16 +35,16 @@ var ( // cCharRE is a regular expression that matches a cgo char. // It is used to detect character arrays to hexdump them. - cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`) + cCharRE = regexp.MustCompile("^.*\\._Ctype_char$") // cUnsignedCharRE is a regular expression that matches a cgo unsigned // char. It is used to detect unsigned character arrays to hexdump // them. - cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`) + cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$") // cUint8tCharRE is a regular expression that matches a cgo uint8_t. // It is used to detect uint8_t arrays to hexdump them. - cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`) + cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$") ) // dumpState contains information about the state of a dump operation. @@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) { // Display dereferenced value. d.w.Write(openParenBytes) switch { - case nilFound: + case nilFound == true: d.w.Write(nilAngleBytes) - case cycleFound: + case cycleFound == true: d.w.Write(circularBytes) default: diff --git a/vendor/github.com/davecgh/go-spew/spew/format.go b/vendor/github.com/davecgh/go-spew/spew/format.go index b04edb7d7a..c49875bacb 100644 --- a/vendor/github.com/davecgh/go-spew/spew/format.go +++ b/vendor/github.com/davecgh/go-spew/spew/format.go @@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) { // Display dereferenced value. switch { - case nilFound: + case nilFound == true: f.fs.Write(nilAngleBytes) - case cycleFound: + case cycleFound == true: f.fs.Write(circularShortBytes) default: diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/license b/vendor/github.com/konsorten/go-windows-terminal-sequences/license deleted file mode 100644 index 14127cd831..0000000000 --- a/vendor/github.com/konsorten/go-windows-terminal-sequences/license +++ /dev/null @@ -1,9 +0,0 @@ -(The MIT License) - -Copyright (c) 2017 marvin + konsorten GmbH (open-source@konsorten.de) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/yunion.io/x/jsonutils/marshal.go b/vendor/yunion.io/x/jsonutils/marshal.go index aff79cf1d2..fb99c077ec 100644 --- a/vendor/yunion.io/x/jsonutils/marshal.go +++ b/vendor/yunion.io/x/jsonutils/marshal.go @@ -12,37 +12,118 @@ import ( "reflect" "time" + "strings" "yunion.io/x/log" "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/tristate" - "yunion.io/x/pkg/util/reflectutils" "yunion.io/x/pkg/util/timeutils" + "yunion.io/x/pkg/utils" ) -func marshalSlice(val reflect.Value) *JSONArray { +func marshalSlice(val reflect.Value, info *jsonMarshalInfo) JSONObject { + if val.Len() == 0 && info != nil && info.omitEmpty { + return JSONNull + } objs := make([]JSONObject, val.Len()) for i := 0; i < val.Len(); i += 1 { - objs[i] = marshalValue(val.Index(i)) + objs[i] = marshalValue(val.Index(i), nil) + } + arr := NewArray(objs...) + if info != nil && info.forceString { + return NewString(arr.String()) + } else { + return arr } - return NewArray(objs...) } -func marshalMap(val reflect.Value) *JSONDict { +func marshalMap(val reflect.Value, info *jsonMarshalInfo) JSONObject { keys := val.MapKeys() + if len(keys) == 0 && info != nil && info.omitEmpty { + return JSONNull + } objPairs := make([]JSONPair, 0) for i := 0; i < len(keys); i += 1 { key := keys[i] - val := marshalValue(val.MapIndex(key)) + val := marshalValue(val.MapIndex(key), nil) if val != JSONNull { objPairs = append(objPairs, JSONPair{key: fmt.Sprintf("%s", key), val: val}) } } - return NewDict(objPairs...) + dict := NewDict(objPairs...) + if info != nil && info.forceString { + return NewString(dict.String()) + } else { + return dict + } } -func marshalStruct(val reflect.Value) *JSONDict { +func marshalStruct(val reflect.Value, info *jsonMarshalInfo) JSONObject { objPairs := struct2JSONPairs(val) - return NewDict(objPairs...) + if len(objPairs) == 0 && info != nil && info.omitEmpty { + return JSONNull + } + dict := NewDict(objPairs...) + if info != nil && info.forceString { + return NewString(dict.String()) + } else { + return dict + } +} + +type jsonMarshalInfo struct { + ignore bool + omitEmpty bool + omitFalse bool + omitZero bool + name string + forceString bool +} + +func parseJsonMarshalInfo(fieldTag reflect.StructTag) jsonMarshalInfo { + info := jsonMarshalInfo{} + info.omitEmpty = true + info.omitZero = false + info.omitFalse = false + + tags := utils.TagMap(fieldTag) + if val, ok := tags["json"]; ok { + keys := strings.Split(val, ",") + if len(keys) > 0 { + if keys[0] == "-" { + if len(keys) > 1 { + info.name = keys[0] + } else { + info.ignore = true + } + } else { + info.name = keys[0] + } + } + if len(keys) > 1 { + for _, k := range keys[1:] { + switch k { + case "omitempty": + info.omitEmpty = true + case "allowempty": + info.omitEmpty = false + case "omitzero": + info.omitZero = true + case "allowzero": + info.omitZero = false + case "omitfalse": + info.omitFalse = true + case "allowfalse": + info.omitFalse = false + case "string": + info.forceString = true + } + } + } + } + if val, ok := tags["name"]; ok { + info.name = val + } + return info } func struct2JSONPairs(val reflect.Value) []JSONPair { @@ -57,12 +138,17 @@ func struct2JSONPairs(val reflect.Value) []JSONPair { newPairs := struct2JSONPairs(val.Field(i)) objPairs = append(objPairs, newPairs...) } else { - key := reflectutils.GetStructFieldName(&fieldType) // utils.CamelSplit(fieldType.Name, "_") - if key == "" { + jsonInfo := parseJsonMarshalInfo(fieldType.Tag) + + if jsonInfo.ignore { continue } - val := marshalValue(val.Field(i)) - if val != JSONNull { + key := jsonInfo.name + if len(key) == 0 { + key = utils.CamelSplit(fieldType.Name, "_") + } + val := marshalValue(val.Field(i), &jsonInfo) + if val != nil && val != JSONNull { objPair := JSONPair{key: key, val: val} objPairs = append(objPairs, objPair) } @@ -71,23 +157,41 @@ func struct2JSONPairs(val reflect.Value) []JSONPair { return objPairs } -func marshalInt64(val int64) *JSONInt { - return NewInt(val) -} - -func marshalFloat64(val float64) *JSONFloat { - return NewFloat(val) -} - -func marshalBoolean(val bool) *JSONBool { - if val { - return JSONTrue +func marshalInt64(val int64, info *jsonMarshalInfo) JSONObject { + if val == 0 && info != nil && info.omitZero { + return JSONNull + } else if info != nil && info.forceString { + return NewString(fmt.Sprintf("%d", val)) } else { - return JSONFalse + return NewInt(val) } } -func marshalTristate(val tristate.TriState) JSONObject { +func marshalFloat64(val float64, info *jsonMarshalInfo) JSONObject { + if val == 0.0 && info != nil && info.omitZero { + return JSONNull + } else if info != nil && info.forceString { + return NewString(fmt.Sprintf("%f", val)) + } else { + return NewFloat(val) + } +} + +func marshalBoolean(val bool, info *jsonMarshalInfo) JSONObject { + if !val && info != nil && info.omitFalse { + return JSONNull + } else if info != nil && info.forceString { + return NewString(fmt.Sprintf("%v", val)) + } else { + if val { + return JSONTrue + } else { + return JSONFalse + } + } +} + +func marshalTristate(val tristate.TriState, info *jsonMarshalInfo) JSONObject { if val.IsTrue() { return JSONTrue } else if val.IsFalse() { @@ -97,16 +201,19 @@ func marshalTristate(val tristate.TriState) JSONObject { } } -func marshalString(val string) JSONObject { - if len(val) == 0 { +func marshalString(val string, info *jsonMarshalInfo) JSONObject { + if len(val) == 0 && info != nil && info.omitEmpty { return JSONNull } else { return NewString(val) } } -func marshalTime(val time.Time) *JSONString { +func marshalTime(val time.Time, info *jsonMarshalInfo) JSONObject { if val.IsZero() { + if info != nil && info.omitEmpty { + return JSONNull + } return NewString("") } else { return NewString(timeutils.FullIsoTime(val)) @@ -118,10 +225,10 @@ func Marshal(obj interface{}) JSONObject { return JSONNull } objValue := reflect.Indirect(reflect.ValueOf(obj)) - return marshalValue(objValue) + return marshalValue(objValue, nil) } -func marshalValue(objValue reflect.Value) JSONObject { +func marshalValue(objValue reflect.Value, info *jsonMarshalInfo) JSONObject { switch objValue.Type() { case JSONDictPtrType, JSONArrayPtrType, JSONBoolPtrType, JSONIntPtrType, JSONFloatPtrType, JSONStringPtrType, JSONObjectType: if objValue.IsNil() { @@ -131,81 +238,105 @@ func marshalValue(objValue reflect.Value) JSONObject { case JSONDictType: json, ok := objValue.Interface().(JSONDict) if ok { - return &json + if len(json.data) == 0 && info != nil && info.omitEmpty { + return JSONNull + } else { + return &json + } } else { return JSONNull } case JSONArrayType: json, ok := objValue.Interface().(JSONArray) if ok { - return &json + if len(json.data) == 0 && info != nil && info.omitEmpty { + return JSONNull + } else { + return &json + } } else { return JSONNull } case JSONBoolType: json, ok := objValue.Interface().(JSONBool) if ok { - return &json + if !json.data && info != nil && info.omitEmpty { + return JSONNull + } else { + return &json + } } else { return JSONNull } case JSONIntType: json, ok := objValue.Interface().(JSONInt) if ok { - return &json + if json.data == 0 && info != nil && info.omitEmpty { + return JSONNull + } else { + return &json + } } else { return JSONNull } case JSONFloatType: json, ok := objValue.Interface().(JSONFloat) if ok { - return &json + if json.data == 0.0 && info != nil && info.omitEmpty { + return JSONNull + } else { + return &json + } } else { return JSONNull } case JSONStringType: json, ok := objValue.Interface().(JSONString) if ok { - return &json + if len(json.data) == 0 && info != nil && info.omitEmpty { + return JSONNull + } else { + return &json + } } else { return JSONNull } case tristate.TriStateType: tri, ok := objValue.Interface().(tristate.TriState) if ok { - return marshalTristate(tri) + return marshalTristate(tri, info) } else { return JSONNull } } switch objValue.Kind() { case reflect.Slice, reflect.Array: - return marshalSlice(objValue) + return marshalSlice(objValue, info) case reflect.Struct: if objValue.Type() == gotypes.TimeType { - return marshalTime(objValue.Interface().(time.Time)) + return marshalTime(objValue.Interface().(time.Time), info) } else { - return marshalStruct(objValue) + return marshalStruct(objValue, info) } case reflect.Map: - return marshalMap(objValue) + return marshalMap(objValue, info) case reflect.String: strValue := objValue.Convert(gotypes.StringType) - return marshalString(strValue.Interface().(string)) + return marshalString(strValue.Interface().(string), info) case reflect.Bool: - return marshalBoolean(objValue.Interface().(bool)) + return marshalBoolean(objValue.Interface().(bool), info) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: intValue := objValue.Convert(gotypes.Int64Type) - return marshalInt64(intValue.Interface().(int64)) + return marshalInt64(intValue.Interface().(int64), info) case reflect.Float32, reflect.Float64: floatValue := objValue.Convert(gotypes.Float64Type) - return marshalFloat64(floatValue.Interface().(float64)) + return marshalFloat64(floatValue.Interface().(float64), info) case reflect.Interface, reflect.Ptr: if objValue.IsNil() { return JSONNull } - return marshalValue(objValue.Elem()) + return marshalValue(objValue.Elem(), info) default: log.Errorf("unsupport object %s %s", objValue.Type(), objValue.Interface()) return JSONNull