diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index d4165a87c7..4fe2fcf682 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -248,6 +248,8 @@ type ICloudHost interface { CreateVM(desc *SManagedVMCreateConfig) (ICloudVM, error) GetIHostNics() ([]ICloudHostNetInterface, error) + + GetSchedtags() ([]string, error) } type ICloudVM interface { diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index 756b6846f9..698706c58c 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -33,6 +33,7 @@ import ( "yunion.io/x/pkg/util/fileutils" "yunion.io/x/pkg/util/netutils" "yunion.io/x/pkg/util/regutils" + "yunion.io/x/pkg/util/sets" "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" @@ -1735,6 +1736,11 @@ func (self *SHost) syncWithCloudHost(ctx context.Context, userCred mcclient.Toke self.SyncShareState(ctx, userCred, provider.getAccountShareInfo()) } + if err := self.syncSchedtags(ctx, userCred, extHost); err != nil { + log.Errorf("syncSchedtags fail: %v", err) + return err + } + if err := HostManager.ClearSchedDescCache(self.Id); err != nil { log.Errorf("ClearSchedDescCache for host %s error %v", self.Name, err) } @@ -1766,6 +1772,102 @@ func (self *SHost) syncWithCloudPrepaidVM(extVM cloudprovider.ICloudVM, host *SH return err } +var ( + METADATA_EXT_SCHEDTAG_KEY = "ext:schedtag" + METADATA_EXT_SCHEDTAG_VALUE = "yes" +) + +func (s *SHost) syncSchedtags(ctx context.Context, userCred mcclient.TokenCredential, extHost cloudprovider.ICloudHost) error { + stq := SchedtagManager.Query() + subq := HostschedtagManager.Query("schedtag_id").Equals("host_id", s.Id).SubQuery() + stq = stq.Join(subq, sqlchemy.Equals(stq.Field("id"), subq.Field("schedtag_id"))) + schedtags := make([]SSchedtag, 0) + err := db.FetchModelObjects(SchedtagManager, stq, &schedtags) + if err != nil { + return errors.Wrap(err, "db.FetchModelObjects") + } + extSchedtagStrs, err := extHost.GetSchedtags() + if err != nil { + return errors.Wrap(err, "extHost.GetSchedtags") + } + extStStrSet := sets.NewString(extSchedtagStrs...) + removed := make([]*SSchedtag, 0) + removedIds := make([]string, 0) + for i := range schedtags { + stag := &schedtags[i] + if v := stag.GetMetadata(METADATA_EXT_SCHEDTAG_KEY, userCred); v != METADATA_EXT_SCHEDTAG_VALUE { + continue + } + if !extStStrSet.Has(stag.GetName()) { + removed = append(removed, stag) + removedIds = append(removedIds, stag.GetId()) + } else { + extStStrSet.Delete(stag.GetName()) + } + } + added := extStStrSet.UnsortedList() + + for _, stStr := range added { + st := &SSchedtag{ + ResourceType: HostManager.KeywordPlural(), + } + st.DomainId = s.DomainId + st.Name = stStr + st.Description = "Sync from cloud" + err := SchedtagManager.TableSpec().Insert(ctx, st) + if err != nil { + return errors.Wrapf(err, "unable to create schedtag %q", stStr) + } + st.SetModelManager(SchedtagManager, st) + st.SetMetadata(ctx, METADATA_EXT_SCHEDTAG_KEY, METADATA_EXT_SCHEDTAG_VALUE, userCred) + // attach + hostschedtag := &SHostschedtag{ + HostId: s.GetId(), + } + hostschedtag.SchedtagId = st.GetId() + err = HostschedtagManager.TableSpec().Insert(ctx, hostschedtag) + if err != nil { + return errors.Wrapf(err, "unable to create hostschedtag for tag %q host %q", stStr, s.GetId()) + } + } + + if len(removedIds) == 0 { + return nil + } + + q := HostschedtagManager.Query().Equals("host_id", s.GetId()).In("schedtag_id", removedIds) + hostschedtags := make([]SHostschedtag, 0, len(removedIds)) + err = db.FetchModelObjects(HostschedtagManager, q, &hostschedtags) + if err != nil { + return errors.Wrap(err, "db.FetchModelObject") + } + for i := range hostschedtags { + err = hostschedtags[i].Detach(ctx, userCred) + if err != nil { + return errors.Wrapf(err, "unable to detach host %q and schedtag %q", hostschedtags[i].HostId, hostschedtags[i].SchedtagId) + } + } + + // try to clean + for _, tag := range removed { + cnt, err := tag.GetObjectCount() + if err != nil { + log.Errorf("unable to GetObjectCount for schedtag %q: %v", tag.GetName(), err) + continue + } + if cnt > 0 { + continue + } + err = tag.Delete(ctx, userCred) + if err != nil { + log.Errorf("unable to delete schedtag %q: %v", tag.GetName(), err) + continue + } + } + + return nil +} + 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) @@ -1844,6 +1946,11 @@ func (manager *SHostManager) newFromCloudHost(ctx context.Context, userCred mccl SyncCloudDomain(userCred, &host, provider.GetOwnerId()) + if err := host.syncSchedtags(ctx, userCred, extHost); err != nil { + log.Errorf("newFromCloudHost fail in syncSchedtags %v", err) + return nil, err + } + if provider != nil { host.SyncShareState(ctx, userCred, provider.getAccountShareInfo()) } diff --git a/pkg/multicloud/esxi/cluster.go b/pkg/multicloud/esxi/cluster.go index 93c2dc6045..46de59ed49 100644 --- a/pkg/multicloud/esxi/cluster.go +++ b/pkg/multicloud/esxi/cluster.go @@ -104,3 +104,7 @@ func (cluster *SCluster) SyncResourcePool(name string) (*mo.ResourcePool, error) } return cluster.CreateResourcePool(name) } + +func (cluster *SCluster) getoCluster() *mo.ClusterComputeResource { + return cluster.object.(*mo.ClusterComputeResource) +} diff --git a/pkg/multicloud/esxi/datacenter.go b/pkg/multicloud/esxi/datacenter.go index 5ad5e2a536..a910c98797 100644 --- a/pkg/multicloud/esxi/datacenter.go +++ b/pkg/multicloud/esxi/datacenter.go @@ -38,6 +38,7 @@ type SDatacenter struct { istorages []cloudprovider.ICloudStorage inetworks []IVMNetwork iresoucePool []cloudprovider.ICloudProject + clusters []*SCluster Name string } @@ -148,6 +149,9 @@ func (dc *SDatacenter) GetCluster(cluster string) (*SCluster, error) { } func (dc *SDatacenter) listClusters() ([]*SCluster, error) { + if dc.clusters != nil { + return dc.clusters, nil + } clusters := []mo.ClusterComputeResource{} err := dc.manager.scanMObjects(dc.object.Entity().Self, RESOURCEPOOL_PROPS, &clusters) if err != nil { diff --git a/pkg/multicloud/esxi/host.go b/pkg/multicloud/esxi/host.go index 6fb41abdfd..2be54d2b15 100644 --- a/pkg/multicloud/esxi/host.go +++ b/pkg/multicloud/esxi/host.go @@ -128,6 +128,29 @@ func (self *SHost) GetMetadata() *jsonutils.JSONDict { return nil } +func (self *SHost) GetSchedtags() ([]string, error) { + clusters, err := self.datacenter.listClusters() + if err != nil { + return nil, err + } + reference := self.GetoHostSystem().Reference() + tags := make([]string, 0, 1) +Loop: + for i := range clusters { + oc := clusters[i].getoCluster() + if len(oc.Host) == 0 { + continue + } + for _, h := range oc.Host { + if h == reference { + tags = append(tags, fmt.Sprintf("cluster:%s", oc.Name)) + continue Loop + } + } + } + return tags, nil +} + func (self *SHost) getHostSystem() *mo.HostSystem { return self.object.(*mo.HostSystem) } diff --git a/pkg/multicloud/esxi/resourcepool.go b/pkg/multicloud/esxi/resourcepool.go index 372ddefaba..85fbbfe8ce 100644 --- a/pkg/multicloud/esxi/resourcepool.go +++ b/pkg/multicloud/esxi/resourcepool.go @@ -24,7 +24,7 @@ import ( "yunion.io/x/onecloud/pkg/multicloud" ) -var RESOURCEPOOL_PROPS = []string{"name", "parent"} +var RESOURCEPOOL_PROPS = []string{"name", "parent", "host"} type SResourcePool struct { multicloud.SResourceBase diff --git a/pkg/multicloud/host_base.go b/pkg/multicloud/host_base.go index 214d6d6b45..e7209223b8 100644 --- a/pkg/multicloud/host_base.go +++ b/pkg/multicloud/host_base.go @@ -29,3 +29,7 @@ func (self *SHostBase) GetMemCmtbound() float32 { func (self *SHostBase) GetReservedMemoryMb() int { return 0 } + +func (self *SHostBase) GetSchedtags() ([]string, error) { + return nil, nil +}