mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-08 14:37:55 +08:00
332 lines
9.2 KiB
Go
332 lines
9.2 KiB
Go
// 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 (
|
|
"context"
|
|
"fmt"
|
|
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/util/netutils"
|
|
"yunion.io/x/pkg/util/rbacscope"
|
|
"yunion.io/x/pkg/util/sets"
|
|
"yunion.io/x/pkg/utils"
|
|
|
|
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
|
"yunion.io/x/onecloud/pkg/compute/models"
|
|
"yunion.io/x/onecloud/pkg/scheduler/algorithm/plugin"
|
|
"yunion.io/x/onecloud/pkg/scheduler/api"
|
|
"yunion.io/x/onecloud/pkg/scheduler/core"
|
|
)
|
|
|
|
// NetworkPredicate will filter the current network information with
|
|
// the specified scheduling information to match, if not specified will
|
|
// randomly match the available network resources.
|
|
type NetworkPredicate struct {
|
|
BasePredicate
|
|
plugin.BasePlugin
|
|
NetworkNicCountGetter INetworkNicCountGetter
|
|
networkFreePortCount map[string]int
|
|
}
|
|
|
|
func NewNetworkPredicate() *NetworkPredicate {
|
|
return &NetworkPredicate{
|
|
NetworkNicCountGetter: nil,
|
|
networkFreePortCount: make(map[string]int),
|
|
}
|
|
}
|
|
|
|
func NewNetworkPredicateWithNicCounter() *NetworkPredicate {
|
|
return NewNetworkPredicate().WithNetworkCountGetter(models.GetNetworkManager())
|
|
}
|
|
|
|
func (p *NetworkPredicate) WithNetworkCountGetter(getter INetworkNicCountGetter) *NetworkPredicate {
|
|
p.NetworkNicCountGetter = getter
|
|
return p
|
|
}
|
|
|
|
func (p *NetworkPredicate) Name() string {
|
|
return "host_network"
|
|
}
|
|
|
|
func (p *NetworkPredicate) Clone() core.FitPredicate {
|
|
return &NetworkPredicate{
|
|
NetworkNicCountGetter: p.NetworkNicCountGetter,
|
|
networkFreePortCount: p.networkFreePortCount,
|
|
}
|
|
}
|
|
|
|
type INetworkNicCountGetter interface {
|
|
GetTotalNicCount([]string) (map[string]int, error)
|
|
}
|
|
|
|
func (p *NetworkPredicate) PreExecute(ctx context.Context, u *core.Unit, cs []core.Candidater) (bool, error) {
|
|
data := u.SchedData()
|
|
if len(data.Networks) == 0 {
|
|
return false, nil
|
|
}
|
|
networkIds := sets.NewString()
|
|
for i := range cs {
|
|
for _, net := range cs[i].Getter().Networks() {
|
|
networkIds.Insert(net.GetId())
|
|
}
|
|
}
|
|
|
|
if p.NetworkNicCountGetter != nil {
|
|
netCounts, err := p.NetworkNicCountGetter.GetTotalNicCount(networkIds.UnsortedList())
|
|
if err != nil {
|
|
return false, errors.Wrap(err, "unable to GetTotalNicCount")
|
|
}
|
|
for i := range cs {
|
|
for _, net := range cs[i].Getter().Networks() {
|
|
p.networkFreePortCount[net.Id] = net.GetTotalAddressCount() - netCounts[net.Id]
|
|
}
|
|
}
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func IsNetworksAvailable(ctx context.Context, c core.Candidater, data *api.SchedInfo, req *computeapi.NetworkConfig, networks []*api.CandidateNetwork, netTypes []string, getFreePort func(string) int) (int, []core.PredicateFailureReason) {
|
|
var fullErrMsgs []core.PredicateFailureReason
|
|
var freeCnt int
|
|
|
|
if len(networks) == 0 {
|
|
return 0, []core.PredicateFailureReason{FailReason{Reason: ErrNoAvailableNetwork}}
|
|
}
|
|
|
|
ovnCapable := c.Getter().OvnCapable()
|
|
ovnNetworks := []*api.CandidateNetwork{}
|
|
for i := len(networks) - 1; i >= 0; i -= 1 {
|
|
net := networks[i]
|
|
if net.Provider == computeapi.CLOUD_PROVIDER_ONECLOUD || net.Provider == computeapi.CLOUD_PROVIDER_CLOUDPODS {
|
|
networks = append(networks[:i], networks[i+1:]...)
|
|
ovnNetworks = append(ovnNetworks, net)
|
|
}
|
|
}
|
|
|
|
checkNets := func(tmpNets []*api.CandidateNetwork) {
|
|
for _, n := range tmpNets {
|
|
if errMsg := IsNetworkAvailable(ctx, c, data, req, n, netTypes, getFreePort); errMsg != nil {
|
|
fullErrMsgs = append(fullErrMsgs, errMsg)
|
|
} else {
|
|
freeCnt = freeCnt + getFreePort(n.GetId())
|
|
}
|
|
}
|
|
}
|
|
|
|
checkNets(networks)
|
|
|
|
if ovnCapable {
|
|
checkNets(ovnNetworks)
|
|
}
|
|
|
|
// reuse network
|
|
if data.ReuseNetwork {
|
|
return freeCnt, nil
|
|
}
|
|
|
|
if freeCnt <= 0 {
|
|
return freeCnt, fullErrMsgs
|
|
}
|
|
if freeCnt < data.Count {
|
|
fullErrMsgs = append(fullErrMsgs, FailReason{
|
|
Reason: fmt.Sprintf("total random ports not enough, free: %d, required: %d", freeCnt, data.Count),
|
|
Type: NetworkFreeCount,
|
|
})
|
|
}
|
|
return freeCnt, nil
|
|
}
|
|
|
|
func checkSriovNic(
|
|
c core.Candidater, netWireId string, dev *computeapi.IsolatedDeviceConfig,
|
|
) error {
|
|
if dev.WireId != "" {
|
|
if dev.WireId != netWireId {
|
|
return fmt.Errorf("Network wire not matched sriov nic wire %s != %s", netWireId, dev.WireId)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
getter := c.Getter()
|
|
devs := getter.UnusedIsolatedDevicesByModelAndWire(dev.Model, netWireId)
|
|
if len(devs) == 0 {
|
|
return fmt.Errorf("Network wire no sriov nic available")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func IsNetworkAvailable(
|
|
ctx context.Context,
|
|
c core.Candidater, data *api.SchedInfo,
|
|
req *computeapi.NetworkConfig, n *api.CandidateNetwork,
|
|
netTypes []string, getFreePort func(string) int,
|
|
) core.PredicateFailureReason {
|
|
address := req.Address
|
|
private := req.Private
|
|
exit := req.Exit
|
|
wire := req.Wire
|
|
|
|
isMatchServerType := func(network *models.SNetwork) bool {
|
|
return utils.IsInStringArray(network.ServerType, netTypes)
|
|
}
|
|
|
|
isMigrate := func() bool {
|
|
return len(data.HostId) > 0
|
|
}
|
|
|
|
if n.IsExitNetwork() != exit {
|
|
return FailReason{
|
|
Reason: fmt.Sprintf("%v(%v): %s", n.Name, n.Id, ErrExitIsNotMatch),
|
|
}
|
|
}
|
|
|
|
if getFreePort == nil {
|
|
getFreePort = c.Getter().GetFreePort
|
|
}
|
|
|
|
if !(getFreePort(n.GetId()) > 0 || isMigrate()) {
|
|
return FailReason{
|
|
Reason: fmt.Sprintf("%v(%v): ports use up", n.Name, n.Id),
|
|
Type: NetworkPort,
|
|
}
|
|
}
|
|
|
|
if wire != "" && !utils.HasPrefix(wire, n.WireId) {
|
|
_wire, _ := n.GetWire()
|
|
if !utils.HasPrefix(wire, _wire.GetName()) {
|
|
return FailReason{
|
|
Reason: fmt.Sprintf("Wire %s != %s", wire, n.WireId),
|
|
Type: NetworkWire,
|
|
}
|
|
}
|
|
}
|
|
|
|
if req.SriovDevice != nil && n.VpcId == computeapi.DEFAULT_VPC_ID {
|
|
err := checkSriovNic(c, n.WireId, req.SriovDevice)
|
|
if err != nil {
|
|
return FailReason{
|
|
Reason: err.Error(),
|
|
Type: NetworkWire,
|
|
}
|
|
}
|
|
}
|
|
|
|
if n.IsPublic && n.PublicScope == string(rbacscope.ScopeSystem) {
|
|
// system-wide share
|
|
} else if n.IsPublic && n.PublicScope == string(rbacscope.ScopeDomain) && (n.DomainId == data.Domain || utils.IsInStringArray(data.Domain, n.GetSharedDomains())) {
|
|
// domain-wide share
|
|
} else if n.PublicScope == string(rbacscope.ScopeProject) && utils.IsInStringArray(data.Project, n.GetSharedProjects()) {
|
|
// project-wide share
|
|
} else if n.ProjectId == data.Project {
|
|
// owner
|
|
} else if db.IsAdminAllowGet(ctx, data.UserCred, n) {
|
|
// system admin, can do anything
|
|
} else if db.IsDomainAllowGet(ctx, data.UserCred, n) && data.UserCred.GetProjectDomainId() == n.DomainId {
|
|
// domain admin, can do anything with domain network
|
|
} else {
|
|
return FailReason{
|
|
Reason: fmt.Sprintf("Network %s not accessible", n.Name),
|
|
Type: NetworkOwnership,
|
|
}
|
|
}
|
|
|
|
if private && n.IsPublic {
|
|
return FailReason{
|
|
Reason: fmt.Sprintf("Network %s is public", n.Name),
|
|
Type: NetworkPublic,
|
|
}
|
|
}
|
|
|
|
if req.Network != "" {
|
|
if !(req.Network == n.GetId() || req.Network == n.GetName()) {
|
|
return FailReason{
|
|
Reason: fmt.Sprintf("%v(%v): id/name not matched", n.Name, n.Id),
|
|
Type: NetworkMatch,
|
|
}
|
|
}
|
|
} else {
|
|
if !isMatchServerType(n.SNetwork) {
|
|
return FailReason{
|
|
Reason: fmt.Sprintf("Network %s type %s match", n.Name, n.ServerType),
|
|
Type: NetworkTypeMatch,
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if req.Network == "" && n.IsAutoAlloc.IsFalse() {
|
|
return FailReason{Reason: fmt.Sprintf("Network %s is not auto alloc", n.Name), Type: NetworkPrivate}
|
|
}
|
|
|
|
checkAddress := func(addr string, net *models.SNetwork) error {
|
|
if len(addr) == 0 {
|
|
return nil
|
|
}
|
|
ipAddr, err := netutils.NewIPV4Addr(addr)
|
|
if err != nil {
|
|
return fmt.Errorf("Invalid ip address %s: %v", addr, err)
|
|
}
|
|
if !net.GetIPRange().Contains(ipAddr) {
|
|
return fmt.Errorf("Address %s not in network %s range", addr, net.Name)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if err := checkAddress(address, n.SNetwork); err != nil {
|
|
return FailReason{Reason: err.Error(), Type: NetworkRange}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *NetworkPredicate) GetNetworkTypes(u *core.Unit, specifyType string) []string {
|
|
netTypes := p.GetHypervisorDriver(u).GetRandomNetworkTypes()
|
|
if len(specifyType) > 0 {
|
|
netTypes = []string{specifyType}
|
|
}
|
|
return netTypes
|
|
}
|
|
|
|
func (p *NetworkPredicate) Execute(ctx context.Context, u *core.Unit, c core.Candidater) (bool, []core.PredicateFailureReason, error) {
|
|
h := NewPredicateHelper(p, u, c)
|
|
|
|
getter := c.Getter()
|
|
networks := getter.Networks()
|
|
d := u.SchedData()
|
|
|
|
getFreePort := func(id string) int {
|
|
if _, ok := p.networkFreePortCount[id]; ok {
|
|
return p.networkFreePortCount[id] - c.Getter().GetPendingUsage().NetUsage.Get(id)
|
|
}
|
|
return c.Getter().GetFreePort(id)
|
|
}
|
|
|
|
for _, reqNet := range d.Networks {
|
|
netTypes := p.GetNetworkTypes(u, reqNet.NetType)
|
|
freePortCnt, errs := IsNetworksAvailable(ctx, c, d, reqNet, networks, netTypes, getFreePort)
|
|
if len(errs) > 0 {
|
|
h.ExcludeByErrors(errs)
|
|
return h.GetResult()
|
|
}
|
|
if !d.ReuseNetwork {
|
|
h.SetCapacity(int64(freePortCnt))
|
|
}
|
|
}
|
|
|
|
return h.GetResult()
|
|
}
|