network_predicate.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package predicates
  15. import (
  16. "context"
  17. "fmt"
  18. "yunion.io/x/pkg/errors"
  19. "yunion.io/x/pkg/util/netutils"
  20. "yunion.io/x/pkg/util/rbacscope"
  21. "yunion.io/x/pkg/util/sets"
  22. "yunion.io/x/pkg/utils"
  23. computeapi "yunion.io/x/onecloud/pkg/apis/compute"
  24. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  25. "yunion.io/x/onecloud/pkg/compute/models"
  26. "yunion.io/x/onecloud/pkg/scheduler/algorithm/plugin"
  27. "yunion.io/x/onecloud/pkg/scheduler/api"
  28. "yunion.io/x/onecloud/pkg/scheduler/core"
  29. schedmodels "yunion.io/x/onecloud/pkg/scheduler/models"
  30. )
  31. // NetworkPredicate will filter the current network information with
  32. // the specified scheduling information to match, if not specified will
  33. // randomly match the available network resources.
  34. type NetworkPredicate struct {
  35. BasePredicate
  36. plugin.BasePlugin
  37. NetworkNicCountGetter INetworkNicCountGetter
  38. networkFreePortCount map[string]int
  39. }
  40. func NewNetworkPredicate() *NetworkPredicate {
  41. return &NetworkPredicate{
  42. NetworkNicCountGetter: nil,
  43. networkFreePortCount: make(map[string]int),
  44. }
  45. }
  46. func NewNetworkPredicateWithNicCounter() *NetworkPredicate {
  47. return NewNetworkPredicate().WithNetworkCountGetter(models.GetNetworkManager())
  48. }
  49. func (p *NetworkPredicate) WithNetworkCountGetter(getter INetworkNicCountGetter) *NetworkPredicate {
  50. p.NetworkNicCountGetter = getter
  51. return p
  52. }
  53. func (p *NetworkPredicate) Name() string {
  54. return "host_network"
  55. }
  56. func (p *NetworkPredicate) Clone() core.FitPredicate {
  57. return &NetworkPredicate{
  58. NetworkNicCountGetter: p.NetworkNicCountGetter,
  59. networkFreePortCount: p.networkFreePortCount,
  60. }
  61. }
  62. type INetworkNicCountGetter interface {
  63. GetTotalNicCount([]string) (map[string]int, error)
  64. }
  65. func (p *NetworkPredicate) PreExecute(ctx context.Context, u *core.Unit, cs []core.Candidater) (bool, error) {
  66. data := u.SchedData()
  67. if data.ResetCpuNumaPin {
  68. return false, nil
  69. }
  70. if len(data.Networks) == 0 {
  71. return false, nil
  72. }
  73. networkIds := sets.NewString()
  74. for i := range cs {
  75. for _, net := range cs[i].Getter().Networks() {
  76. networkIds.Insert(net.GetId())
  77. }
  78. }
  79. if p.NetworkNicCountGetter != nil {
  80. netCounts, err := p.NetworkNicCountGetter.GetTotalNicCount(networkIds.UnsortedList())
  81. if err != nil {
  82. return false, errors.Wrap(err, "unable to GetTotalNicCount")
  83. }
  84. for i := range cs {
  85. for _, net := range cs[i].Getter().Networks() {
  86. p.networkFreePortCount[net.Id] = net.GetTotalAddressCount() - netCounts[net.Id]
  87. }
  88. }
  89. }
  90. return true, nil
  91. }
  92. func IsNetworksAvailable(ctx context.Context, c core.Candidater, data *api.SchedInfo, req *computeapi.NetworkConfig, networks []*api.CandidateNetwork, netTypes []computeapi.TNetworkType, getFreePort func(string) int) (int, []core.PredicateFailureReason) {
  93. var fullErrMsgs []core.PredicateFailureReason
  94. var freeCnt int
  95. if len(networks) == 0 {
  96. return 0, []core.PredicateFailureReason{FailReason{Reason: ErrNoAvailableNetwork}}
  97. }
  98. ovnCapable := c.Getter().OvnCapable()
  99. ovnNetworks := []*api.CandidateNetwork{}
  100. hostLocalNetworks := []*api.CandidateNetwork{}
  101. for i := len(networks) - 1; i >= 0; i -= 1 {
  102. net := networks[i]
  103. if net.Provider == computeapi.CLOUD_PROVIDER_ONECLOUD || net.Provider == computeapi.CLOUD_PROVIDER_CLOUDPODS {
  104. networks = append(networks[:i], networks[i+1:]...)
  105. if net.WireId == computeapi.DEFAULT_HOST_LOCAL_WIRE_ID {
  106. hostLocalNetworks = append(hostLocalNetworks, net)
  107. } else {
  108. ovnNetworks = append(ovnNetworks, net)
  109. }
  110. }
  111. }
  112. checkNets := func(tmpNets []*api.CandidateNetwork) {
  113. for _, n := range tmpNets {
  114. if errMsg := IsNetworkAvailable(ctx, c, data, req, n, netTypes, getFreePort); errMsg != nil {
  115. fullErrMsgs = append(fullErrMsgs, errMsg)
  116. } else {
  117. freeCnt = freeCnt + getFreePort(n.GetId())
  118. }
  119. }
  120. }
  121. checkNets(networks)
  122. if ovnCapable {
  123. checkNets(ovnNetworks)
  124. }
  125. if len(hostLocalNetworks) > 0 {
  126. checkNets(hostLocalNetworks)
  127. }
  128. // reuse network
  129. if data.ReuseNetwork {
  130. return freeCnt, nil
  131. }
  132. if freeCnt <= 0 {
  133. return freeCnt, fullErrMsgs
  134. }
  135. if freeCnt < data.Count {
  136. fullErrMsgs = append(fullErrMsgs, FailReason{
  137. Reason: fmt.Sprintf("total random ports not enough, free: %d, required: %d", freeCnt, data.Count),
  138. Type: NetworkFreeCount,
  139. })
  140. }
  141. return freeCnt, nil
  142. }
  143. func checkSriovNic(
  144. c core.Candidater, netWireId string, dev *computeapi.IsolatedDeviceConfig,
  145. ) error {
  146. if dev.WireId != "" {
  147. if dev.WireId != netWireId {
  148. return fmt.Errorf("Network wire not matched sriov nic wire %s != %s", netWireId, dev.WireId)
  149. } else {
  150. return nil
  151. }
  152. }
  153. getter := c.Getter()
  154. devs := getter.UnusedIsolatedDevicesByModelAndWire(dev.Model, netWireId)
  155. if len(devs) == 0 {
  156. return fmt.Errorf("Network wire no sriov nic available")
  157. }
  158. return nil
  159. }
  160. func IsNetworkAvailable(
  161. ctx context.Context,
  162. c core.Candidater, data *api.SchedInfo,
  163. req *computeapi.NetworkConfig, n *api.CandidateNetwork,
  164. netTypes []computeapi.TNetworkType, getFreePort func(string) int,
  165. ) core.PredicateFailureReason {
  166. address := req.Address
  167. private := req.Private
  168. exit := req.Exit
  169. wire := req.Wire
  170. if req.RequireIPv6 && !n.IsSupportIPv6() {
  171. return FailReason{
  172. Reason: fmt.Sprintf("%v(%v): %s", n.Name, n.Id, ErrNotSupportIpv6),
  173. }
  174. }
  175. isMatchServerType := func(network *models.SNetwork) bool {
  176. return computeapi.IsInNetworkTypes(network.ServerType, netTypes)
  177. }
  178. isMigrate := func() bool {
  179. return len(data.HostId) > 0
  180. }
  181. if isExit := n.IsExitNetwork(); isExit && isExit != exit {
  182. return FailReason{
  183. Reason: fmt.Sprintf("%v(%v): %s", n.Name, n.Id, ErrExitIsNotMatch),
  184. }
  185. }
  186. if getFreePort == nil {
  187. getFreePort = c.Getter().GetFreePort
  188. }
  189. if !(getFreePort(n.GetId()) > 0 || isMigrate()) {
  190. return FailReason{
  191. Reason: fmt.Sprintf("%v(%v): ports use up", n.Name, n.Id),
  192. Type: NetworkPort,
  193. }
  194. }
  195. if wire != "" && !utils.HasPrefix(wire, n.WireId) {
  196. _wire, _ := n.GetWire()
  197. if !utils.HasPrefix(wire, _wire.GetName()) {
  198. return FailReason{
  199. Reason: fmt.Sprintf("Wire %s != %s", wire, n.WireId),
  200. Type: NetworkWire,
  201. }
  202. }
  203. }
  204. if req.SriovDevice != nil && n.VpcId == computeapi.DEFAULT_VPC_ID {
  205. err := checkSriovNic(c, n.WireId, req.SriovDevice)
  206. if err != nil {
  207. return FailReason{
  208. Reason: err.Error(),
  209. Type: NetworkWire,
  210. }
  211. }
  212. }
  213. if n.IsPublic && n.PublicScope == string(rbacscope.ScopeSystem) {
  214. // system-wide share
  215. } else if n.IsPublic && n.PublicScope == string(rbacscope.ScopeDomain) && (n.DomainId == data.Domain || utils.IsInStringArray(data.Domain, n.GetSharedDomains())) {
  216. // domain-wide share
  217. } else if n.PublicScope == string(rbacscope.ScopeProject) && utils.IsInStringArray(data.Project, n.GetSharedProjects()) {
  218. // project-wide share
  219. } else if n.ProjectId == data.Project {
  220. // owner
  221. } else if db.IsAdminAllowGet(ctx, data.UserCred, n) {
  222. // system admin, can do anything
  223. } else if db.IsDomainAllowGet(ctx, data.UserCred, n) && data.UserCred.GetProjectDomainId() == n.DomainId {
  224. // domain admin, can do anything with domain network
  225. } else {
  226. return FailReason{
  227. Reason: fmt.Sprintf("Network %s not accessible", n.Name),
  228. Type: NetworkOwnership,
  229. }
  230. }
  231. if private && n.IsPublic {
  232. return FailReason{
  233. Reason: fmt.Sprintf("Network %s is public", n.Name),
  234. Type: NetworkPublic,
  235. }
  236. }
  237. if req.Network != "" {
  238. if !(req.Network == n.GetId() || req.Network == n.GetName()) {
  239. return FailReason{
  240. Reason: fmt.Sprintf("%v(%v): id/name not matched", n.Name, n.Id),
  241. Type: NetworkMatch,
  242. }
  243. }
  244. } else {
  245. if !isMatchServerType(n.SNetwork) {
  246. return FailReason{
  247. Reason: fmt.Sprintf("Network %s type %s match", n.Name, n.ServerType),
  248. Type: NetworkTypeMatch,
  249. }
  250. }
  251. }
  252. if req.Network == "" && n.IsAutoAlloc.IsFalse() {
  253. return FailReason{Reason: fmt.Sprintf("Network %s is not auto alloc", n.Name), Type: NetworkPrivate}
  254. }
  255. checkAddress := func(addr string, net *models.SNetwork) error {
  256. if len(addr) == 0 {
  257. return nil
  258. }
  259. ipAddr, err := netutils.NewIPV4Addr(addr)
  260. if err != nil {
  261. return fmt.Errorf("Invalid ip address %s: %v", addr, err)
  262. }
  263. if !net.GetIPRange().Contains(ipAddr) {
  264. return fmt.Errorf("Address %s not in network %s range", addr, net.Name)
  265. }
  266. return nil
  267. }
  268. if err := checkAddress(address, n.SNetwork); err != nil {
  269. return FailReason{Reason: err.Error(), Type: NetworkRange}
  270. }
  271. return nil
  272. }
  273. func (p *NetworkPredicate) GetNetworkTypes(u *core.Unit, specifyType computeapi.TNetworkType) []computeapi.TNetworkType {
  274. netTypes := []computeapi.TNetworkType{}
  275. driver := p.GetHypervisorDriver(u)
  276. if driver != nil {
  277. netTypes = driver.GetRandomNetworkTypes()
  278. }
  279. if len(specifyType) > 0 {
  280. netTypes = []computeapi.TNetworkType{specifyType}
  281. }
  282. return netTypes
  283. }
  284. func (p *NetworkPredicate) Execute(ctx context.Context, u *core.Unit, c core.Candidater) (bool, []core.PredicateFailureReason, error) {
  285. h := NewPredicateHelper(p, u, c)
  286. getter := c.Getter()
  287. networks := getter.Networks()
  288. d := u.SchedData()
  289. getFreePort := func(id string) int {
  290. if _, ok := p.networkFreePortCount[id]; ok {
  291. return p.networkFreePortCount[id] - schedmodels.HostPendingUsageManager.GetNetPendingUsage(id)
  292. }
  293. return c.Getter().GetFreePort(id)
  294. }
  295. for _, reqNet := range d.Networks {
  296. netTypes := p.GetNetworkTypes(u, reqNet.NetType)
  297. freePortCnt, errs := IsNetworksAvailable(ctx, c, d, reqNet, networks, netTypes, getFreePort)
  298. if len(errs) > 0 {
  299. h.ExcludeByErrors(errs)
  300. return h.GetResult()
  301. }
  302. if !d.ReuseNetwork {
  303. h.SetCapacity(int64(freePortCnt))
  304. }
  305. }
  306. return h.GetResult()
  307. }