net_tap_flows.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  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 models
  15. import (
  16. "context"
  17. "database/sql"
  18. "strings"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/tristate"
  23. "yunion.io/x/pkg/utils"
  24. "yunion.io/x/sqlchemy"
  25. api "yunion.io/x/onecloud/pkg/apis/compute"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  27. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  28. "yunion.io/x/onecloud/pkg/httperrors"
  29. "yunion.io/x/onecloud/pkg/mcclient"
  30. "yunion.io/x/onecloud/pkg/util/stringutils2"
  31. )
  32. // +onecloud:swagger-gen-model-singular=tap_flow
  33. // +onecloud:swagger-gen-model-plural=tap_flows
  34. type SNetTapFlowManager struct {
  35. db.SEnabledStatusStandaloneResourceBaseManager
  36. }
  37. var NetTapFlowManager *SNetTapFlowManager
  38. func init() {
  39. NetTapFlowManager = &SNetTapFlowManager{
  40. SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager(
  41. SNetTapFlow{},
  42. "net_tap_flows_tbl",
  43. "tap_flow",
  44. "tap_flows",
  45. ),
  46. }
  47. NetTapFlowManager.SetVirtualObject(NetTapFlowManager)
  48. }
  49. type SNetTapFlow struct {
  50. db.SEnabledStatusStandaloneResourceBase
  51. TapId string `width:"36" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"`
  52. Type string `width:"10" charset:"ascii" list:"admin" create:"admin_required"`
  53. SourceId string `width:"36" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"`
  54. NetId string `width:"36" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"`
  55. MacAddr string `width:"18" charset:"ascii" list:"admin" create:"admin_optional"`
  56. VlanId int `nullable:"true" list:"admin" create:"admin_optional"`
  57. Direction string `width:"6" charset:"ascii" list:"admin" create:"admin_required" default:"BOTH"`
  58. FlowId uint16 `nullable:"false" list:"admin"`
  59. }
  60. func (man *SNetTapFlowManager) ListItemFilter(
  61. ctx context.Context,
  62. q *sqlchemy.SQuery,
  63. userCred mcclient.TokenCredential,
  64. query api.NetTapFlowListInput,
  65. ) (*sqlchemy.SQuery, error) {
  66. q, err := man.SEnabledStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
  67. if err != nil {
  68. return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemFilter")
  69. }
  70. if len(query.TapId) > 0 {
  71. tapObj, err := NetTapServiceManager.FetchByIdOrName(ctx, userCred, query.TapId)
  72. if err != nil {
  73. if errors.Cause(err) == sql.ErrNoRows {
  74. return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "%s %s not found", NetTapServiceManager.Keyword(), query.TapId)
  75. } else {
  76. return nil, errors.Wrap(err, "NetTapServiceManager.FetchByIdOrName")
  77. }
  78. }
  79. q = q.Equals("tap_id", tapObj.GetId())
  80. }
  81. if len(query.HostId) > 0 {
  82. hostObj, err := HostManager.FetchByIdOrName(ctx, userCred, query.HostId)
  83. if err != nil {
  84. if errors.Cause(err) == sql.ErrNoRows {
  85. return nil, httperrors.NewResourceNotFoundError2(HostManager.Keyword(), query.HostId)
  86. } else {
  87. return nil, errors.Wrap(err, "HostManager.FetchHostById")
  88. }
  89. }
  90. q = man.filterByHostId(q, hostObj.GetId())
  91. }
  92. return q, nil
  93. }
  94. func (man *SNetTapFlowManager) filterByHostId(q *sqlchemy.SQuery, hostId string) *sqlchemy.SQuery {
  95. guestIdQ := GuestManager.Query("id").Equals("host_id", hostId).SubQuery()
  96. q = q.Filter(sqlchemy.OR(
  97. sqlchemy.AND(
  98. sqlchemy.Equals(q.Field("type"), api.TapFlowVSwitch),
  99. sqlchemy.Equals(q.Field("source_id"), hostId),
  100. ),
  101. sqlchemy.AND(
  102. sqlchemy.Equals(q.Field("type"), api.TapFlowGuestNic),
  103. sqlchemy.In(q.Field("source_id"), guestIdQ),
  104. ),
  105. ))
  106. return q
  107. }
  108. func (man *SNetTapFlowManager) getEnabledTapFlowsOfTap(tapId string) ([]SNetTapFlow, error) {
  109. return man.getEnabledTapFlows("", tapId)
  110. }
  111. func (man *SNetTapFlowManager) getEnabledTapFlowsOnHost(hostId string) ([]SNetTapFlow, error) {
  112. return man.getEnabledTapFlows(hostId, "")
  113. }
  114. func (man *SNetTapFlowManager) getEnabledTapFlows(hostId string, tapId string) ([]SNetTapFlow, error) {
  115. q := man.Query().IsTrue("enabled")
  116. if len(hostId) > 0 {
  117. q = man.filterByHostId(q, hostId)
  118. }
  119. if len(tapId) > 0 {
  120. q = q.Equals("tap_id", tapId)
  121. }
  122. // filter by enabled tap
  123. tapQ := NetTapServiceManager.Query().IsTrue("enabled").SubQuery()
  124. q = q.Join(tapQ, sqlchemy.Equals(q.Field("tap_id"), tapQ.Field("id")))
  125. flows := make([]SNetTapFlow, 0)
  126. err := db.FetchModelObjects(man, q, &flows)
  127. if err != nil {
  128. return nil, errors.Wrap(err, "FetchModelObjects")
  129. }
  130. return flows, nil
  131. }
  132. func (man *SNetTapFlowManager) OrderByExtraFields(
  133. ctx context.Context,
  134. q *sqlchemy.SQuery,
  135. userCred mcclient.TokenCredential,
  136. query api.NetTapFlowListInput,
  137. ) (*sqlchemy.SQuery, error) {
  138. q, err := man.SEnabledStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
  139. if err != nil {
  140. return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields")
  141. }
  142. return q, nil
  143. }
  144. func (man *SNetTapFlowManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  145. var err error
  146. q, err = man.SEnabledStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
  147. if err == nil {
  148. return q, nil
  149. }
  150. return q, httperrors.ErrNotFound
  151. }
  152. func (manager *SNetTapFlowManager) FetchCustomizeColumns(
  153. ctx context.Context,
  154. userCred mcclient.TokenCredential,
  155. query jsonutils.JSONObject,
  156. objs []interface{},
  157. fields stringutils2.SSortedStrings,
  158. isList bool,
  159. ) []api.NetTapFlowDetails {
  160. rows := make([]api.NetTapFlowDetails, len(objs))
  161. stdRows := manager.SEnabledStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  162. tapIds := make([]string, len(objs))
  163. for i := range rows {
  164. rows[i] = api.NetTapFlowDetails{
  165. EnabledStatusStandaloneResourceDetails: stdRows[i],
  166. }
  167. flow := objs[i].(*SNetTapFlow)
  168. tapIds[i] = flow.TapId
  169. rows[i] = flow.getMoreDetails(ctx, rows[i])
  170. }
  171. tapIdMap, err := db.FetchIdNameMap2(NetTapServiceManager, tapIds)
  172. if err != nil {
  173. log.Errorf("FetchIdNameMap2 fail: %s", err)
  174. return rows
  175. }
  176. for i := range rows {
  177. if name, ok := tapIdMap[tapIds[i]]; ok {
  178. rows[i].Tap = name
  179. }
  180. }
  181. return rows
  182. }
  183. func (flow *SNetTapFlow) getMoreDetails(ctx context.Context, details api.NetTapFlowDetails) api.NetTapFlowDetails {
  184. switch flow.Type {
  185. case api.TapFlowVSwitch:
  186. host := HostManager.FetchHostById(flow.SourceId)
  187. if host != nil {
  188. details.Source = host.Name
  189. details.SourceIps = host.AccessIp
  190. }
  191. wire := WireManager.FetchWireById(flow.NetId)
  192. if wire != nil {
  193. details.Net = wire.Name
  194. }
  195. case api.TapFlowGuestNic:
  196. guest := GuestManager.FetchGuestById(flow.SourceId)
  197. if guest != nil {
  198. details.Source = guest.Name
  199. ret := fetchGuestIPs([]string{flow.SourceId}, tristate.False)
  200. details.SourceIps = strings.Join(ret[flow.SourceId], ",")
  201. }
  202. netObj, _ := NetworkManager.FetchById(flow.NetId)
  203. if netObj != nil {
  204. details.Net = netObj.GetName()
  205. }
  206. }
  207. return details
  208. }
  209. func (manager *SNetTapFlowManager) ValidateCreateData(
  210. ctx context.Context,
  211. userCred mcclient.TokenCredential,
  212. ownerId mcclient.IIdentityProvider,
  213. query jsonutils.JSONObject,
  214. input api.NetTapFlowCreateInput,
  215. ) (api.NetTapFlowCreateInput, error) {
  216. var err error
  217. input.EnabledStatusStandaloneResourceCreateInput, err = manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusStandaloneResourceCreateInput)
  218. if err != nil {
  219. return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ValidateCreateData(")
  220. }
  221. tapObj, err := NetTapServiceManager.FetchByIdOrName(ctx, userCred, input.TapId)
  222. if err != nil {
  223. if errors.Cause(err) == sql.ErrNoRows {
  224. return input, httperrors.NewResourceNotFoundError2(NetTapServiceManager.Keyword(), input.TapId)
  225. } else {
  226. return input, errors.Wrap(err, "NetTapServiceManager.FetchByIdOrName")
  227. }
  228. }
  229. tap := tapObj.(*SNetTapService)
  230. input.TapId = tap.Id
  231. switch input.Type {
  232. case api.TapFlowVSwitch:
  233. hostObj, err := HostManager.FetchByIdOrName(ctx, userCred, input.HostId)
  234. if err != nil {
  235. if errors.Cause(err) == sql.ErrNoRows {
  236. return input, httperrors.NewResourceNotFoundError2(HostManager.Keyword(), input.HostId)
  237. } else {
  238. return input, errors.Wrap(err, "HostManager.FetchByIdOrName")
  239. }
  240. }
  241. wireObj, err := WireManager.FetchByIdOrName(ctx, userCred, input.WireId)
  242. if err != nil {
  243. if errors.Cause(err) == sql.ErrNoRows {
  244. return input, httperrors.NewResourceNotFoundError2(WireManager.Keyword(), input.WireId)
  245. } else {
  246. return input, errors.Wrap(err, "WireManager.FetchByIdOrName")
  247. }
  248. }
  249. host := hostObj.(*SHost)
  250. if host.HostType != api.HOST_TYPE_HYPERVISOR {
  251. return input, errors.Wrapf(httperrors.ErrNotSupported, "host type %s not supported", host.HostType)
  252. }
  253. wire := wireObj.(*SWire)
  254. netifs := host.getNetifsOnWire(wire.Id)
  255. if len(netifs) == 0 {
  256. return input, errors.Wrapf(httperrors.ErrInvalidStatus, "host %s and wire %s not attached", input.HostId, input.WireId)
  257. }
  258. ipmiCnt := 0
  259. nicCnt := 0
  260. for _, netif := range netifs {
  261. if netif.NicType == api.NIC_TYPE_IPMI {
  262. ipmiCnt++
  263. }
  264. nicCnt++
  265. }
  266. if ipmiCnt == nicCnt {
  267. return input, errors.Wrapf(httperrors.ErrInvalidStatus, "host %s and wire %s attached with IPMI links", input.HostId, input.WireId)
  268. }
  269. input.SourceId = host.Id
  270. input.MacAddr = ""
  271. input.NetId = wire.Id
  272. if input.VlanId != nil && (*input.VlanId <= 0 || *input.VlanId > 4095) {
  273. return input, errors.Wrapf(httperrors.ErrInputParameter, "invalid vlan id %d", *input.VlanId)
  274. }
  275. case api.TapFlowGuestNic:
  276. guestObj, err := GuestManager.FetchByIdOrName(ctx, userCred, input.GuestId)
  277. if err != nil {
  278. if errors.Cause(err) == sql.ErrNoRows {
  279. return input, httperrors.NewResourceNotFoundError2(GuestManager.Keyword(), input.GuestId)
  280. } else {
  281. return input, errors.Wrap(err, "GuestManager.FetchByIdOrName")
  282. }
  283. }
  284. guest := guestObj.(*SGuest)
  285. if guest.Hypervisor != api.HYPERVISOR_KVM {
  286. return input, errors.Wrapf(httperrors.ErrInvalidStatus, "hypervisor %s not supported", guest.Hypervisor)
  287. }
  288. gns, err := GuestnetworkManager.FetchByGuestId(guest.Id)
  289. if err != nil {
  290. return input, errors.Wrap(err, "GuestnetworkManager.FetchByGuestId")
  291. }
  292. var gn *SGuestnetwork
  293. if len(input.IpAddr) == 0 && len(input.MacAddr) == 0 {
  294. if len(gns) == 1 {
  295. gn = &gns[0]
  296. } else {
  297. return input, errors.Wrap(httperrors.ErrInputParameter, "either ip_addr or mac_addr should be specified")
  298. }
  299. } else {
  300. for i := range gns {
  301. if (len(input.IpAddr) > 0 && input.IpAddr == gns[i].IpAddr) || (len(input.MacAddr) > 0 && input.MacAddr == gns[i].MacAddr) {
  302. gn = &gns[i]
  303. break
  304. }
  305. }
  306. if gn == nil {
  307. return input, errors.Wrap(httperrors.ErrNotFound, "Guest network not found")
  308. }
  309. }
  310. input.SourceId = guest.Id
  311. input.MacAddr = gn.MacAddr
  312. input.NetId = gn.NetworkId
  313. input.VlanId = nil
  314. // check loop
  315. if tap.Type == api.TapServiceGuest && tap.TargetId == input.SourceId {
  316. return input, errors.Wrap(httperrors.ErrInputParameter, "cannot tap trafic from guest itself")
  317. }
  318. default:
  319. return input, errors.Wrapf(httperrors.ErrInputParameter, "invalid flow type %s", input.Type)
  320. }
  321. // check duplicity
  322. dupCnt, err := manager.Query().Equals("tap_id", tap.Id).Equals("type", input.Type).Equals("source_id", input.SourceId).Equals("net_id", input.NetId).CountWithError()
  323. if err != nil {
  324. return input, errors.Wrap(err, "query duplicity")
  325. }
  326. if dupCnt > 0 {
  327. return input, errors.Wrap(httperrors.ErrConflict, "this source has been added")
  328. }
  329. if len(input.Direction) == 0 {
  330. input.Direction = api.TapFlowDirectionBoth
  331. }
  332. if !utils.IsInStringArray(input.Direction, api.TapFlowDirections) {
  333. return input, errors.Wrapf(httperrors.ErrNotSupported, "unsupported direction %s", input.Direction)
  334. }
  335. if input.Enabled == nil {
  336. trueVal := true
  337. input.Enabled = &trueVal
  338. }
  339. return input, nil
  340. }
  341. func (tap *SNetTapFlow) CustomizeCreate(
  342. ctx context.Context,
  343. userCred mcclient.TokenCredential,
  344. ownerId mcclient.IIdentityProvider,
  345. query jsonutils.JSONObject,
  346. data jsonutils.JSONObject,
  347. ) error {
  348. // generate flowId
  349. err := func() error {
  350. lockman.LockClass(ctx, NetTapFlowManager, "")
  351. defer lockman.ReleaseClass(ctx, NetTapFlowManager, "")
  352. flowId, err := NetTapFlowManager.getFreeFlowId()
  353. if err != nil {
  354. return errors.Wrap(err, "getFreeFlowId")
  355. }
  356. tap.FlowId = flowId
  357. return nil
  358. }()
  359. if err != nil {
  360. return errors.Wrap(err, "generate flow id")
  361. }
  362. return tap.SEnabledStatusStandaloneResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
  363. }
  364. func (manager *SNetTapFlowManager) getFreeFlowId() (uint16, error) {
  365. flowIds := make([]struct {
  366. FlowId uint16 `json:"flow_id"`
  367. }, 0)
  368. q := manager.Query("flow_id").Asc("flow_id")
  369. err := q.All(&flowIds)
  370. if err != nil && errors.Cause(err) != sql.ErrNoRows {
  371. return 0, errors.Wrap(err, "queryAll")
  372. }
  373. if len(flowIds) == 0 {
  374. return api.TapFlowIdMin, nil
  375. }
  376. if flowIds[0].FlowId > api.TapFlowIdMin {
  377. return flowIds[0].FlowId - 1, nil
  378. }
  379. if flowIds[len(flowIds)-1].FlowId < api.TapFlowIdMax {
  380. return flowIds[len(flowIds)-1].FlowId + 1, nil
  381. }
  382. for i := 0; i < len(flowIds)-1; i++ {
  383. if flowIds[i].FlowId+1 < flowIds[i+1].FlowId {
  384. return flowIds[i].FlowId + 1, nil
  385. }
  386. }
  387. return 0, errors.Wrap(httperrors.ErrOutOfResource, "run out of flow id!!!")
  388. }
  389. func (flow *SNetTapFlow) getTap() *SNetTapService {
  390. srvObj, _ := NetTapServiceManager.FetchById(flow.TapId)
  391. return srvObj.(*SNetTapService)
  392. }
  393. func (flow *SNetTapFlow) getTapHostIp() string {
  394. return flow.getTap().getTapHostIp()
  395. }
  396. func (flow *SNetTapFlow) getMirrorConfig(needTapHostIp bool) (api.SMirrorConfig, error) {
  397. ret := api.SMirrorConfig{}
  398. if needTapHostIp {
  399. ret.TapHostIp = flow.getTapHostIp()
  400. }
  401. var hostId, wireId string
  402. switch flow.Type {
  403. case api.TapFlowVSwitch:
  404. hostId = flow.SourceId
  405. wireId = flow.NetId
  406. case api.TapFlowGuestNic:
  407. guest := GuestManager.FetchGuestById(flow.SourceId)
  408. if guest == nil {
  409. // guest has been deleted?
  410. return ret, errors.Wrap(errors.ErrNotFound, "source not found")
  411. }
  412. gn, err := guest.GetGuestnetworkByMac(flow.MacAddr)
  413. if err != nil {
  414. return ret, errors.Wrap(err, "GetGuestnetworkByMac")
  415. }
  416. ret.Port = gn.Ifname
  417. hostId = guest.HostId
  418. net, err := gn.GetNetwork()
  419. if err != nil {
  420. return ret, errors.Wrapf(err, "GetNetwork")
  421. }
  422. if net.IsClassic() {
  423. wireId = net.WireId
  424. } else {
  425. ret.Bridge = api.HostVpcBridge
  426. }
  427. }
  428. host := HostManager.FetchHostById(hostId)
  429. if len(wireId) > 0 {
  430. // classic network
  431. netifs := host.getNetifsOnWire(wireId)
  432. if len(netifs) == 0 {
  433. return ret, errors.Error("invalid flow? no valid hostwire")
  434. }
  435. if len(netifs) > 1 {
  436. return ret, errors.Error("invalid flow? host and wire have multiple hostwires")
  437. }
  438. ret.Bridge = netifs[0].Bridge
  439. }
  440. ret.HostIp = host.AccessIp
  441. ret.FlowId = flow.FlowId
  442. ret.VlanId = flow.VlanId
  443. ret.Direction = flow.Direction
  444. return ret, nil
  445. }
  446. func (manager *SNetTapFlowManager) removeTapFlowsByGuestId(ctx context.Context, userCred mcclient.TokenCredential, sourceId string) error {
  447. return manager.removeTapFlows(ctx, userCred, api.TapFlowGuestNic, sourceId)
  448. }
  449. func (manager *SNetTapFlowManager) removeTapFlowsByHostId(ctx context.Context, userCred mcclient.TokenCredential, sourceId string) error {
  450. return manager.removeTapFlows(ctx, userCred, api.TapFlowVSwitch, sourceId)
  451. }
  452. func (manager *SNetTapFlowManager) removeTapFlows(ctx context.Context, userCred mcclient.TokenCredential, srvType string, targetId string) error {
  453. srvs, err := manager.getTapFlows(srvType, targetId)
  454. if err != nil {
  455. return errors.Wrap(err, "getTapServicesByHostId")
  456. }
  457. for i := range srvs {
  458. err := srvs[i].Delete(ctx, userCred)
  459. if err != nil {
  460. return errors.Wrap(err, "Delete")
  461. }
  462. }
  463. return nil
  464. }
  465. func (manager *SNetTapFlowManager) getTapFlows(srvType string, sourceId string) ([]SNetTapFlow, error) {
  466. q := manager.Query()
  467. q = q.Equals("type", srvType)
  468. q = q.Equals("source_id", sourceId)
  469. ret := make([]SNetTapFlow, 0)
  470. err := db.FetchModelObjects(manager, q, &ret)
  471. if err != nil {
  472. return nil, errors.Wrap(err, "FetchModelObjects")
  473. }
  474. return ret, nil
  475. }