// 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" "sync" "yunion.io/x/pkg/util/netutils" "yunion.io/x/pkg/util/sets" "yunion.io/x/pkg/utils" computeapi "yunion.io/x/onecloud/pkg/apis/compute" "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" "yunion.io/x/onecloud/pkg/util/rbacutils" ) // 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 SelectedNetworks sync.Map } func (p *NetworkPredicate) Name() string { return "host_network" } func (p *NetworkPredicate) Clone() core.FitPredicate { return &NetworkPredicate{} } func (p *NetworkPredicate) PreExecute(u *core.Unit, cs []core.Candidater) (bool, error) { data := u.SchedData() if len(data.Networks) == 0 { return false, nil } return true, nil } func (p *NetworkPredicate) Execute(u *core.Unit, c core.Candidater) (bool, []core.PredicateFailureReason, error) { h := NewPredicateHelper(p, u, c) getter := c.Getter() ovnCapable := getter.OvnCapable() networks := getter.Networks() ovnNetworks := []*api.CandidateNetwork{} for i := len(networks) - 1; i >= 0; i -= 1 { net := networks[i] if net.Provider == computeapi.CLOUD_PROVIDER_ONECLOUD { networks = append(networks[:i], networks[i+1:]...) ovnNetworks = append(ovnNetworks, net) } } d := u.SchedData() isMigrate := func() bool { return len(d.HostId) > 0 } counterOfNetwork := func(u *core.Unit, n *models.SNetwork, r int) core.Counter { counter := u.CounterManager.GetOrCreate("net:"+n.Id, func() core.Counter { return core.NewNormalCounter(int64(getter.GetFreePort(n.Id) - r)) }) u.SharedResourceManager.Add(n.GetId(), counter) return counter } isMatchServerType := func(network *models.SNetwork) bool { if d.Hypervisor == computeapi.HYPERVISOR_BAREMETAL { return network.ServerType == computeapi.NETWORK_TYPE_BAREMETAL } return sets.NewString( "", computeapi.NETWORK_TYPE_GUEST, computeapi.NETWORK_TYPE_CONTAINER).Has(network.ServerType) } 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 } checkNetCount := func(net *models.SNetwork, reqCount int) (core.Counter, core.PredicateFailureReason) { counter := counterOfNetwork(u, net, 0) if counter.GetCount() < int64(reqCount) { return nil, FailReason{ Reason: fmt.Sprintf("%s: ports not enough, free: %d, required: %d", net.Name, counter.GetCount(), d.Count), Type: NetworkFreeCount, } } return counter, nil } isRandomNetworkAvailable := func(address, domain string, private bool, exit bool, wire string, counters core.MultiCounter) []core.PredicateFailureReason { var fullErrMsgs []core.PredicateFailureReason found := false if len(networks) == 0 { fullErrMsgs = append(fullErrMsgs, &FailReason{ Reason: "Not found random available networks", Type: NetworkMatch, }) return fullErrMsgs } for _, n := range networks { errMsgs := []core.PredicateFailureReason{} appendError := func(msg core.PredicateFailureReason) { errMsgs = append(errMsgs, msg) } if !isMatchServerType(n.SNetwork) { appendError(FailReason{ Reason: fmt.Sprintf("Network %s type %s match", n.Name, n.ServerType), Type: NetworkTypeMatch, }) } if n.IsExitNetwork() != exit { appendError(FailReason{Reason: ErrExitIsNotMatch}) } if !(c.Getter().GetFreePort(n.GetId()) > 0 || isMigrate()) { appendError(FailReason{ Reason: fmt.Sprintf("%v(%v): ports use up", n.Name, n.Id), Type: NetworkPort, }) } if wire != "" && !utils.HasPrefix(wire, n.WireId) && !utils.HasPrefix(wire, n.GetWire().GetName()) { appendError(FailReason{ Reason: fmt.Sprintf("Wire %s != %s", wire, n.WireId), Type: NetworkWire, }) } schedData := u.SchedData() if n.IsPublic && n.PublicScope == string(rbacutils.ScopeSystem) { // system-wide share } else if n.IsPublic && n.PublicScope == string(rbacutils.ScopeDomain) && (n.DomainId == schedData.Domain || utils.IsInStringArray(schedData.Domain, n.GetSharedDomains())) { // domain-wide share } else if n.PublicScope == string(rbacutils.ScopeProject) && utils.IsInStringArray(schedData.Project, n.GetSharedProjects()) { // project-wide share } else if n.ProjectId == schedData.Project { // owner } else { appendError(FailReason{ Reason: fmt.Sprintf("Network %s not accessible", n.Name), Type: NetworkOwnership, }) } if private { if n.IsPublic { appendError(FailReason{ Reason: fmt.Sprintf("Network %s is public", n.Name), Type: NetworkPublic, }) } /*else if n.ProjectId != schedData.Project && utils.IsInStringArray(schedData.Project, n.GetSharedProjects()) { appendError(FailReason{ Reason: fmt.Sprintf("Network project %s + %v not owner by %s", n.ProjectId, n.GetSharedProjects(), schedData.Project), Type: NetworkOwner, }) }*/ } else { if !n.IsPublic { appendError(FailReason{Reason: fmt.Sprintf("Network %s is private", n.Name), Type: NetworkPrivate}) } /*else if rbacutils.TRbacScope(n.PublicScope) == rbacutils.ScopeDomain { netDomain := n.DomainId reqDomain := domain if netDomain != reqDomain { appendError(FailReason{Reason: fmt.Sprintf("Network %s domain scope %s not owner by %s", n.Name, netDomain, reqDomain), Type: NetworkDomain}) } }*/ } if err := checkAddress(address, n.SNetwork); err != nil { appendError(FailReason{Reason: err.Error(), Type: NetworkRange}) } if len(errMsgs) == 0 { // add resource counter := counterOfNetwork(u, n.SNetwork, 0) p.SelectedNetworks.Store(n.GetId(), counter.GetCount()) counters.Add(counter) found = true } else { fullErrMsgs = append(fullErrMsgs, errMsgs...) } } if counters.GetCount() < int64(d.Count) { found = false fullErrMsgs = append(fullErrMsgs, FailReason{ Reason: fmt.Sprintf("total random ports not enough, free: %d, required: %d", counters.GetCount(), d.Count), Type: NetworkFreeCount, }) } if !found { return fullErrMsgs } return nil } filterByRandomNetwork := func() { counters := core.NewCounters() if errMsg := isRandomNetworkAvailable("", u.SchedData().Domain, false, false, "", counters); len(errMsg) != 0 { h.ExcludeByErrors(errMsg) } h.SetCapacityCounter(counters) } isNetworkAvaliable := func(n *computeapi.NetworkConfig, counters *core.MinCounters, networks []*api.CandidateNetwork) []core.PredicateFailureReason { errMsgs := make([]core.PredicateFailureReason, 0) if len(networks) == 0 { errMsgs = append(errMsgs, &FailReason{ Reason: ErrNoAvailableNetwork, Type: NetworkMatch, }) return errMsgs } for _, net := range networks { if !(n.Network == net.GetId() || n.Network == net.GetName()) { errMsgs = append(errMsgs, &FailReason{ Reason: fmt.Sprintf("%v(%v): id/name not matched", net.Name, net.Id), Type: NetworkMatch, }) continue } if !(c.Getter().GetFreePort(net.GetId()) > 0 || isMigrate()) { errMsgs = append(errMsgs, &FailReason{ Reason: fmt.Sprintf("%v(%v): ports use up", net.Name, net.Id), Type: NetworkPort, }) continue } counter, err := checkNetCount(net.SNetwork, d.Count) if err != nil { errMsgs = append(errMsgs, err) continue } p.SelectedNetworks.Store(net.Id, counter.GetCount()) counters.Add(counter) return nil } return errMsgs } filterBySpecifiedNetworks := func() { counters := core.NewMinCounters() var errMsgs []core.PredicateFailureReason for _, n := range d.Networks { if len(networks) == 0 && len(ovnNetworks) == 0 { errMsgs = append(errMsgs, FailReason{ Reason: ErrNoAvailableNetwork, }) continue } if n.Network == "" { counters0 := core.NewCounters() retMsg := isRandomNetworkAvailable(n.Address, n.Domain, n.Private, n.Exit, n.Wire, counters0) counters.Add(counters0) errMsgs = append(errMsgs, retMsg...) continue } var availCheckErrs []core.PredicateFailureReason if errMsg := isNetworkAvaliable(n, counters, networks); len(errMsg) == 0 { continue } else { availCheckErrs = append(availCheckErrs, errMsg...) } if ovnCapable { if errMsg := isNetworkAvaliable(n, counters, ovnNetworks); len(errMsg) == 0 { continue } else { availCheckErrs = append(availCheckErrs, errMsg...) } } errMsgs = append(errMsgs, availCheckErrs...) } if len(errMsgs) > 0 { h.ExcludeByErrors(errMsgs) } else { h.SetCapacityCounter(counters) } } if len(d.Networks) == 0 { filterByRandomNetwork() } else { filterBySpecifiedNetworks() } return h.GetResult() }