metadata.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  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 db
  15. import (
  16. "context"
  17. "database/sql"
  18. "fmt"
  19. "strings"
  20. "time"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/log"
  23. "yunion.io/x/pkg/errors"
  24. "yunion.io/x/pkg/util/printutils"
  25. "yunion.io/x/pkg/util/rbacscope"
  26. "yunion.io/x/pkg/util/stringutils"
  27. "yunion.io/x/pkg/utils"
  28. "yunion.io/x/sqlchemy"
  29. "yunion.io/x/onecloud/pkg/apis"
  30. dbapi "yunion.io/x/onecloud/pkg/apis/cloudcommon/db"
  31. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  32. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  33. "yunion.io/x/onecloud/pkg/cloudcommon/policy"
  34. "yunion.io/x/onecloud/pkg/httperrors"
  35. "yunion.io/x/onecloud/pkg/mcclient"
  36. )
  37. const (
  38. SYSTEM_ADMIN_PREFIX = "__sys_"
  39. SYS_TAG_PREFIX = "__"
  40. CLOUD_TAG_PREFIX = dbapi.CLOUD_TAG_PREFIX
  41. USER_TAG_PREFIX = dbapi.USER_TAG_PREFIX
  42. SYS_CLOUD_TAG_PREFIX = dbapi.SYS_CLOUD_TAG_PREFIX
  43. CLASS_TAG_PREFIX = dbapi.CLASS_TAG_PREFIX
  44. SKU_METADAT_KEY = "md5"
  45. ORGANIZATION_TAG_PREFIX = dbapi.ORGANIZATION_TAG_PREFIX
  46. // TAG_DELETE_RANGE_USER = "user"
  47. // TAG_DELETE_RANGE_CLOUD = CLOUD_TAG_PREFIX // "cloud"
  48. TAG_DELETE_RANGE_ALL = "all"
  49. OBJECT_TYPE_ID_SEP = "::"
  50. RE_BILLING_AT = "__re_billing_at"
  51. )
  52. type SMetadataManager struct {
  53. SModelBaseManager
  54. }
  55. type SMetadata struct {
  56. SModelBase
  57. // 资源类型
  58. // example: network
  59. ObjType string `width:"40" charset:"ascii" index:"true" list:"user" get:"user"`
  60. // 资源ID
  61. // example: 87321a70-1ecb-422a-8b0c-c9aa632a46a7
  62. ObjId string `width:"88" charset:"ascii" index:"true" list:"user" get:"user"`
  63. // 资源组合ID
  64. // example: network::87321a70-1ecb-422a-8b0c-c9aa632a46a7
  65. Id string `width:"128" charset:"ascii" primary:"true" list:"user" get:"user"`
  66. // 标签KEY
  67. // exmaple: 部门
  68. Key string `width:"64" charset:"utf8" primary:"true" list:"user" get:"user"`
  69. // 标签值
  70. // example: 技术部
  71. Value string `charset:"utf8" list:"user" get:"user"`
  72. // 更新时间
  73. UpdatedAt time.Time `nullable:"false" updated_at:"true"`
  74. // 是否被删除
  75. Deleted bool `nullable:"false" default:"false" index:"true"`
  76. }
  77. var Metadata *SMetadataManager
  78. func init() {
  79. Metadata = &SMetadataManager{
  80. SModelBaseManager: NewModelBaseManager(
  81. SMetadata{},
  82. "metadata_tbl",
  83. "metadata",
  84. "metadatas",
  85. ),
  86. }
  87. Metadata.SetVirtualObject(Metadata)
  88. Metadata.TableSpec().AddIndex(false, "obj_type", "obj_id", "key", "deleted")
  89. }
  90. func (manager *SMetadataManager) InitializeData() error {
  91. /*no need to do this initilization any more
  92. q := manager.RawQuery()
  93. q = q.Filter(sqlchemy.OR(
  94. sqlchemy.IsNullOrEmpty(q.Field("obj_type")),
  95. sqlchemy.IsNullOrEmpty(q.Field("obj_id")),
  96. ))
  97. mds := make([]SMetadata, 0)
  98. err := FetchModelObjects(manager, q, &mds)
  99. if err != nil && err != sql.ErrNoRows {
  100. return errors.Wrap(err, "FetchModelObjects")
  101. }
  102. for i := range mds {
  103. _, err := Update(&mds[i], func() error {
  104. parts := strings.Split(mds[i].Id, OBJECT_TYPE_ID_SEP)
  105. if len(parts) == 2 {
  106. mds[i].ObjType = parts[0]
  107. mds[i].ObjId = parts[1]
  108. return nil
  109. } else {
  110. return errors.Wrapf(httperrors.ErrInvalidFormat, "invlid id format %s", mds[i].Id)
  111. }
  112. })
  113. if err != nil {
  114. return errors.Wrap(err, "update")
  115. }
  116. }*/
  117. return nil
  118. }
  119. func (m *SMetadata) GetId() string {
  120. return fmt.Sprintf("%s-%s", m.Id, m.Key)
  121. }
  122. func (m *SMetadata) GetName() string {
  123. return fmt.Sprintf("%s-%s", m.Id, m.Key)
  124. }
  125. // func (m *SMetadata) GetModelManager() IModelManager {
  126. // return Metadata
  127. // }
  128. func GetModelIdstr(model IModel) string {
  129. return getObjectIdstr(model.GetModelManager().Keyword(), model.GetId())
  130. }
  131. func getObjectIdstr(objType, objId string) string {
  132. return fmt.Sprintf("%s%s%s", objType, OBJECT_TYPE_ID_SEP, objId)
  133. }
  134. func (manager *SMetadataManager) Query(fields ...string) *sqlchemy.SQuery {
  135. return manager.SModelBaseManager.Query(fields...).IsFalse("deleted")
  136. }
  137. func (manager *SMetadataManager) RawQuery(fields ...string) *sqlchemy.SQuery {
  138. return manager.SModelBaseManager.Query(fields...)
  139. }
  140. func (m *SMetadata) MarkDelete() error {
  141. m.Deleted = true
  142. return nil
  143. }
  144. func (m *SMetadata) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
  145. return DeleteModel(ctx, userCred, m)
  146. }
  147. func (manager *SMetadataManager) fetchKeyValueQuery(
  148. ctx context.Context,
  149. userCred mcclient.TokenCredential,
  150. input apis.MetaGetPropertyTagValuePairsInput,
  151. ) (*sqlchemy.SQuery, error) {
  152. var err error
  153. sq := manager.Query().SubQuery()
  154. keyOnly := (input.KeyOnly != nil && *input.KeyOnly)
  155. var q *sqlchemy.SQuery
  156. var queryFields []sqlchemy.IQueryField
  157. if keyOnly {
  158. queryFields = []sqlchemy.IQueryField{
  159. sq.Field("key"),
  160. sqlchemy.COUNT("count", sq.Field("key")),
  161. }
  162. } else {
  163. queryFields = []sqlchemy.IQueryField{
  164. sq.Field("key"),
  165. sq.Field("value"),
  166. sqlchemy.COUNT("count", sq.Field("key")),
  167. }
  168. }
  169. q = sq.Query(queryFields...)
  170. q, err = manager.ListItemFilter(ctx, q, userCred, input.MetadataListInput)
  171. if err != nil {
  172. return nil, errors.Wrap(err, "ListItemFilter")
  173. }
  174. if keyOnly {
  175. q = q.GroupBy(q.Field("key"))
  176. } else {
  177. q = q.GroupBy(q.Field("key"), q.Field("value"))
  178. }
  179. if input.Order == string(sqlchemy.SQL_ORDER_DESC) {
  180. q = q.Desc(q.Field("key"))
  181. if !keyOnly {
  182. q = q.Desc(q.Field("value"))
  183. }
  184. } else {
  185. q = q.Asc(q.Field("key"))
  186. if !keyOnly {
  187. q = q.Asc(q.Field("value"))
  188. }
  189. }
  190. return q, nil
  191. }
  192. // +onecloud:swagger-gen-ignore
  193. func (manager *SMetadataManager) GetPropertyTagValuePairs(
  194. ctx context.Context,
  195. userCred mcclient.TokenCredential,
  196. input apis.MetaGetPropertyTagValuePairsInput,
  197. ) (*printutils.ListResult, error) {
  198. q, err := manager.fetchKeyValueQuery(ctx, userCred, input)
  199. if err != nil {
  200. return nil, errors.Wrap(err, "fetchKeyValueQuery")
  201. }
  202. totalCnt, err := q.CountWithError()
  203. if err != nil {
  204. return nil, errors.Wrap(err, "CountWithError")
  205. }
  206. if totalCnt == 0 {
  207. emptyList := printutils.ListResult{Data: []jsonutils.JSONObject{}}
  208. return &emptyList, nil
  209. }
  210. maxLimit := consts.GetMaxPagingLimit()
  211. limit := consts.GetDefaultPagingLimit()
  212. if input.Limit != nil {
  213. limit = int64(*input.Limit)
  214. }
  215. offset := int64(0)
  216. if input.Offset != nil {
  217. offset = int64(*input.Offset)
  218. }
  219. if offset < 0 {
  220. offset = int64(totalCnt) + offset
  221. }
  222. if offset < 0 {
  223. offset = 0
  224. }
  225. if int64(totalCnt) > maxLimit && (limit <= 0 || limit > maxLimit) {
  226. limit = maxLimit
  227. }
  228. if limit > 0 {
  229. q = q.Limit(int(limit))
  230. }
  231. if offset > 0 {
  232. q = q.Offset(int(offset))
  233. }
  234. data, err := manager.metaDataQuery2List(ctx, q, userCred, input.MetadataListInput)
  235. if err != nil {
  236. return nil, errors.Wrap(err, "metadataQuery2List")
  237. }
  238. emptyList := printutils.ListResult{
  239. Data: data,
  240. Total: totalCnt,
  241. Limit: int(limit),
  242. Offset: int(offset),
  243. }
  244. return &emptyList, nil
  245. }
  246. func (manager *SMetadataManager) metaDataQuery2List(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input apis.MetadataListInput) ([]jsonutils.JSONObject, error) {
  247. metadatas := make([]struct {
  248. Key string
  249. Value string
  250. Count int64
  251. }, 0)
  252. err := q.All(&metadatas)
  253. if err != nil {
  254. return nil, errors.Wrap(err, "Query.All")
  255. }
  256. ret := make([]jsonutils.JSONObject, len(metadatas))
  257. keys := []string{}
  258. for i := range metadatas {
  259. if !utils.IsInStringArray(metadatas[i].Key, keys) {
  260. keys = append(keys, metadatas[i].Key)
  261. }
  262. ret[i] = jsonutils.Marshal(metadatas[i])
  263. }
  264. if input.Details == nil || !*input.Details {
  265. return ret, nil
  266. }
  267. mQ, err := manager.ListItemFilter(ctx, manager.Query(), userCred, input)
  268. if err != nil {
  269. return nil, errors.Wrap(err, "ListItemFilter")
  270. }
  271. metas := []SMetadata{}
  272. err = mQ.In("key", keys).All(&metas)
  273. if err != nil {
  274. return ret, errors.Wrapf(err, "q.All")
  275. }
  276. count := map[string]map[string]map[string]int64{}
  277. for i := range metas {
  278. meta := metas[i]
  279. _, ok := count[meta.Key]
  280. if !ok {
  281. count[meta.Key] = map[string]map[string]int64{}
  282. }
  283. _, ok = count[meta.Key][meta.Value]
  284. if !ok {
  285. count[meta.Key][meta.Value] = map[string]int64{}
  286. }
  287. k := fmt.Sprintf("%s_count", meta.ObjType)
  288. _, ok = count[meta.Key][meta.Value][k]
  289. if !ok {
  290. count[meta.Key][meta.Value][k] = 0
  291. }
  292. count[meta.Key][meta.Value][k] += 1
  293. }
  294. for i, meta := range metadatas {
  295. jsonutils.Update(ret[i], count[meta.Key][meta.Value])
  296. }
  297. return ret, nil
  298. }
  299. func (manager *SMetadataManager) metadataBaseFilter(q *sqlchemy.SQuery, input apis.MetadataBaseFilterInput) *sqlchemy.SQuery {
  300. if len(input.KeyLike) > 0 {
  301. q = q.Contains("key", input.KeyLike)
  302. }
  303. if len(input.ValueLike) > 0 {
  304. q = q.Contains("value", input.ValueLike)
  305. }
  306. if len(input.Key) > 0 {
  307. q = q.In("key", input.Key)
  308. }
  309. if len(input.Value) > 0 {
  310. q = q.In("value", input.Value)
  311. }
  312. if input.SysMeta != nil && *input.SysMeta {
  313. q = q.Filter(sqlchemy.Startswith(q.Field("key"), SYS_TAG_PREFIX))
  314. }
  315. if input.CloudMeta != nil && *input.CloudMeta {
  316. q = q.Filter(sqlchemy.Startswith(q.Field("key"), CLOUD_TAG_PREFIX))
  317. }
  318. if input.UserMeta != nil && *input.UserMeta {
  319. q = q.Filter(sqlchemy.Startswith(q.Field("key"), USER_TAG_PREFIX))
  320. }
  321. withConditions := []sqlchemy.ICondition{}
  322. if input.WithSysMeta != nil && *input.WithSysMeta {
  323. withConditions = append(withConditions, sqlchemy.Startswith(q.Field("key"), SYS_TAG_PREFIX))
  324. }
  325. if input.WithCloudMeta != nil && *input.WithCloudMeta {
  326. withConditions = append(withConditions, sqlchemy.Startswith(q.Field("key"), CLOUD_TAG_PREFIX))
  327. }
  328. if input.WithUserMeta != nil && *input.WithUserMeta {
  329. withConditions = append(withConditions, sqlchemy.Startswith(q.Field("key"), USER_TAG_PREFIX))
  330. }
  331. if len(withConditions) > 0 {
  332. q = q.Filter(sqlchemy.OR(withConditions...))
  333. }
  334. return q
  335. }
  336. // 元数据(标签)列表
  337. func (manager *SMetadataManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input apis.MetadataListInput) (*sqlchemy.SQuery, error) {
  338. var err error
  339. q, err = manager.SModelBaseManager.ListItemFilter(ctx, q, userCred, input.ModelBaseListInput)
  340. if err != nil {
  341. return nil, errors.Wrap(err, "SModelBaseManager.ListItemFilter")
  342. }
  343. q = manager.metadataBaseFilter(q, input.MetadataBaseFilterInput)
  344. if len(input.Search) > 0 {
  345. q = q.Filter(sqlchemy.OR(
  346. sqlchemy.Contains(q.Field("key"), input.Search),
  347. sqlchemy.Contains(q.Field("value"), input.Search),
  348. ))
  349. }
  350. if !(input.Scope == string(rbacscope.ScopeSystem) && userCred.HasSystemAdminPrivilege()) {
  351. resources := input.Resources
  352. if len(resources) == 0 {
  353. for resource := range globalTables {
  354. resources = append(resources, resource)
  355. }
  356. }
  357. conditions := []sqlchemy.ICondition{}
  358. for _, resource := range resources {
  359. man, ok := globalTables[resource]
  360. if !ok {
  361. return nil, httperrors.NewNotFoundError("Not support resource %s tag filter", resource)
  362. }
  363. if !man.IsStandaloneManager() {
  364. continue
  365. }
  366. sq := man.Query("id")
  367. query := jsonutils.Marshal(input)
  368. ownerId, queryScope, err, _ := FetchCheckQueryOwnerScope(ctx, userCred, query, man, policy.PolicyActionList, true)
  369. if err != nil {
  370. log.Warningf("FetchCheckQueryOwnerScope.%s error: %v", man.Keyword(), err)
  371. continue
  372. }
  373. sq = man.FilterByOwner(ctx, sq, man, userCred, ownerId, queryScope)
  374. sq = man.FilterBySystemAttributes(sq, userCred, query, queryScope)
  375. sq = man.FilterByHiddenSystemAttributes(sq, userCred, query, queryScope)
  376. conditions = append(conditions, sqlchemy.In(q.Field("obj_id"), sq))
  377. }
  378. if len(conditions) > 0 {
  379. q = q.Filter(sqlchemy.OR(conditions...))
  380. }
  381. }
  382. /*for args, prefix := range map[string]string{"sys_meta": SYS_TAG_PREFIX, "cloud_meta": CLOUD_TAG_PREFIX, "user_meta": USER_TAG_PREFIX} {
  383. if jsonutils.QueryBoolean(query, args, false) {
  384. q = q.Filter(sqlchemy.Startswith(q.Field("key"), prefix))
  385. }
  386. }*/
  387. /*for args, prefix := range map[string]string{"with_sys_meta": SYS_TAG_PREFIX, "with_cloud_meta": CLOUD_TAG_PREFIX, "with_user_meta": USER_TAG_PREFIX} {
  388. if jsonutils.QueryBoolean(query, args, false) {
  389. withConditions = append(withConditions, sqlchemy.Startswith(q.Field("key"), prefix))
  390. }
  391. }*/
  392. return q, nil
  393. }
  394. func (manager *SMetadataManager) GetStringValue(ctx context.Context, model IModel, key string, userCred mcclient.TokenCredential) string {
  395. if !isAllowGetMetadata(ctx, model, userCred) {
  396. return ""
  397. }
  398. if strings.HasPrefix(key, SYSTEM_ADMIN_PREFIX) && (userCred == nil || !IsAllowGetSpec(ctx, rbacscope.ScopeSystem, userCred, model, "metadata")) {
  399. return ""
  400. }
  401. idStr := GetModelIdstr(model)
  402. m := SMetadata{}
  403. err := manager.Query("value").Equals("id", idStr).Equals("key", key).First(&m)
  404. if err == nil {
  405. return m.Value
  406. }
  407. return ""
  408. }
  409. func (manager *SMetadataManager) GetJsonValue(ctx context.Context, model IModel, key string, userCred mcclient.TokenCredential) jsonutils.JSONObject {
  410. if !isAllowGetMetadata(ctx, model, userCred) {
  411. return nil
  412. }
  413. if strings.HasPrefix(key, SYSTEM_ADMIN_PREFIX) && (userCred == nil || !IsAllowGetSpec(ctx, rbacscope.ScopeSystem, userCred, model, "metadata")) {
  414. return nil
  415. }
  416. idStr := GetModelIdstr(model)
  417. m := SMetadata{}
  418. err := manager.Query("value").Equals("id", idStr).Equals("key", key).First(&m)
  419. if err == nil {
  420. json, _ := jsonutils.ParseString(m.Value)
  421. return json
  422. }
  423. return nil
  424. }
  425. type sMetadataChange struct {
  426. Key string
  427. OValue string
  428. NValue string `json:",allowempty"`
  429. }
  430. func (manager *SMetadataManager) RemoveAll(ctx context.Context, model IModel, userCred mcclient.TokenCredential) error {
  431. idStr := GetModelIdstr(model)
  432. if len(idStr) == 0 {
  433. return fmt.Errorf("invalid model")
  434. }
  435. lockman.LockObject(ctx, model)
  436. defer lockman.ReleaseObject(ctx, model)
  437. changes := []sMetadataChange{}
  438. records := make([]SMetadata, 0)
  439. q := manager.Query().Equals("id", idStr)
  440. err := FetchModelObjects(manager, q, &records)
  441. if err != nil {
  442. return fmt.Errorf("find metadata for %s fail: %s", idStr, err)
  443. }
  444. for _, rec := range records {
  445. if err = rec.Delete(ctx, userCred); err != nil {
  446. log.Errorf("remove metadata %v error: %v", rec, err)
  447. continue
  448. }
  449. changes = append(changes, sMetadataChange{Key: rec.Key, OValue: rec.Value})
  450. }
  451. if len(changes) > 0 {
  452. OpsLog.LogEvent(model, ACT_SET_METADATA, jsonutils.Marshal(changes), userCred)
  453. }
  454. return nil
  455. }
  456. func infMap2StrMap(input map[string]interface{}) map[string]string {
  457. output := make(map[string]string)
  458. for k, v := range input {
  459. output[k] = stringutils.Interface2String(v)
  460. }
  461. return output
  462. }
  463. func (manager *SMetadataManager) SetValue(ctx context.Context, obj IModel, key string, value interface{}, userCred mcclient.TokenCredential) error {
  464. return manager.SetValuesWithLog(ctx, obj, map[string]interface{}{key: value}, userCred)
  465. }
  466. func (manager *SMetadataManager) SetValuesWithLog(ctx context.Context, obj IModel, store map[string]interface{}, userCred mcclient.TokenCredential) error {
  467. lockman.LockObject(ctx, obj)
  468. defer lockman.ReleaseObject(ctx, obj)
  469. changes, err := manager.rawSetValues(ctx, obj.Keyword(), obj.GetId(), infMap2StrMap(store), false, "")
  470. if err != nil {
  471. return err
  472. }
  473. if len(changes) > 0 {
  474. OpsLog.LogEvent(obj.GetIModel(), ACT_SET_METADATA, jsonutils.Marshal(changes), userCred)
  475. for _, change := range changes {
  476. if change.Key == RE_BILLING_AT {
  477. desc := obj.GetIModel().GetShortDesc(ctx)
  478. desc.Set("created_at", jsonutils.Marshal(change.NValue))
  479. OpsLog.LogEvent(obj.GetIModel(), ACT_RE_BILLING, desc, userCred)
  480. break
  481. }
  482. }
  483. }
  484. return nil
  485. }
  486. func (manager *SMetadataManager) rawSetValues(ctx context.Context, objType string, objId string, store map[string]string, replace bool, replaceRange string) ([]sMetadataChange, error) {
  487. idStr := getObjectIdstr(objType, objId)
  488. keys := make([]string, 0, len(store))
  489. changes := make([]sMetadataChange, 0)
  490. for key, value := range store {
  491. keys = append(keys, key)
  492. record := SMetadata{}
  493. record.SetModelManager(manager, &record)
  494. err := manager.RawQuery().Equals("id", idStr).Equals("key", key).First(&record) //避免之前设置的tag被删除后再次设置时出现Duplicate entry error
  495. if err != nil {
  496. if errors.Cause(err) != sql.ErrNoRows {
  497. return changes, errors.Wrap(err, "RawQuery")
  498. } else {
  499. record.Deleted = true
  500. }
  501. }
  502. newRecord := SMetadata{}
  503. valStr := value
  504. valStrLower := strings.ToLower(valStr)
  505. if valStrLower == "none" || valStrLower == "null" {
  506. newRecord.Value = record.Value
  507. newRecord.Deleted = true
  508. } else {
  509. newRecord.Value = valStr
  510. newRecord.Deleted = false
  511. }
  512. if record.Deleted == newRecord.Deleted && record.Value == newRecord.Value {
  513. // no changes
  514. continue
  515. }
  516. newRecord.SetModelManager(manager, &newRecord)
  517. newRecord.ObjId = objId
  518. newRecord.ObjType = objType
  519. newRecord.Id = idStr
  520. newRecord.Key = key
  521. if len(record.Id) == 0 {
  522. err = manager.TableSpec().InsertOrUpdate(ctx, &newRecord)
  523. } else {
  524. rV, rD := record.Value, record.Deleted
  525. _, err = Update(&record, func() error {
  526. record.Value = newRecord.Value
  527. record.Key = key
  528. record.Deleted = newRecord.Deleted
  529. return nil
  530. })
  531. record.Value, record.Deleted = rV, rD
  532. }
  533. if err != nil {
  534. return nil, errors.Wrapf(err, "InsertOrUpdate %s=%s", key, valStr)
  535. }
  536. if record.Deleted != newRecord.Deleted {
  537. if record.Deleted {
  538. // create
  539. changes = append(changes, sMetadataChange{Key: key, NValue: valStr})
  540. } else {
  541. // delete
  542. changes = append(changes, sMetadataChange{Key: key, OValue: record.Value})
  543. }
  544. } else {
  545. // change
  546. changes = append(changes, sMetadataChange{Key: key, OValue: record.Value, NValue: valStr})
  547. }
  548. }
  549. if replace {
  550. records := []SMetadata{}
  551. q := manager.Query().Equals("id", idStr).NotLike("key", `\_\_%`) //避免删除系统内置的metadata, _ 在mysql里面有特殊含义,需要转义
  552. // switch replaceRange {
  553. // case USER_TAG_PREFIX:
  554. // q = q.Startswith("key", USER_TAG_PREFIX)
  555. // case CLOUD_TAG_PREFIX:
  556. // q = q.Startswith("key", CLOUD_TAG_PREFIX)
  557. // case SYS_CLOUD_TAG_PREFIX:
  558. // q = q.Startswith("key", SYS_CLOUD_TAG_PREFIX)
  559. // }
  560. q = q.Startswith("key", replaceRange)
  561. q = q.Filter(sqlchemy.NOT(sqlchemy.In(q.Field("key"), keys)))
  562. if err := FetchModelObjects(manager, q, &records); err != nil {
  563. log.Errorf("failed to fetch metadata error: %v", err)
  564. }
  565. for _, rec := range records {
  566. _, err := Update(&rec, func() error {
  567. rec.Deleted = true
  568. return nil
  569. })
  570. if err != nil {
  571. log.Errorf("failed to delete metadata record %s %s %s", objType, objId, rec.Key)
  572. } else {
  573. changes = append(changes, sMetadataChange{Key: rec.Key, OValue: rec.Value})
  574. }
  575. }
  576. }
  577. return changes, nil
  578. }
  579. func (manager *SMetadataManager) SetAllWithoutDelelte(ctx context.Context, obj IModel, store map[string]interface{}, userCred mcclient.TokenCredential) error {
  580. lockman.LockObject(ctx, obj)
  581. defer lockman.ReleaseObject(ctx, obj)
  582. changes, err := manager.rawSetValues(ctx, obj.Keyword(), obj.GetId(), infMap2StrMap(store), false, "")
  583. if err != nil {
  584. return errors.Wrap(err, "setValues")
  585. }
  586. if len(changes) > 0 {
  587. OpsLog.LogEvent(obj.GetIModel(), ACT_SET_METADATA, jsonutils.Marshal(changes), userCred)
  588. }
  589. return nil
  590. }
  591. func (manager *SMetadataManager) SetAll(ctx context.Context, obj IModel, store map[string]interface{}, userCred mcclient.TokenCredential, delRange string) error {
  592. lockman.LockObject(ctx, obj)
  593. defer lockman.ReleaseObject(ctx, obj)
  594. changes, err := manager.rawSetValues(ctx, obj.Keyword(), obj.GetId(), infMap2StrMap(store), true, delRange)
  595. if err != nil {
  596. return errors.Wrap(err, "setValues")
  597. }
  598. if len(changes) > 0 {
  599. OpsLog.LogEvent(obj.GetIModel(), ACT_SET_METADATA, jsonutils.Marshal(changes), userCred)
  600. }
  601. return nil
  602. }
  603. func isAllowGetMetadata(ctx context.Context, obj IModel, userCred mcclient.TokenCredential) bool {
  604. if userCred != nil {
  605. for _, scope := range []rbacscope.TRbacScope{
  606. rbacscope.ScopeSystem,
  607. rbacscope.ScopeDomain,
  608. rbacscope.ScopeProject,
  609. } {
  610. if IsAllowGetSpec(ctx, scope, userCred, obj, "metadata") {
  611. return true
  612. }
  613. }
  614. return false
  615. }
  616. return true
  617. }
  618. type IMetadataGetter interface {
  619. GetId() string
  620. Keyword() string
  621. }
  622. func (manager *SMetadataManager) GetAll(ctx context.Context, obj IMetadataGetter, keys []string, keyPrefix string, userCred mcclient.TokenCredential) (map[string]string, error) {
  623. modelObj, isIModel := obj.(IModel)
  624. if isIModel && !isAllowGetMetadata(ctx, modelObj, userCred) {
  625. return map[string]string{}, nil
  626. }
  627. meta, err := manager.rawGetAll(obj.Keyword(), obj.GetId(), keys, keyPrefix)
  628. if err != nil {
  629. return nil, errors.Wrap(err, "rawGetAll")
  630. }
  631. ret := make(map[string]string)
  632. for k, v := range meta {
  633. if strings.HasPrefix(k, SYSTEM_ADMIN_PREFIX) && (userCred == nil || (isIModel && !IsAllowGetSpec(ctx, rbacscope.ScopeSystem, userCred, modelObj, "metadata"))) {
  634. continue
  635. }
  636. ret[k] = v
  637. }
  638. return ret, nil
  639. }
  640. func (manager *SMetadataManager) rawGetAll(objType, objId string, keys []string, keyPrefix string) (map[string]string, error) {
  641. idStr := getObjectIdstr(objType, objId)
  642. records := make([]SMetadata, 0)
  643. q := manager.Query().Equals("id", idStr)
  644. if len(keys) > 0 {
  645. q = q.In("key", keys)
  646. }
  647. if len(keyPrefix) > 0 {
  648. q = q.Startswith("key", keyPrefix)
  649. }
  650. err := FetchModelObjects(manager, q, &records)
  651. if err != nil {
  652. return nil, errors.Wrap(err, "FetchModelObjects")
  653. }
  654. ret := make(map[string]string)
  655. for _, rec := range records {
  656. if len(rec.Value) > 0 || strings.HasPrefix(rec.Key, USER_TAG_PREFIX) || strings.HasPrefix(rec.Key, CLOUD_TAG_PREFIX) {
  657. ret[rec.Key] = rec.Value
  658. }
  659. }
  660. return ret, nil
  661. }
  662. /*func (manager *SMetadataManager) IsSystemAdminKey(key string) bool {
  663. return isMetadataKeySystemAdmin(key)
  664. }*/
  665. func isMetadataLoginKey(key string) bool {
  666. return strings.HasPrefix(key, "login_")
  667. }
  668. func isMetadataKeySystemAdmin(key string) bool {
  669. return strings.HasPrefix(key, SYSTEM_ADMIN_PREFIX)
  670. }
  671. func isMetadataKeyPrivateKey(key string) bool {
  672. for _, k := range []string{"admin", "project"} {
  673. for _, v := range []string{"ssh-private-key", "ssh-public-key"} {
  674. if key == fmt.Sprintf("%s-%s", k, v) {
  675. return true
  676. }
  677. }
  678. }
  679. return strings.HasPrefix(key, SYSTEM_ADMIN_PREFIX)
  680. }
  681. func isMetadataKeySysTag(key string) bool {
  682. return strings.HasPrefix(key, SYS_TAG_PREFIX)
  683. }
  684. func (manager *SMetadataManager) GetSysadminKey(key string) string {
  685. return fmt.Sprintf("%s%s", SYSTEM_ADMIN_PREFIX, key)
  686. }
  687. func IsMetadataKeyVisible(key string) bool {
  688. return !(isMetadataKeySysTag(key) || isMetadataKeySystemAdmin(key) || isMetadataKeyPrivateKey(key))
  689. }
  690. func GetVisibleMetadata(ctx context.Context, model IStandaloneModel, userCred mcclient.TokenCredential) (map[string]string, error) {
  691. metaData, err := model.GetAllMetadata(ctx, userCred)
  692. if err != nil {
  693. return nil, err
  694. }
  695. for _, key := range model.StandaloneModelManager().GetMetadataHiddenKeys() {
  696. delete(metaData, key)
  697. }
  698. for key := range metaData {
  699. if !IsMetadataKeyVisible(key) {
  700. delete(metaData, key)
  701. }
  702. }
  703. return metaData, nil
  704. }
  705. func metaList2Map(manager IMetadataBaseModelManager, userCred mcclient.TokenCredential, metaList []SMetadata) map[string]string {
  706. metaMap := make(map[string]string)
  707. hiddenKeys := manager.GetMetadataHiddenKeys()
  708. for _, meta := range metaList {
  709. if IsMetadataKeyVisible(meta.Key) && !utils.IsInStringArray(meta.Key, hiddenKeys) {
  710. metaMap[meta.Key] = meta.Value
  711. }
  712. }
  713. return metaMap
  714. }
  715. func CopyTags(ctx context.Context, objType string, keys1 []string, values []string, keys2 []string) error {
  716. return Metadata.copyTags(ctx, objType, keys1, values, keys2)
  717. }
  718. func (manager *SMetadataManager) copyTags(ctx context.Context, objType string, keys1 []string, values []string, keys2 []string) error {
  719. for i := 0; i < len(keys1) && i < len(values) && i < len(keys2); i++ {
  720. key1 := keys1[i]
  721. key2 := keys2[i]
  722. value := values[i]
  723. q := manager.Query("obj_id").Equals("obj_type", objType).Equals("key", key1).Equals("value", value)
  724. q2 := manager.Query("obj_id").Equals("obj_type", objType).Equals("key", key2).Equals("value", value)
  725. q = q.NotIn("obj_id", q2.SubQuery())
  726. results := []struct {
  727. ObjId string
  728. }{}
  729. err := q.All(&results)
  730. if err != nil {
  731. return errors.Wrapf(err, "copy key %s value %s to %s", key1, value, key2)
  732. }
  733. for _, result := range results {
  734. record := SMetadata{}
  735. record.SetModelManager(manager, &record)
  736. record.ObjId = result.ObjId
  737. record.ObjType = objType
  738. record.Id = getObjectIdstr(objType, result.ObjId)
  739. record.Key = key2
  740. record.Value = value
  741. err := manager.TableSpec().InsertOrUpdate(ctx, &record)
  742. if err != nil {
  743. return errors.Wrapf(err, "insert %s", jsonutils.Marshal(record))
  744. }
  745. }
  746. }
  747. return nil
  748. }