reservedips.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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. "strconv"
  19. "time"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. "yunion.io/x/pkg/util/rbacscope"
  24. "yunion.io/x/sqlchemy"
  25. api "yunion.io/x/onecloud/pkg/apis/compute"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  27. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  28. "yunion.io/x/onecloud/pkg/httperrors"
  29. "yunion.io/x/onecloud/pkg/mcclient"
  30. "yunion.io/x/onecloud/pkg/util/logclient"
  31. "yunion.io/x/onecloud/pkg/util/stringutils2"
  32. )
  33. type SReservedipManager struct {
  34. db.SResourceBaseManager
  35. SNetworkResourceBaseManager
  36. }
  37. var ReservedipManager *SReservedipManager
  38. func init() {
  39. ReservedipManager = &SReservedipManager{
  40. SResourceBaseManager: db.NewResourceBaseManager(
  41. SReservedip{},
  42. api.RESERVEDIP_TABLE,
  43. api.RESERVEDIP_RESOURCE_TYPE,
  44. api.RESERVEDIP_RESOURCE_TYPES,
  45. ),
  46. }
  47. ReservedipManager.SetVirtualObject(ReservedipManager)
  48. }
  49. type SReservedip struct {
  50. db.SResourceBase
  51. SNetworkResourceBase `width:"36" charset:"ascii" nullable:"false" list:"user"`
  52. // 自增Id
  53. Id int64 `primary:"true" auto_increment:"true" list:"user"`
  54. // IP子网Id
  55. // NetworkId string `width:"36" charset:"ascii" nullable:"false" list:"user"`
  56. // IP地址
  57. IpAddr string `width:"16" charset:"ascii" list:"user"`
  58. // IPv6地址
  59. Ip6Addr string `width:"64" charset:"ascii" list:"user"`
  60. // 预留原因或描述
  61. Notes string `width:"512" charset:"utf8" nullable:"true" list:"user" update:"user"`
  62. // 过期时间
  63. ExpiredAt time.Time `nullable:"true" list:"user"`
  64. // 状态
  65. Status string `width:"12" charset:"ascii" nullable:"false" default:"unknown" list:"user" create:"optional" update:"user"`
  66. }
  67. func (manager *SReservedipManager) CreateByInsertOrUpdate() bool {
  68. return false
  69. }
  70. func (manager *SReservedipManager) ReserveIP(ctx context.Context, userCred mcclient.TokenCredential, network *SNetwork, ip string, notes string, addrType api.TAddressType) error {
  71. return manager.ReserveIPWithDuration(ctx, userCred, network, ip, notes, 0, addrType)
  72. }
  73. func (manager *SReservedipManager) ReserveIPWithDuration(ctx context.Context, userCred mcclient.TokenCredential, network *SNetwork, ip string, notes string, duration time.Duration, addrType api.TAddressType) error {
  74. return manager.ReserveIPWithDurationAndStatus(ctx, userCred, network, ip, notes, duration, "", addrType)
  75. }
  76. func (manager *SReservedipManager) ReserveIPWithDurationAndStatus(ctx context.Context, userCred mcclient.TokenCredential, network *SNetwork, ip string, notes string, duration time.Duration, status string, addrType api.TAddressType) error {
  77. expiredAt := time.Time{}
  78. if duration > 0 {
  79. expiredAt = time.Now().UTC().Add(duration)
  80. }
  81. rip := manager.getReservedIP(network, ip, addrType)
  82. if rip == nil {
  83. // not found
  84. rip = &SReservedip{
  85. Notes: notes,
  86. ExpiredAt: expiredAt,
  87. Status: status,
  88. }
  89. rip.SetModelManager(manager, rip)
  90. if addrType == api.AddressTypeIPv6 {
  91. rip.Ip6Addr = ip
  92. } else {
  93. rip.IpAddr = ip
  94. }
  95. rip.NetworkId = network.Id
  96. err := manager.TableSpec().Insert(ctx, rip)
  97. if err != nil {
  98. log.Errorf("ReserveIP fail: %s", err)
  99. return errors.Wrap(err, "Insert")
  100. }
  101. } else {
  102. // extend the expiration
  103. err := rip.extend(notes, expiredAt, status)
  104. if err != nil {
  105. return errors.Wrap(err, "extend")
  106. }
  107. }
  108. db.OpsLog.LogEvent(network, db.ACT_RESERVE_IP, rip.GetShortDesc(ctx), userCred)
  109. logclient.AddSimpleActionLog(network, logclient.ACT_RESERVE_IP, rip.GetShortDesc(ctx), userCred, true)
  110. return nil
  111. }
  112. func (rip *SReservedip) extendWithDuration(notes string, duration time.Duration, status string) error {
  113. expiredAt := time.Time{}
  114. if duration > 0 {
  115. expiredAt = time.Now().UTC().Add(duration)
  116. }
  117. return rip.extend(notes, expiredAt, status)
  118. }
  119. func (rip *SReservedip) extend(notes string, expiredAt time.Time, status string) error {
  120. _, err := db.Update(rip, func() error {
  121. rip.Notes = notes
  122. rip.ExpiredAt = expiredAt
  123. if len(status) > 0 {
  124. rip.Status = status
  125. }
  126. return nil
  127. })
  128. if err != nil {
  129. return errors.Wrap(err, "Update")
  130. }
  131. return nil
  132. }
  133. func (manager *SReservedipManager) getReservedIP(network *SNetwork, ip string, addrType api.TAddressType) *SReservedip {
  134. rip := SReservedip{}
  135. rip.SetModelManager(manager, &rip)
  136. q := manager.Query().Equals("network_id", network.Id)
  137. if addrType == api.AddressTypeIPv6 {
  138. q = q.Equals("ip6_addr", ip)
  139. } else {
  140. q = q.Equals("ip_addr", ip)
  141. }
  142. err := q.First(&rip)
  143. if err != nil {
  144. if errors.Cause(err) != sql.ErrNoRows {
  145. log.Errorf("GetReservedIP fail: %s", err)
  146. }
  147. return nil
  148. }
  149. return &rip
  150. }
  151. func (manager *SReservedipManager) GetReservedIP(network *SNetwork, ip string, addrType api.TAddressType) *SReservedip {
  152. rip := manager.getReservedIP(network, ip, addrType)
  153. if rip == nil {
  154. log.Errorf("GetReserved IP %s: not found", ip)
  155. return nil
  156. }
  157. if rip.IsExpired() {
  158. log.Errorf("GetReserved IP %s: expired", ip)
  159. return nil
  160. }
  161. return rip
  162. }
  163. func (manager *SReservedipManager) GetReservedIPs(network *SNetwork) []SReservedip {
  164. rips := make([]SReservedip, 0)
  165. q := manager.Query().Equals("network_id", network.Id)
  166. q = filterExpiredReservedIps(q)
  167. err := db.FetchModelObjects(manager, q, &rips)
  168. if err != nil {
  169. log.Errorf("GetReservedIPs fail: %s", err)
  170. return nil
  171. }
  172. return rips
  173. }
  174. func (self *SReservedip) GetNetwork() *SNetwork {
  175. net, _ := NetworkManager.FetchById(self.NetworkId)
  176. if net != nil {
  177. return net.(*SNetwork)
  178. }
  179. return nil
  180. }
  181. func (rip *SReservedip) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
  182. desc := rip.SResourceBase.GetShortDesc(ctx)
  183. if len(rip.IpAddr) > 0 {
  184. desc.Set("ip_addr", jsonutils.NewString(rip.IpAddr))
  185. }
  186. if len(rip.Ip6Addr) > 0 {
  187. desc.Set("ip6_addr", jsonutils.NewString(rip.Ip6Addr))
  188. }
  189. return desc
  190. }
  191. func (self *SReservedip) Release(ctx context.Context, userCred mcclient.TokenCredential, network *SNetwork) error {
  192. // db.DeleteModel(self.ResourceModelManager(), self)
  193. if network == nil {
  194. network = self.GetNetwork()
  195. }
  196. err := db.DeleteModel(ctx, userCred, self)
  197. if err == nil && network != nil {
  198. db.OpsLog.LogEvent(network, db.ACT_RELEASE_IP, self.GetShortDesc(ctx), userCred)
  199. logclient.AddSimpleActionLog(network, logclient.ACT_RELEASE_IP, self.GetShortDesc(ctx), userCred, true)
  200. }
  201. return err
  202. }
  203. func (manager *SReservedipManager) FetchCustomizeColumns(
  204. ctx context.Context,
  205. userCred mcclient.TokenCredential,
  206. query jsonutils.JSONObject,
  207. objs []interface{},
  208. fields stringutils2.SSortedStrings,
  209. isList bool,
  210. ) []api.ReservedipDetails {
  211. rows := make([]api.ReservedipDetails, len(objs))
  212. resRows := manager.SResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  213. netRows := manager.SNetworkResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  214. for i := range rows {
  215. rows[i] = api.ReservedipDetails{
  216. ResourceBaseDetails: resRows[i],
  217. NetworkResourceInfo: netRows[i],
  218. }
  219. rows[i].Expired = objs[i].(*SReservedip).IsExpired()
  220. }
  221. return rows
  222. }
  223. // 预留IP地址列表
  224. func (manager *SReservedipManager) ListItemFilter(
  225. ctx context.Context,
  226. q *sqlchemy.SQuery,
  227. userCred mcclient.TokenCredential,
  228. query api.ReservedipListInput,
  229. ) (*sqlchemy.SQuery, error) {
  230. var err error
  231. q, err = manager.SResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ResourceBaseListInput)
  232. if err != nil {
  233. return nil, errors.Wrap(err, "SResourceBaseManager.ListItemFilter")
  234. }
  235. q, err = manager.SNetworkResourceBaseManager.ListItemFilter(ctx, q, userCred, query.NetworkFilterListInput)
  236. if err != nil {
  237. return nil, errors.Wrap(err, "SNetworkResourceBaseManager.ListItemFilter")
  238. }
  239. if query.All == nil || *query.All == false {
  240. q = filterExpiredReservedIps(q)
  241. }
  242. if len(query.IpAddr) > 0 {
  243. q = q.In("ip_addr", query.IpAddr)
  244. }
  245. if len(query.Status) > 0 {
  246. q = q.In("status", query.Status)
  247. }
  248. return q, nil
  249. }
  250. func filterExpiredReservedIps(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  251. return q.Filter(sqlchemy.OR(
  252. sqlchemy.IsNullOrEmpty(q.Field("expired_at")),
  253. sqlchemy.GT(q.Field("expired_at"), time.Now().UTC()),
  254. ))
  255. }
  256. func filterExpiredReservedIp4s(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  257. return q.Filter(sqlchemy.OR(
  258. sqlchemy.IsNullOrEmpty(q.Field("expired_at")),
  259. sqlchemy.GT(q.Field("expired_at"), time.Now().UTC()),
  260. )).IsNotEmpty("ip_addr")
  261. }
  262. func filterExpiredReservedIp6s(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  263. return q.Filter(sqlchemy.OR(
  264. sqlchemy.IsNullOrEmpty(q.Field("expired_at")),
  265. sqlchemy.GT(q.Field("expired_at"), time.Now().UTC()),
  266. )).IsNotEmpty("ip6_addr")
  267. }
  268. func (manager *SReservedipManager) OrderByExtraFields(
  269. ctx context.Context,
  270. q *sqlchemy.SQuery,
  271. userCred mcclient.TokenCredential,
  272. query api.ReservedipListInput,
  273. ) (*sqlchemy.SQuery, error) {
  274. var err error
  275. q, err = manager.SResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ResourceBaseListInput)
  276. if err != nil {
  277. return nil, errors.Wrap(err, "SResourceBaseManager.OrderByExtraFields")
  278. }
  279. q, err = manager.SNetworkResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.NetworkFilterListInput)
  280. if err != nil {
  281. return nil, errors.Wrap(err, "SNetworkResourceBaseManager.OrderByExtraFields")
  282. }
  283. return q, nil
  284. }
  285. func (manager *SReservedipManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  286. var err error
  287. q, err = manager.SResourceBaseManager.QueryDistinctExtraField(q, field)
  288. if err == nil {
  289. return q, nil
  290. }
  291. q, err = manager.SNetworkResourceBaseManager.QueryDistinctExtraField(q, field)
  292. if err == nil {
  293. return q, nil
  294. }
  295. return q, httperrors.ErrNotFound
  296. }
  297. func (rip *SReservedip) GetId() string {
  298. return strconv.FormatInt(rip.Id, 10)
  299. }
  300. func (rip *SReservedip) GetName() string {
  301. return rip.GetId()
  302. }
  303. func (manager *SReservedipManager) FilterById(q *sqlchemy.SQuery, idStr string) *sqlchemy.SQuery {
  304. return q.Equals("id", idStr)
  305. }
  306. func (manager *SReservedipManager) FilterByName(q *sqlchemy.SQuery, name string) *sqlchemy.SQuery {
  307. return q.Equals("id", name)
  308. }
  309. func (rip *SReservedip) IsExpired() bool {
  310. if !rip.ExpiredAt.IsZero() && rip.ExpiredAt.Before(time.Now()) {
  311. return true
  312. }
  313. return false
  314. }
  315. func (self *SReservedip) GetUniqValues() jsonutils.JSONObject {
  316. return jsonutils.Marshal(map[string]string{"network_id": self.NetworkId})
  317. }
  318. func (manager *SReservedipManager) FetchUniqValues(ctx context.Context, data jsonutils.JSONObject) jsonutils.JSONObject {
  319. networkId, _ := data.GetString("network_id")
  320. return jsonutils.Marshal(map[string]string{"network_id": networkId})
  321. }
  322. func (manager *SReservedipManager) FilterByUniqValues(q *sqlchemy.SQuery, values jsonutils.JSONObject) *sqlchemy.SQuery {
  323. networkId, _ := values.GetString("network_id")
  324. if len(networkId) > 0 {
  325. q = q.Equals("network_id", networkId)
  326. }
  327. return q
  328. }
  329. func (manager *SReservedipManager) NamespaceScope() rbacscope.TRbacScope {
  330. if consts.IsDomainizedNamespace() {
  331. return rbacscope.ScopeDomain
  332. } else {
  333. return rbacscope.ScopeSystem
  334. }
  335. }
  336. func (manager *SReservedipManager) ResourceScope() rbacscope.TRbacScope {
  337. return rbacscope.ScopeProject
  338. }
  339. func (manager *SReservedipManager) FilterByOwner(ctx context.Context, q *sqlchemy.SQuery, man db.FilterByOwnerProvider, userCred mcclient.TokenCredential, owner mcclient.IIdentityProvider, scope rbacscope.TRbacScope) *sqlchemy.SQuery {
  340. if owner != nil {
  341. switch scope {
  342. case rbacscope.ScopeProject, rbacscope.ScopeDomain:
  343. netsQ := NetworkManager.Query("id")
  344. netsQ = NetworkManager.FilterByOwner(ctx, netsQ, NetworkManager, userCred, owner, scope)
  345. netsSQ := netsQ.SubQuery()
  346. q = q.Join(netsSQ, sqlchemy.Equals(q.Field("network_id"), netsSQ.Field("id")))
  347. }
  348. }
  349. return q
  350. }
  351. func (manager *SReservedipManager) FetchOwnerId(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) {
  352. return db.FetchProjectInfo(ctx, data)
  353. }
  354. func (rip *SReservedip) GetOwnerId() mcclient.IIdentityProvider {
  355. network := rip.GetNetwork()
  356. if network != nil {
  357. return network.GetOwnerId()
  358. }
  359. return nil
  360. }
  361. func (manager *SReservedipManager) ListItemExportKeys(ctx context.Context,
  362. q *sqlchemy.SQuery,
  363. userCred mcclient.TokenCredential,
  364. keys stringutils2.SSortedStrings,
  365. ) (*sqlchemy.SQuery, error) {
  366. var err error
  367. q, err = manager.SResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  368. if err != nil {
  369. return nil, err
  370. }
  371. return manager.SNetworkResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  372. }