hcso support sync vm host

This commit is contained in:
tb365
2021-08-25 20:45:08 +08:00
parent 5d6cc519f3
commit d57d2be977
12 changed files with 255 additions and 52 deletions
+4
View File
@@ -304,6 +304,10 @@ func (self *SBaseGuestDriver) RemoteDeployGuestForCreate(ctx context.Context, us
return nil, cloudprovider.ErrNotSupported
}
func (self *SBaseGuestDriver) RemoteDeployGuestSyncHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, iVM cloudprovider.ICloudVM) (cloudprovider.ICloudHost, error) {
return nil, cloudprovider.ErrNotSupported
}
func (self *SBaseGuestDriver) RemoteActionAfterGuestCreated(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, ivm cloudprovider.ICloudVM, desc *cloudprovider.SManagedVMCreateConfig) {
return
}
+41
View File
@@ -15,11 +15,17 @@
package guestdrivers
import (
"context"
"database/sql"
"fmt"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
@@ -143,6 +149,41 @@ func (self *SHuaweiCloudStackGuestDriver) GetInstanceCapability() cloudprovider.
}
}
func (self *SHuaweiCloudStackGuestDriver) RemoteDeployGuestSyncHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, iVM cloudprovider.ICloudVM) (cloudprovider.ICloudHost, error) {
if hostId := iVM.GetIHostId(); len(hostId) > 0 {
nh, err := db.FetchByExternalIdAndManagerId(models.HostManager, hostId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
return q.Equals("manager_id", host.ManagerId)
})
if err != nil {
log.Debugf("failed to found new hostId(%s) for ivm %s(%s) error: %v", hostId, guest.Name, guest.Id, err)
if errors.Cause(err) != sql.ErrNoRows {
return nil, errors.Wrap(err, "FetchByExternalIdAndManagerId")
}
// HYPERVISOR_HUAWEI_CLOUD_STACK VM被部署到一台全新的宿主机
zone, err := host.GetZone()
if err != nil {
log.Warningf("host %s GetZone: %s", host.GetId(), err)
} else {
_host, err := models.HostManager.NewFromCloudHost(ctx, userCred, iVM.GetIHost(), host.GetCloudprovider(), zone)
if err != nil {
log.Warningf("NewFromCloudHost %s: %s", iVM.GetIHostId(), err)
} else {
host = _host
}
}
} else {
host = nh.(*models.SHost)
}
}
if host.GetId() != guest.HostId {
guest.OnScheduleToHost(ctx, userCred, host.GetId())
}
return host.GetIHost()
}
func (self *SHuaweiCloudStackGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle) bool {
months := bc.GetMonths()
if (months >= 1 && months <= 9) || (months == 12) || (months == 24) || (months == 36) {
+21 -13
View File
@@ -491,24 +491,16 @@ func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestForCreate(ctx conte
}
db.SetExternalId(guest, userCred, iVM.GetGlobalId())
if hostId := iVM.GetIHostId(); len(hostId) > 0 {
host, err := db.FetchByExternalIdAndManagerId(models.HostManager, hostId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
return q.Equals("manager_id", host.ManagerId)
})
if err != nil {
log.Warningf("failed to found new hostId(%s) for ivm %s(%s) error: %v", hostId, guest.Name, guest.Id, err)
} else if host.GetId() != guest.HostId {
guest.OnScheduleToHost(ctx, userCred, host.GetId())
}
}
return iVM, nil
}()
if err != nil {
return nil, err
}
// iVM 实际所在的ihost 可能和 调度选择的host不是同一个,此处根据iVM实际所在host,重新同步
ihost, err = guest.GetDriver().RemoteDeployGuestSyncHost(ctx, userCred, guest, host, iVM)
if err != nil {
return nil, errors.Wrap(err, "RemoteDeployGuestSyncHost")
}
initialState := guest.GetDriver().GetGuestInitialStateAfterCreate()
log.Debugf("VMcreated %s, wait status %s ...", iVM.GetGlobalId(), initialState)
@@ -552,6 +544,22 @@ func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestForCreate(ctx conte
return data, nil
}
func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestSyncHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, iVM cloudprovider.ICloudVM) (cloudprovider.ICloudHost, error) {
if hostId := iVM.GetIHostId(); len(hostId) > 0 {
nh, err := db.FetchByExternalIdAndManagerId(models.HostManager, hostId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
return q.Equals("manager_id", host.ManagerId)
})
if err != nil {
log.Warningf("failed to found new hostId(%s) for ivm %s(%s) error: %v", hostId, guest.Name, guest.Id, err)
} else if nh.GetId() != guest.HostId {
guest.OnScheduleToHost(ctx, userCred, nh.GetId())
host = nh.(*models.SHost)
}
}
return host.GetIHost()
}
func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestForDeploy(ctx context.Context, guest *models.SGuest, ihost cloudprovider.ICloudHost, task taskman.ITask, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error) {
iVM, err := ihost.GetIVMById(guest.GetExternalId())
if err != nil || iVM == nil {
+1
View File
@@ -91,6 +91,7 @@ type IGuestDriver interface {
RequestDeployGuestOnHost(ctx context.Context, guest *SGuest, host *SHost, task taskman.ITask) error
RemoteDeployGuestForCreate(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, host *SHost, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error)
RemoteDeployGuestSyncHost(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, host *SHost, iVM cloudprovider.ICloudVM) (cloudprovider.ICloudHost, error)
RemoteActionAfterGuestCreated(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, host *SHost, iVM cloudprovider.ICloudVM, desc *cloudprovider.SManagedVMCreateConfig)
RemoteDeployGuestForDeploy(ctx context.Context, guest *SGuest, ihost cloudprovider.ICloudHost, task taskman.ITask, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error)
RemoteDeployGuestForRebuildRoot(ctx context.Context, guest *SGuest, ihost cloudprovider.ICloudHost, task taskman.ITask, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error)
+4 -8
View File
@@ -1713,7 +1713,7 @@ func (manager *SHostManager) SyncHosts(ctx context.Context, userCred mcclient.To
}
}
for i := 0; i < len(added); i += 1 {
new, err := manager.newFromCloudHost(ctx, userCred, added[i], provider, zone)
new, err := manager.NewFromCloudHost(ctx, userCred, added[i], provider, zone)
if err != nil {
syncResult.AddError(err)
} else {
@@ -1968,7 +1968,7 @@ func (s *SHost) syncSchedtags(ctx context.Context, userCred mcclient.TokenCreden
return nil
}
func (manager *SHostManager) newFromCloudHost(ctx context.Context, userCred mcclient.TokenCredential, extHost cloudprovider.ICloudHost, provider *SCloudprovider, izone *SZone) (*SHost, error) {
func (manager *SHostManager) NewFromCloudHost(ctx context.Context, userCred mcclient.TokenCredential, extHost cloudprovider.ICloudHost, provider *SCloudprovider, izone *SZone) (*SHost, error) {
host := SHost{}
host.SetModelManager(manager, &host)
@@ -1989,11 +1989,7 @@ func (manager *SHostManager) newFromCloudHost(ctx context.Context, userCred mccl
izone, _ = wire.GetZone()
}
newName, err := db.GenerateName(manager, userCred, extHost.GetName())
if err != nil {
return nil, fmt.Errorf("generate name fail %s", err)
}
host.Name = newName
host.Name = extHost.GetName()
host.ExternalId = extHost.GetGlobalId()
host.ZoneId = izone.Id
@@ -2040,7 +2036,7 @@ func (manager *SHostManager) newFromCloudHost(ctx context.Context, userCred mccl
host.IsPublic = false
host.PublicScope = string(rbacutils.ScopeNone)
err = manager.TableSpec().Insert(ctx, &host)
err := manager.TableSpec().Insert(ctx, &host)
if err != nil {
log.Errorf("newFromCloudHost fail %s", err)
return nil, err
@@ -444,6 +444,9 @@ func (self *SStoragecachedimage) syncRemoveCloudImage(ctx context.Context, userC
func (self *SStoragecachedimage) syncWithCloudImage(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, image cloudprovider.ICloudImage, managerId string) error {
cachedImage := self.GetCachedimage()
if len(self.ExternalId) == 0 {
self.SetExternalId(cachedImage.GetExternalId())
}
if len(cachedImage.ExternalId) > 0 {
self.SetStatus(userCred, image.GetStatus(), "")
return cachedImage.syncWithCloudImage(ctx, userCred, ownerId, image, managerId)
-1
View File
@@ -719,7 +719,6 @@ func (cache *SStoragecache) syncCloudImages(
commondb := make([]SStoragecachedimage, 0)
commonext := make([]cloudprovider.ICloudImage, 0)
added := make([]cloudprovider.ICloudImage, 0)
err := compare.CompareSets(localCachedImages, remoteImages, &removed, &commondb, &commonext, &added)
if err != nil {
syncResult.Error(errors.Wrapf(err, "compare.CompareSets"))
+63 -20
View File
@@ -32,20 +32,31 @@ import (
type SHost struct {
multicloud.SHostBase
zone *SZone
vms []SInstance
// 华为私有云没有直接列出host的接口,所有账号下的host都是通过VM反向解析出来的
// 当账号下没有虚拟机时,如果没有host,会导致调度找不到可用的HOST。
// 因此,为了避免上述情况始终会在每个zone下返回一台虚拟的host
IsFake bool
projectId string
Id string
Name string
}
func (self *SHost) GetId() string {
return fmt.Sprintf("%s-%s", self.zone.region.client.cpcfg.Id, self.zone.GetId())
return self.Id
}
func (self *SHost) GetName() string {
return fmt.Sprintf("%s-%s", self.zone.region.client.cpcfg.Name, self.zone.GetId())
if len(self.Name) > 0 {
return self.Name
}
return self.Id
}
func (self *SHost) GetGlobalId() string {
return fmt.Sprintf("%s-%s", self.zone.region.client.cpcfg.Id, self.zone.GetId())
return self.Id
}
func (self *SHost) GetStatus() string {
@@ -53,36 +64,61 @@ func (self *SHost) GetStatus() string {
}
func (self *SHost) Refresh() error {
return nil
_, err := self.getVMs()
return errors.Wrap(err, "getVMs")
}
func (self *SHost) IsEmulated() bool {
return true
return self.IsFake
}
func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
vms, err := self.zone.region.GetInstances()
if err != nil {
return nil, err
}
filtedVms := make([]SInstance, 0)
for i := range vms {
if vms[i].OSEXTAZAvailabilityZone == self.zone.GetId() {
filtedVms = append(filtedVms, vms[i])
var vms []SInstance
var err error
if self.vms != nil {
vms = self.vms
} else {
vms, err = self.getVMs()
if err != nil {
return nil, err
}
}
ivms := make([]cloudprovider.ICloudVM, len(filtedVms))
for i := 0; i < len(filtedVms); i += 1 {
filtedVms[i].host = self
ivms[i] = &filtedVms[i]
ret := make([]cloudprovider.ICloudVM, len(vms))
for i := range vms {
vm := vms[i]
vm.host = self
ret[i] = &vm
}
return ivms, nil
return ret, nil
}
func (self *SHost) getVMs() ([]SInstance, error) {
vms, err := self.zone.region.GetInstances()
if err != nil {
return nil, errors.Wrap(err, "GetInstances")
}
ret := []SInstance{}
for i := range vms {
vm := vms[i]
if vm.OSEXTAZAvailabilityZone == self.GetId() && vm.HostID == self.GetId() {
vm.host = self
ret = append(ret, vm)
}
}
self.vms = ret
return ret, nil
}
func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
vm, err := self.zone.region.GetInstanceByID(id)
if vm.HostID != self.GetId() {
return nil, errors.Wrap(cloudprovider.ErrNotFound, "GetInstanceByID")
}
vm.host = self
return &vm, err
}
@@ -118,6 +154,8 @@ func (self *SHost) GetAccessMac() string {
func (self *SHost) GetSysInfo() jsonutils.JSONObject {
info := jsonutils.NewDict()
info.Add(jsonutils.NewString(CLOUD_PROVIDER_HUAWEI), "manufacture")
info.Add(jsonutils.NewString(self.GetId()), "id")
info.Add(jsonutils.NewString(self.GetName()), "name")
return info
}
@@ -171,6 +209,10 @@ func (self *SHost) GetInstanceById(instanceId string) (*SInstance, error) {
return nil, err
}
if instance.HostID != self.GetId() {
return nil, errors.Wrap(cloudprovider.ErrNotFound, "GetInstanceByID")
}
instance.host = self
return &instance, nil
}
@@ -188,7 +230,8 @@ func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudpr
return nil, err
}
vm, err := self.GetInstanceById(vmId)
// VM实际调度到的host, 可能不是当前host.因此需要改写host信息
vm, err := self.zone.region.GetIVMById(vmId)
if err != nil {
return nil, err
}
+4
View File
@@ -362,6 +362,10 @@ func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
return self.host
}
func (self *SInstance) GetIHostId() string {
return self.host.GetGlobalId()
}
func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
err := self.Refresh()
if err != nil {
+13 -1
View File
@@ -76,7 +76,7 @@ func (self *SRegion) getECSClient() (*client.Client, error) {
regionId := strings.Split(project.Name, "_")[0]
if regionId != self.ID {
// log.Debugf("project %s not in region %s", self.client.projectId, self.ID)
// log.Debugf("project %s not in region %s", self.client.ProjectId, self.ID)
return nil, errors.Error("region and project mismatch")
}
}
@@ -153,6 +153,18 @@ func (self *SRegion) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
if err != nil {
return nil, err
}
zone, err := self.getZoneById(instance.OSEXTAZAvailabilityZone)
if err != nil {
return nil, errors.Wrap(err, "getZoneById")
}
instance.host = &SHost{
zone: zone,
vms: nil,
projectId: self.client.projectId,
Id: instance.HostID,
Name: instance.OSEXTSRVATTRHost,
}
return &instance, err
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package shell
import (
huawei "yunion.io/x/onecloud/pkg/multicloud/huaweistack"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type HostListOptions struct {
}
shellutils.R(&HostListOptions{}, "host-list", "List hosts", func(cli *huawei.SRegion, args *HostListOptions) error {
hosts, e := cli.GetIHosts()
if e != nil {
return e
}
printList(hosts, 0, 0, 0, []string{})
return nil
})
}
+67 -9
View File
@@ -40,7 +40,7 @@ type SZone struct {
multicloud.SResourceBase
multicloud.HuaweiTags
region *SRegion
host *SHost
ihosts []cloudprovider.ICloudHost
iwires []cloudprovider.ICloudWire
istorages []cloudprovider.ICloudStorage
@@ -81,11 +81,62 @@ func (self *SZone) fetchStorages() error {
return nil
}
func (self *SZone) getHost() *SHost {
if self.host == nil {
self.host = &SHost{zone: self, projectId: self.region.client.projectId}
// 华为私有云没有直接列出host的接口,所有账号下的host都是通过VM反向解析出来的
// 当账号下没有虚拟机时,如果没有host,会导致调度找不到可用的HOST。
// 因此,为了避免上述情况始终会在每个zone下返回一台虚拟的host
func (self *SZone) getEmulatedHost() SHost {
return SHost{
zone: self,
vms: nil,
IsFake: true,
projectId: self.region.client.projectId,
Id: fmt.Sprintf("%s-%s", self.region.client.cpcfg.Id, self.GetId()),
Name: fmt.Sprintf("%s-%s", self.region.client.cpcfg.Name, self.GetId()),
}
return self.host
}
func (self *SZone) getHosts() ([]cloudprovider.ICloudHost, error) {
if self.ihosts != nil {
return self.ihosts, nil
}
vms, err := self.region.GetInstances()
if err != nil {
return nil, errors.Wrap(err, "GetInstances")
}
hosts := map[string]string{}
hostVms := map[string][]SInstance{}
for i := range vms {
vm := vms[i]
if vm.OSEXTAZAvailabilityZone == self.GetId() {
hosts[vm.HostID] = vm.OSEXTSRVATTRHost
if _, ok := hostVms[vm.HostID]; ok {
hostVms[vm.HostID] = append(hostVms[vm.HostID], vm)
} else {
hostVms[vm.HostID] = []SInstance{vm}
}
}
}
fakeHost := self.getEmulatedHost()
ihosts := []cloudprovider.ICloudHost{&fakeHost}
for k, _ := range hosts {
h := SHost{
zone: self,
projectId: self.region.client.projectId,
Id: k,
Name: hosts[k],
}
for i := range hostVms[k] {
hostVms[k][i].host = &h
}
h.vms = hostVms[k]
ihosts = append(ihosts, &h)
}
return ihosts, nil
}
func (self *SZone) GetId() string {
@@ -124,14 +175,21 @@ func (self *SZone) GetIRegion() cloudprovider.ICloudRegion {
}
func (self *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) {
return []cloudprovider.ICloudHost{self.getHost()}, nil
return self.getHosts()
}
func (self *SZone) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
host := self.getHost()
if host.GetGlobalId() == id {
return host, nil
ihosts, err := self.getHosts()
if err != nil {
return nil, errors.Wrap(err, "getHosts")
}
for i := range ihosts {
if ihosts[i].GetGlobalId() == id {
return ihosts[i], nil
}
}
return nil, cloudprovider.ErrNotFound
}