mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-01 15:07:17 +08:00
feat(scheduler): Change the action for guests with backup and instanceGroup
1. 对于主机组的打散功能,放在调度的最后单独处理。 2. 主备机的调度,通过在主机和备机之间设置一个粒度为1的主机组来实现。 3. 虚拟机的调度默认打散,在打散的基础上,选择容量大的宿主机。 3. 主备机中,主机和备机的默认打散流程分散开,互不影响。
This commit is contained in:
@@ -1,156 +0,0 @@
|
||||
// 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 predicates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/scheduler/core"
|
||||
)
|
||||
|
||||
type InstanceGroupPredicate struct {
|
||||
BasePredicate
|
||||
}
|
||||
|
||||
func (p *InstanceGroupPredicate) Name() string {
|
||||
return "instance_group"
|
||||
}
|
||||
|
||||
func (p *InstanceGroupPredicate) Clone() core.FitPredicate {
|
||||
return &InstanceGroupPredicate{}
|
||||
}
|
||||
|
||||
func (p *InstanceGroupPredicate) PreExecute(u *core.Unit, cs []core.Candidater) (bool, error) {
|
||||
schedDate := u.SchedData()
|
||||
if schedDate.InstanceGroupIds == nil || len(schedDate.InstanceGroupIds) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *InstanceGroupPredicate) Execute(u *core.Unit, c core.Candidater) (bool, []core.PredicateFailureReason, error) {
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
type SForcedGroupPredicate struct {
|
||||
InstanceGroupPredicate
|
||||
}
|
||||
|
||||
func (p *SForcedGroupPredicate) Name() string {
|
||||
return "forced_instance_group"
|
||||
}
|
||||
|
||||
func (p *SForcedGroupPredicate) Clone() core.FitPredicate {
|
||||
return &SForcedGroupPredicate{}
|
||||
}
|
||||
|
||||
// SForcedGroupPredicate make sure that there is no more guest with same group whose IsForcedSpe is ture in a host
|
||||
// for all forced groups in u.SchedData.InstanceGroupIds, so that the capacity is the min value of the FreeGroupCounts
|
||||
func (p *SForcedGroupPredicate) Execute(u *core.Unit, c core.Candidater) (bool, []core.PredicateFailureReason, error) {
|
||||
h := NewPredicateHelper(p, u, c)
|
||||
schedDate := u.SchedData()
|
||||
|
||||
instanceGroups := c.Getter().InstanceGroups()
|
||||
minFree := math.MaxInt32
|
||||
for _, id := range schedDate.InstanceGroupIds {
|
||||
detail := schedDate.InstanceGroupsDetail[id]
|
||||
// SForcedGroupPredicate only deal with group whose ForceDispersion is ture
|
||||
if detail.ForceDispersion.IsFalse() {
|
||||
continue
|
||||
}
|
||||
var free int
|
||||
if _, ok := instanceGroups[id]; ok {
|
||||
free, _ = c.Getter().GetFreeGroupCount(id)
|
||||
if free < 1 {
|
||||
h.AppendPredicateFailMsg(fmt.Sprintf(
|
||||
"the number of guests with same instance group '%s' in this host has reached the upper limit",
|
||||
instanceGroups[id].GetName()))
|
||||
minFree = 0
|
||||
break
|
||||
}
|
||||
} else {
|
||||
free = detail.Granularity
|
||||
}
|
||||
if free < minFree {
|
||||
minFree = free
|
||||
}
|
||||
}
|
||||
// chose the min capacity of groups
|
||||
h.SetCapacity(int64(minFree))
|
||||
return h.GetResult()
|
||||
}
|
||||
|
||||
type SUnForcedGroupPredicate struct {
|
||||
InstanceGroupPredicate
|
||||
}
|
||||
|
||||
func (p *SUnForcedGroupPredicate) Name() string {
|
||||
return "unforced_instance_group"
|
||||
}
|
||||
|
||||
func (p *SUnForcedGroupPredicate) Clone() core.FitPredicate {
|
||||
return &SUnForcedGroupPredicate{}
|
||||
}
|
||||
|
||||
func (p *SUnForcedGroupPredicate) PreExecute(u *core.Unit, cs []core.Candidater) (bool, error) {
|
||||
ret, err := p.InstanceGroupPredicate.PreExecute(u, cs)
|
||||
if err != nil || !ret {
|
||||
return ret, err
|
||||
}
|
||||
u.RegisterSelectPriorityUpdater(p.Name(), func(u *core.Unit, origin core.SSelectPriorityValue,
|
||||
hostID string) core.SSelectPriorityValue {
|
||||
|
||||
return origin.SubOne()
|
||||
})
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// SUnForcedGroupPredicate make sure that the guests are assigned to these hosts who has enough FreeGroupCount of
|
||||
// unforced groups, so that it will improve the priority of these hosts meet the conditions and the priority should
|
||||
// be the max value of the FreeGroupCounts
|
||||
func (p *SUnForcedGroupPredicate) Execute(u *core.Unit, c core.Candidater) (bool, []core.PredicateFailureReason,
|
||||
error) {
|
||||
|
||||
h := NewPredicateHelper(p, u, c)
|
||||
schedDate := u.SchedData()
|
||||
|
||||
instanceGroups := c.Getter().InstanceGroups()
|
||||
maxPriority := 0
|
||||
for _, id := range schedDate.InstanceGroupIds {
|
||||
detail := schedDate.InstanceGroupsDetail[id]
|
||||
// SUnForcedGroupPredicate only deal with group whose ForceDispersion is false
|
||||
if detail.ForceDispersion.IsTrue() {
|
||||
continue
|
||||
}
|
||||
var priority int
|
||||
if _, ok := instanceGroups[id]; ok {
|
||||
free, _ := c.Getter().GetFreeGroupCount(id)
|
||||
if free < 1 {
|
||||
priority = 0
|
||||
}
|
||||
priority = free
|
||||
} else {
|
||||
priority = detail.Granularity
|
||||
}
|
||||
if priority > maxPriority {
|
||||
maxPriority = priority
|
||||
}
|
||||
}
|
||||
|
||||
// set priority
|
||||
h.SetSelectPriority(maxPriority)
|
||||
return h.GetResult()
|
||||
}
|
||||
@@ -45,8 +45,6 @@ func defaultPredicates() sets.String {
|
||||
factory.RegisterFitPredicate("m-GuestDiskschedtagFilter", &predicates.DiskSchedtagPredicate{}),
|
||||
factory.RegisterFitPredicate("n-ServerSkuFilter", &predicates.InstanceTypePredicate{}),
|
||||
factory.RegisterFitPredicate("o-GuestNetschedtagFilter", &predicates.NetworkSchedtagPredicate{}),
|
||||
factory.RegisterFitPredicate("p-GuestForcedDispersionFilter", &predicates.SForcedGroupPredicate{}),
|
||||
factory.RegisterFitPredicate("p-GuestUnForcedDispersionFilter", &predicates.SUnForcedGroupPredicate{}),
|
||||
factory.RegisterFitPredicate("z-QuotaFilter", &predicates.SQuotaPredicate{}),
|
||||
)
|
||||
}
|
||||
|
||||
+3
-1
@@ -31,6 +31,8 @@ import (
|
||||
schedmodels "yunion.io/x/onecloud/pkg/scheduler/models"
|
||||
)
|
||||
|
||||
var ErrInstanceGroupNotFound = errors.Error("InstanceGroupNotFound")
|
||||
|
||||
type BaseHostDesc struct {
|
||||
*computemodels.SHost
|
||||
Region *computemodels.SCloudregion `json:"region"`
|
||||
@@ -128,7 +130,7 @@ func (b baseHostGetter) GetFreeGroupCount(groupId string) (int, error) {
|
||||
// Must Be
|
||||
scg, ok := b.h.InstanceGroups[groupId]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("No such Group id")
|
||||
return 0, errors.Wrap(ErrInstanceGroupNotFound, groupId)
|
||||
}
|
||||
free := scg.Granularity - scg.ReferCount
|
||||
if free < 1 {
|
||||
|
||||
@@ -450,17 +450,12 @@ func SelectHosts(unit *Unit, priorityList HostPriorityList) ([]*SelectedCandidat
|
||||
completed:
|
||||
for len(priorityList) > 0 {
|
||||
log.V(10).Debugf("PriorityList: %#v", priorityList)
|
||||
currentPriority := unit.GetMaxSelectPriority()
|
||||
priorityList0 := HostPriorityList{}
|
||||
for _, it := range priorityList {
|
||||
if count <= 0 {
|
||||
break completed
|
||||
}
|
||||
hostID := it.Host
|
||||
if !currentPriority.IsEmpty() && unit.GetSelectPriority(hostID).Less(currentPriority) {
|
||||
priorityList0 = append(priorityList0, it)
|
||||
continue
|
||||
}
|
||||
var (
|
||||
selectedItem *SelectedCandidate
|
||||
ok bool
|
||||
@@ -479,9 +474,6 @@ completed:
|
||||
priorityList0 = append(priorityList0, it)
|
||||
}
|
||||
}
|
||||
if !currentPriority.IsEmpty() {
|
||||
unit.UpdateSelectPriority()
|
||||
}
|
||||
// sort by score
|
||||
priorityList = priorityList0
|
||||
//sort.Sort(sort.Reverse(priorityList))
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
// 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 manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
|
||||
schedapi "yunion.io/x/onecloud/pkg/apis/scheduler"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/scheduler/api"
|
||||
"yunion.io/x/onecloud/pkg/scheduler/cache/candidate"
|
||||
"yunion.io/x/onecloud/pkg/scheduler/core"
|
||||
)
|
||||
|
||||
func transToInstanceGroupSchedResult(result *core.SchedResultItemList, schedInfo *api.SchedInfo) *schedapi.ScheduleOutput {
|
||||
for _, item := range result.Data {
|
||||
item.Count = 0
|
||||
}
|
||||
guestInfos, backGuestInfos, groups := generateGuestInfo(schedInfo)
|
||||
hosts := buildHosts(result, groups)
|
||||
if len(backGuestInfos) > 0 {
|
||||
return getBackupSchedResult(hosts, guestInfos, backGuestInfos, schedInfo.SessionId)
|
||||
}
|
||||
return getSchedResult(hosts, guestInfos, schedInfo.SessionId)
|
||||
}
|
||||
|
||||
type sGuestInfo struct {
|
||||
schedInfo *api.SchedInfo
|
||||
instanceGroupsDetail map[string]*models.SGroup
|
||||
preferHost string
|
||||
}
|
||||
|
||||
type sSchedResultItem struct {
|
||||
*core.SchedResultItem
|
||||
instanceGroupCapacity map[string]int64
|
||||
masterCount int64
|
||||
backupCount int64
|
||||
}
|
||||
|
||||
func buildHosts(result *core.SchedResultItemList, groups map[string]*models.SGroup) []*sSchedResultItem {
|
||||
hosts := make([]*sSchedResultItem, result.Data.Len())
|
||||
for i := 0; i < len(result.Data); i++ {
|
||||
getter := result.Data[i].Candidater.Getter()
|
||||
igCapacity := make(map[string]int64)
|
||||
for id, group := range groups {
|
||||
c, err := getter.GetFreeGroupCount(id)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == candidate.ErrInstanceGroupNotFound {
|
||||
igCapacity[id] = int64(group.Granularity)
|
||||
} else {
|
||||
igCapacity[id] = 0
|
||||
log.Errorf("GetFreeGroupCount: %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
igCapacity[id] = int64(c)
|
||||
}
|
||||
}
|
||||
hosts[i] = &sSchedResultItem{
|
||||
SchedResultItem: result.Data[i],
|
||||
instanceGroupCapacity: igCapacity,
|
||||
}
|
||||
}
|
||||
sortHosts(hosts, nil)
|
||||
return hosts
|
||||
}
|
||||
|
||||
// sortHost sorts the host for guest that is the backup one of the high-availability guest
|
||||
// if isBackup is true and the master one if isBackup is false.
|
||||
func sortHosts(hosts []*sSchedResultItem, isBackup *bool) {
|
||||
sort.Slice(hosts, func(i, j int) bool {
|
||||
var counti, countj int64
|
||||
switch {
|
||||
case isBackup == nil:
|
||||
counti, countj = hosts[i].Count, hosts[j].Count
|
||||
case *isBackup:
|
||||
counti, countj = hosts[i].backupCount, hosts[j].backupCount
|
||||
default:
|
||||
counti, countj = hosts[i].masterCount, hosts[j].masterCount
|
||||
}
|
||||
if counti == countj {
|
||||
return hosts[i].Capacity > hosts[j].Capacity
|
||||
}
|
||||
return counti < countj
|
||||
})
|
||||
}
|
||||
|
||||
// buildWireHosts classify hosts according to their wire
|
||||
func buildWireHosts(hosts []*sSchedResultItem) map[string][]*sSchedResultItem {
|
||||
wireHostMap := make(map[string][]*sSchedResultItem)
|
||||
for _, host := range hosts {
|
||||
networks := host.Candidater.Getter().Networks()
|
||||
for j := 0; j < len(networks); j++ {
|
||||
if hosts, ok := wireHostMap[networks[j].WireId]; ok {
|
||||
if hostsIndex(host.ID, hosts) < 0 {
|
||||
wireHostMap[networks[j].WireId] = append(hosts, host)
|
||||
}
|
||||
} else {
|
||||
wireHostMap[networks[j].WireId] = []*sSchedResultItem{host}
|
||||
}
|
||||
}
|
||||
}
|
||||
return wireHostMap
|
||||
}
|
||||
|
||||
// generateGuestInfo return guestInfos, backupGuestInfos and all instanceGroups
|
||||
func generateGuestInfo(schedInfo *api.SchedInfo) ([]sGuestInfo, []sGuestInfo, map[string]*models.SGroup) {
|
||||
infos := make([]sGuestInfo, 0, schedInfo.Count)
|
||||
infobs := make([]sGuestInfo, 0, schedInfo.Count)
|
||||
groups := make(map[string]*models.SGroup)
|
||||
name := schedInfo.Name
|
||||
if len(name) == 0 {
|
||||
name = "default"
|
||||
}
|
||||
for id, group := range schedInfo.InstanceGroupsDetail {
|
||||
groups[id] = group
|
||||
}
|
||||
for i := 0; i < schedInfo.Count; i++ {
|
||||
info := sGuestInfo{
|
||||
schedInfo: schedInfo,
|
||||
instanceGroupsDetail: make(map[string]*models.SGroup),
|
||||
preferHost: schedInfo.PreferHost,
|
||||
}
|
||||
for id, group := range schedInfo.InstanceGroupsDetail {
|
||||
info.instanceGroupsDetail[id] = group
|
||||
}
|
||||
infos = append(infos, info)
|
||||
if !schedInfo.Backup {
|
||||
continue
|
||||
}
|
||||
infob := sGuestInfo{
|
||||
schedInfo: schedInfo,
|
||||
preferHost: schedInfo.PreferBackupHost,
|
||||
instanceGroupsDetail: make(map[string]*models.SGroup),
|
||||
}
|
||||
infobs = append(infobs, infob)
|
||||
// Virtual an instanceGroup
|
||||
group := models.SGroup{
|
||||
Granularity: 1,
|
||||
ForceDispersion: tristate.True,
|
||||
}
|
||||
groupid := fmt.Sprintf("virtual-%s-%d", name, i)
|
||||
group.Id = groupid
|
||||
infos[i].instanceGroupsDetail[groupid] = &group
|
||||
infobs[i].instanceGroupsDetail[groupid] = &group
|
||||
groups[groupid] = &group
|
||||
}
|
||||
return infos, infobs, groups
|
||||
}
|
||||
|
||||
func hostsIndex(hostId string, hosts []*sSchedResultItem) int {
|
||||
for i := 0; i < len(hosts); i++ {
|
||||
if hosts[i].ID == hostId {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// getBackupSchedResult return the ScheduleOutput for guest without backup
|
||||
func getSchedResult(hosts []*sSchedResultItem, guestInfos []sGuestInfo, sid string) *schedapi.ScheduleOutput {
|
||||
apiResults := make([]*schedapi.CandidateResource, 0)
|
||||
storageUsed := core.NewStorageUsed()
|
||||
var i int = 0
|
||||
for ; i < len(guestInfos); i++ {
|
||||
host := selectHost(hosts, guestInfos[i], nil, true)
|
||||
if host == nil {
|
||||
host = selectHost(hosts, guestInfos[i], nil, false)
|
||||
if host == nil {
|
||||
er := &schedapi.CandidateResource{Error: fmt.Sprintf("no suitable Host for No.%d Guest", i+1)}
|
||||
apiResults = append(apiResults, er)
|
||||
break
|
||||
}
|
||||
}
|
||||
markHostUsed(host, guestInfos[i], nil)
|
||||
tr := host.ToCandidateResource(storageUsed)
|
||||
tr.SessionId = sid
|
||||
apiResults = append(apiResults, tr)
|
||||
}
|
||||
for ; i < len(guestInfos); i++ {
|
||||
er := &schedapi.CandidateResource{Error: fmt.Sprintf("no suitable Host for No.%d Guest", i+1)}
|
||||
apiResults = append(apiResults, er)
|
||||
}
|
||||
ret := new(schedapi.ScheduleOutput)
|
||||
ret.Candidates = apiResults
|
||||
return ret
|
||||
}
|
||||
|
||||
// getBackupSchedResult return the ScheduleOutput for guest with backup
|
||||
func getBackupSchedResult(hosts []*sSchedResultItem, guestInfos, backGuestInfos []sGuestInfo, sid string) *schedapi.ScheduleOutput {
|
||||
wireHostMap := buildWireHosts(hosts)
|
||||
apiResults := make([]*schedapi.CandidateResource, 0, len(guestInfos))
|
||||
nowireIds := sets.NewString()
|
||||
storageUsed := core.NewStorageUsed()
|
||||
isBackup := true
|
||||
isMaster := false
|
||||
for i := 0; i < len(guestInfos); i++ {
|
||||
for wireid, hosts := range wireHostMap {
|
||||
if nowireIds.Has(wireid) {
|
||||
continue
|
||||
}
|
||||
masterItem := selectHost(hosts, guestInfos[i], &isMaster, true)
|
||||
if masterItem == nil {
|
||||
masterItem = selectHost(hosts, guestInfos[i], &isMaster, false)
|
||||
if masterItem == nil {
|
||||
nowireIds.Insert(wireid)
|
||||
continue
|
||||
}
|
||||
}
|
||||
// mark master used for now
|
||||
markHostUsed(masterItem, guestInfos[i], &isMaster)
|
||||
backupItem := selectHost(hosts, backGuestInfos[i], &isBackup, false)
|
||||
if backupItem == nil {
|
||||
nowireIds.Insert(wireid)
|
||||
unMarkHostUsed(masterItem, guestInfos[i], &isMaster)
|
||||
continue
|
||||
}
|
||||
markHostUsed(backupItem, backGuestInfos[i], &isBackup)
|
||||
canRe := masterItem.ToCandidateResource(storageUsed)
|
||||
canRe.BackupCandidate = backupItem.ToCandidateResource(storageUsed)
|
||||
canRe.SessionId = sid
|
||||
canRe.BackupCandidate.SessionId = sid
|
||||
apiResults = append(apiResults, canRe)
|
||||
break
|
||||
}
|
||||
if len(apiResults) == i+1 {
|
||||
continue
|
||||
}
|
||||
er := &schedapi.CandidateResource{Error: fmt.Sprintf("no suitable Host for No.%d Highly available Guest", i+1)}
|
||||
apiResults = append(apiResults, er)
|
||||
}
|
||||
ret := new(schedapi.ScheduleOutput)
|
||||
ret.Candidates = apiResults
|
||||
return ret
|
||||
}
|
||||
|
||||
func markHostUsed(host *sSchedResultItem, guestInfo sGuestInfo, isBackup *bool) {
|
||||
for gid := range guestInfo.instanceGroupsDetail {
|
||||
host.instanceGroupCapacity[gid] = host.instanceGroupCapacity[gid] - 1
|
||||
}
|
||||
host.Capacity--
|
||||
host.Count++
|
||||
if isBackup == nil {
|
||||
return
|
||||
}
|
||||
if *isBackup {
|
||||
host.backupCount++
|
||||
} else {
|
||||
host.masterCount++
|
||||
}
|
||||
}
|
||||
|
||||
// unMarkHostUsed is the reverse operation of markHostUsed
|
||||
func unMarkHostUsed(host *sSchedResultItem, guestInfo sGuestInfo, isBackup *bool) {
|
||||
for gid := range guestInfo.instanceGroupsDetail {
|
||||
host.instanceGroupCapacity[gid] = host.instanceGroupCapacity[gid] + 1
|
||||
}
|
||||
host.Capacity++
|
||||
host.Count--
|
||||
if isBackup == nil {
|
||||
return
|
||||
}
|
||||
if *isBackup {
|
||||
host.backupCount--
|
||||
} else {
|
||||
host.masterCount--
|
||||
}
|
||||
}
|
||||
|
||||
// selectHost select host from hosts for guest described by guestInfo.
|
||||
// If forced is true, all instanceGroups will be forced.
|
||||
// Otherwise, the instanceGroups with ForceDispersion 'false' will be unforced.
|
||||
func selectHost(hosts []*sSchedResultItem, guestInfo sGuestInfo, isBackup *bool, forced bool) *sSchedResultItem {
|
||||
sortHosts(hosts, isBackup)
|
||||
var idx = -1
|
||||
if len(guestInfo.preferHost) > 0 {
|
||||
if idx = hostsIndex(guestInfo.preferHost, hosts); idx < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
var choosed bool
|
||||
Loop:
|
||||
for i, host := range hosts {
|
||||
if idx >= 0 && idx != i {
|
||||
continue
|
||||
}
|
||||
if host.Capacity <= 0 {
|
||||
continue
|
||||
}
|
||||
// check forced instanceGroup
|
||||
for id, group := range guestInfo.instanceGroupsDetail {
|
||||
capacity := host.instanceGroupCapacity[id]
|
||||
checkCapacity := forced || group.ForceDispersion.IsTrue()
|
||||
if checkCapacity && capacity <= 0 {
|
||||
continue Loop
|
||||
}
|
||||
}
|
||||
idx = i
|
||||
choosed = true
|
||||
break
|
||||
}
|
||||
if choosed {
|
||||
return hosts[idx]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -16,7 +16,6 @@ package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"yunion.io/x/log"
|
||||
|
||||
@@ -28,9 +27,8 @@ import (
|
||||
)
|
||||
|
||||
func transToSchedResult(result *core.SchedResultItemList, schedInfo *api.SchedInfo) *schedapi.ScheduleOutput {
|
||||
if schedInfo.Backup {
|
||||
return transToBackupSchedResult(result,
|
||||
schedInfo.PreferHost, schedInfo.PreferBackupHost, int64(schedInfo.Count), schedInfo.SessionId)
|
||||
if schedInfo.Backup || len(schedInfo.InstanceGroupsDetail) > 0 {
|
||||
return transToInstanceGroupSchedResult(result, schedInfo)
|
||||
} else {
|
||||
return transToRegionSchedResult(result.Data, int64(schedInfo.Count), schedInfo.SessionId)
|
||||
}
|
||||
@@ -81,101 +79,6 @@ func transToRegionSchedResult(result core.SchedResultItems, count int64, sid str
|
||||
}
|
||||
}
|
||||
|
||||
func transToBackupSchedResult(
|
||||
result *core.SchedResultItemList, preferMasterHost, preferBackupHost string, count int64, sid string,
|
||||
) *schedapi.ScheduleOutput {
|
||||
// clean each result sched result item's count
|
||||
for _, item := range result.Data {
|
||||
item.Count = 0
|
||||
}
|
||||
|
||||
apiResults := newBackupSchedResult(result, preferMasterHost, preferBackupHost, count, sid)
|
||||
return apiResults
|
||||
}
|
||||
|
||||
func newBackupSchedResult(
|
||||
result *core.SchedResultItemList,
|
||||
preferMasterHost, preferBackupHost string,
|
||||
count int64,
|
||||
sid string,
|
||||
) *schedapi.ScheduleOutput {
|
||||
ret := new(schedapi.ScheduleOutput)
|
||||
apiResults := make([]*schedapi.CandidateResource, 0)
|
||||
storageUsed := core.NewStorageUsed()
|
||||
var wireHostMap map[string]core.SchedResultItems
|
||||
for i := 0; i < int(count); i++ {
|
||||
log.V(10).Debugf("Select backup host from result: %s", result)
|
||||
target, err := getSchedBackupResult(result, preferMasterHost, preferBackupHost, sid, wireHostMap, storageUsed)
|
||||
if err != nil {
|
||||
er := &schedapi.CandidateResource{Error: err.Error()}
|
||||
apiResults = append(apiResults, er)
|
||||
continue
|
||||
}
|
||||
apiResults = append(apiResults, target)
|
||||
}
|
||||
ret.Candidates = apiResults
|
||||
return ret
|
||||
}
|
||||
|
||||
func getSchedBackupResult(
|
||||
result *core.SchedResultItemList,
|
||||
preferMasterHost, preferBackupHost string,
|
||||
sid string, wireHostMap map[string]core.SchedResultItems,
|
||||
storageUsed *core.StorageUsed,
|
||||
) (*schedapi.CandidateResource, error) {
|
||||
if wireHostMap == nil {
|
||||
wireHostMap = buildWireHostMap(result)
|
||||
} else {
|
||||
reviseWireHostMap(wireHostMap)
|
||||
}
|
||||
|
||||
masterHost, backupHost := selectHosts(wireHostMap, preferMasterHost, preferBackupHost)
|
||||
if masterHost == nil {
|
||||
return nil, fmt.Errorf("Can't find master host %q", preferMasterHost)
|
||||
}
|
||||
if backupHost == nil {
|
||||
return nil, fmt.Errorf("Can't find backup host %q by master %q", preferBackupHost, masterHost.ID)
|
||||
}
|
||||
|
||||
markHostUsed(masterHost)
|
||||
markHostUsed(backupHost)
|
||||
|
||||
ret := masterHost.ToCandidateResource(storageUsed)
|
||||
ret.BackupCandidate = backupHost.ToCandidateResource(storageUsed)
|
||||
ret.SessionId = sid
|
||||
ret.BackupCandidate.SessionId = sid
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func buildWireHostMap(result *core.SchedResultItemList) map[string]core.SchedResultItems {
|
||||
sort.Sort(sort.Reverse(result.Data))
|
||||
wireHostMap := make(map[string]core.SchedResultItems)
|
||||
for i := 0; i < len(result.Data); i++ {
|
||||
networks := result.Data[i].Candidater.Getter().Networks()
|
||||
for j := 0; j < len(networks); j++ {
|
||||
if hosts, ok := wireHostMap[networks[j].WireId]; ok {
|
||||
if hostInResultItemsIndex(result.Data[i].ID, hosts) < 0 {
|
||||
wireHostMap[networks[j].WireId] = append(hosts, result.Data[i])
|
||||
}
|
||||
} else {
|
||||
wireHostMap[networks[j].WireId] = core.SchedResultItems{result.Data[i]}
|
||||
}
|
||||
}
|
||||
}
|
||||
return wireHostMap
|
||||
}
|
||||
|
||||
func reviseWireHostMap(wireHostMap map[string]core.SchedResultItems) {
|
||||
for _, hosts := range wireHostMap {
|
||||
sort.Sort(sort.Reverse(hosts))
|
||||
}
|
||||
}
|
||||
|
||||
func markHostUsed(host *core.SchedResultItem) {
|
||||
host.Count++
|
||||
host.Capacity--
|
||||
}
|
||||
|
||||
func hostInResultItemsIndex(hostId string, hosts core.SchedResultItems) int {
|
||||
for i := 0; i < len(hosts); i++ {
|
||||
if hosts[i].ID == hostId {
|
||||
@@ -185,75 +88,6 @@ func hostInResultItemsIndex(hostId string, hosts core.SchedResultItems) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func selectHosts(
|
||||
wireHostMap map[string]core.SchedResultItems, preferMasterHost, preferBackupHost string,
|
||||
) (*core.SchedResultItem, *core.SchedResultItem) {
|
||||
var scroe int64
|
||||
var masterIdx, backupIdx int
|
||||
var selectedWireId string
|
||||
for wireId, hosts := range wireHostMap {
|
||||
masterIdx, backupIdx = -1, -1
|
||||
if len(hosts) < 2 {
|
||||
continue
|
||||
}
|
||||
if len(preferMasterHost) > 0 {
|
||||
if masterIdx = hostInResultItemsIndex(preferMasterHost, hosts); masterIdx < 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(preferBackupHost) > 0 {
|
||||
if backupIdx = hostInResultItemsIndex(preferBackupHost, hosts); backupIdx < 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// select master host index
|
||||
if masterIdx < 0 {
|
||||
for i := 0; i < len(hosts); i++ {
|
||||
if hosts[i].ID != preferBackupHost {
|
||||
masterIdx = i
|
||||
}
|
||||
}
|
||||
}
|
||||
if hosts[masterIdx].Capacity <= 0 {
|
||||
if len(preferMasterHost) > 0 {
|
||||
// in case prefer master host capacity isn't enough
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// select backup host index
|
||||
if backupIdx < 0 {
|
||||
for i := 0; i < len(hosts); i++ {
|
||||
if i != masterIdx {
|
||||
backupIdx = i
|
||||
}
|
||||
}
|
||||
}
|
||||
if hosts[backupIdx].Capacity <= 0 {
|
||||
if len(preferBackupHost) > 0 {
|
||||
// in case perfer backup host capacity isn't enough
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// the highest total score wins
|
||||
curScore := hosts[masterIdx].Capacity + hosts[backupIdx].Capacity
|
||||
if curScore > scroe {
|
||||
selectedWireId = wireId
|
||||
scroe = curScore
|
||||
}
|
||||
}
|
||||
if len(selectedWireId) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return wireHostMap[selectedWireId][masterIdx], wireHostMap[selectedWireId][backupIdx]
|
||||
}
|
||||
|
||||
func transToSchedTestResult(result *core.SchedResultItemList, limit int64) interface{} {
|
||||
return &api.SchedTestResult{
|
||||
Data: result.Data,
|
||||
|
||||
Reference in New Issue
Block a user