modelbase.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  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. "net/http"
  20. "strings"
  21. "time"
  22. "yunion.io/x/jsonutils"
  23. "yunion.io/x/pkg/errors"
  24. "yunion.io/x/pkg/gotypes"
  25. "yunion.io/x/pkg/object"
  26. "yunion.io/x/pkg/util/rbacscope"
  27. "yunion.io/x/pkg/util/version"
  28. "yunion.io/x/sqlchemy"
  29. "yunion.io/x/sqlchemy/backends/clickhouse"
  30. "yunion.io/x/onecloud/pkg/apis"
  31. "yunion.io/x/onecloud/pkg/appsrv"
  32. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  33. "yunion.io/x/onecloud/pkg/cloudcommon/policy"
  34. "yunion.io/x/onecloud/pkg/httperrors"
  35. "yunion.io/x/onecloud/pkg/mcclient"
  36. "yunion.io/x/onecloud/pkg/util/dbutils"
  37. "yunion.io/x/onecloud/pkg/util/logclient"
  38. "yunion.io/x/onecloud/pkg/util/splitable"
  39. "yunion.io/x/onecloud/pkg/util/stringutils2"
  40. )
  41. const (
  42. COLUMN_RECORD_CHECKSUM = "record_checksum"
  43. COLUMN_UPDATE_VERSION = "update_version"
  44. COLUMN_UPDATED_AT = "updated_at"
  45. )
  46. type SModelBase struct {
  47. object.SObject
  48. manager IModelManager `ignore:"true"` // pointer to modelmanager
  49. }
  50. type SModelBaseManager struct {
  51. object.SObject
  52. tableSpec ITableSpec
  53. keyword string
  54. keywordPlural string
  55. alias string
  56. aliasPlural string
  57. extraHook IModelManagerExtraHook
  58. }
  59. func NewModelBaseManager(model interface{}, tableName string, keyword string, keywordPlural string) SModelBaseManager {
  60. return NewModelBaseManagerWithDBName(model, tableName, keyword, keywordPlural, sqlchemy.DefaultDB)
  61. }
  62. func NewModelBaseManagerWithDBName(model interface{}, tableName string, keyword string, keywordPlural string, dbName sqlchemy.DBName) SModelBaseManager {
  63. return NewModelBaseManagerWithSplitableDBName(model, tableName, keyword, keywordPlural, "", "", 0, 0, dbName)
  64. }
  65. func NewModelBaseManagerWithSplitable(model interface{}, tableName string, keyword string, keywordPlural string, indexField string, dateField string, maxDuration time.Duration, maxSegments int) SModelBaseManager {
  66. return NewModelBaseManagerWithSplitableDBName(model, tableName, keyword, keywordPlural, indexField, dateField, maxDuration, maxSegments, sqlchemy.DefaultDB)
  67. }
  68. func NewModelBaseManagerWithSplitableDBName(model interface{}, tableName string, keyword string, keywordPlural string, indexField string, dateField string, maxDuration time.Duration, maxSegments int, dbName sqlchemy.DBName) SModelBaseManager {
  69. ts := newTableSpec(model, tableName, indexField, dateField, maxDuration, maxSegments, dbName)
  70. modelMan := SModelBaseManager{
  71. tableSpec: ts,
  72. keyword: keyword,
  73. keywordPlural: keywordPlural,
  74. extraHook: NewEmptyExtraHook(),
  75. }
  76. return modelMan
  77. }
  78. func NewModelBaseManagerWithClickhouseMapping(manager IModelManager, keyword, keywordPlural string) SModelBaseManager {
  79. ots := manager.TableSpec()
  80. var extraOpts sqlchemy.TableExtraOptions
  81. switch consts.DefaultDBDialect() {
  82. case "mysql":
  83. cfg := dbutils.ParseMySQLConnStr(consts.DefaultDBConnStr())
  84. err := cfg.Validate()
  85. if err != nil {
  86. panic(fmt.Sprintf("invalid mysql connection string %s", consts.DefaultDBConnStr()))
  87. }
  88. extraOpts = clickhouse.MySQLExtraOptions(cfg.Hostport, cfg.Database, ots.Name(), cfg.Username, cfg.Password)
  89. default:
  90. panic(fmt.Sprintf("unsupport dialect %s to be backend of clickhouse", consts.DefaultDBDialect()))
  91. }
  92. nts := newClickhouseTableSpecFromMySQL(ots, ots.Name(), ClickhouseDB, extraOpts)
  93. modelMan := SModelBaseManager{
  94. tableSpec: nts,
  95. keyword: keyword,
  96. keywordPlural: keywordPlural,
  97. }
  98. return modelMan
  99. }
  100. func (manager *SModelBaseManager) CreateByInsertOrUpdate() bool {
  101. return true
  102. }
  103. func (manager *SModelBaseManager) IsStandaloneManager() bool {
  104. return false
  105. }
  106. func (manager *SModelBaseManager) GetIModelManager() IModelManager {
  107. virt := manager.GetVirtualObject()
  108. if virt == nil {
  109. panic(fmt.Sprintf("[%s] Forgot to call SetVirtualObject?", manager.Keyword()))
  110. }
  111. r, ok := virt.(IModelManager)
  112. if !ok {
  113. panic(fmt.Sprintf("Cannot convert virtual object to IModelManager: %#v", virt))
  114. }
  115. return r
  116. }
  117. func (manager *SModelBaseManager) GetImmutableInstance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) IModelManager {
  118. return manager.GetIModelManager()
  119. }
  120. func (manager *SModelBaseManager) GetMutableInstance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) IModelManager {
  121. return manager.GetIModelManager()
  122. }
  123. func (manager *SModelBaseManager) PrepareQueryContext(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) context.Context {
  124. return ctx
  125. }
  126. func (manager *SModelBaseManager) SetAlias(alias string, aliasPlural string) {
  127. manager.alias = alias
  128. manager.aliasPlural = aliasPlural
  129. }
  130. func (manager *SModelBaseManager) TableSpec() ITableSpec {
  131. return manager.tableSpec
  132. }
  133. func (manager *SModelBaseManager) GetSplitTable() *splitable.SSplitTableSpec {
  134. return manager.TableSpec().GetSplitTable()
  135. }
  136. func (manager *SModelBaseManager) Keyword() string {
  137. return manager.keyword
  138. }
  139. func (manager *SModelBaseManager) KeywordPlural() string {
  140. return manager.keywordPlural
  141. }
  142. func (manager *SModelBaseManager) GetContextManagers() [][]IModelManager {
  143. return nil
  144. }
  145. func (manager *SModelBaseManager) Alias() string {
  146. return manager.alias
  147. }
  148. func (manager *SModelBaseManager) AliasPlural() string {
  149. return manager.aliasPlural
  150. }
  151. func (manager *SModelBaseManager) ValidateName(name string) error {
  152. return nil
  153. }
  154. func (manager *SModelBaseManager) EnableGenerateName() bool {
  155. return true
  156. }
  157. func (manager *SModelBaseManager) HasName() bool {
  158. return false
  159. }
  160. func (model *SModelBase) MarkDeletePreventionOn() {
  161. return
  162. }
  163. func (model *SModelBase) MarkDeletePreventionOff() {
  164. return
  165. }
  166. // list hooks
  167. func (manager *SModelBaseManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
  168. return false
  169. }
  170. func (manager *SModelBaseManager) ValidateListConditions(ctx context.Context, userCred mcclient.TokenCredential, query *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
  171. return query, nil
  172. }
  173. func (manager *SModelBaseManager) ListItemFilter(
  174. ctx context.Context,
  175. q *sqlchemy.SQuery,
  176. userCred mcclient.TokenCredential,
  177. input apis.ModelBaseListInput,
  178. ) (*sqlchemy.SQuery, error) {
  179. return q, nil
  180. }
  181. func (manager *SModelBaseManager) OrderByExtraFields(
  182. ctx context.Context,
  183. q *sqlchemy.SQuery,
  184. userCred mcclient.TokenCredential,
  185. input apis.ModelBaseListInput,
  186. ) (*sqlchemy.SQuery, error) {
  187. return q, nil
  188. }
  189. func (manager *SModelBaseManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  190. // no field match
  191. return q, httperrors.ErrNotFound
  192. }
  193. func (manager *SModelBaseManager) QueryDistinctExtraFields(q *sqlchemy.SQuery, resource string, fields []string) (*sqlchemy.SQuery, error) {
  194. return q, httperrors.ErrNotImplemented
  195. }
  196. func (manager *SModelBaseManager) CustomizeFilterList(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*CustomizeListFilters, error) {
  197. return NewCustomizeListFilters(), nil
  198. }
  199. func (manager *SModelBaseManager) ExtraSearchConditions(ctx context.Context, q *sqlchemy.SQuery, like string) []sqlchemy.ICondition {
  200. return nil
  201. }
  202. func (manager *SModelBaseManager) NewQuery(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, useRawQuery bool) *sqlchemy.SQuery {
  203. if useRawQuery {
  204. return manager.Query()
  205. } else {
  206. return manager.GetIModelManager().Query()
  207. }
  208. }
  209. // fetch hook
  210. func (manager *SModelBaseManager) getTable() *sqlchemy.STable {
  211. return manager.TableSpec().Instance()
  212. }
  213. func (manager *SModelBaseManager) Query(fieldNames ...string) *sqlchemy.SQuery {
  214. instance := manager.getTable()
  215. fields := make([]sqlchemy.IQueryField, len(fieldNames))
  216. for i, f := range fieldNames {
  217. fields[i] = instance.Field(f)
  218. }
  219. return instance.Query(fields...)
  220. }
  221. func (manager *SModelBaseManager) RawQuery(fieldNames ...string) *sqlchemy.SQuery {
  222. return manager.Query(fieldNames...)
  223. }
  224. func (manager *SModelBaseManager) FilterById(q *sqlchemy.SQuery, idStr string) *sqlchemy.SQuery {
  225. return q
  226. }
  227. func (manager *SModelBaseManager) FilterByNotId(q *sqlchemy.SQuery, idStr string) *sqlchemy.SQuery {
  228. return q
  229. }
  230. func (manager *SModelBaseManager) FilterByName(q *sqlchemy.SQuery, name string) *sqlchemy.SQuery {
  231. return q
  232. }
  233. func (manager *SModelBaseManager) FilterByOwner(ctx context.Context, q *sqlchemy.SQuery, man FilterByOwnerProvider, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, scope rbacscope.TRbacScope) *sqlchemy.SQuery {
  234. return q
  235. }
  236. func (manager *SModelBaseManager) FilterBySystemAttributes(q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject, scope rbacscope.TRbacScope) *sqlchemy.SQuery {
  237. return q
  238. }
  239. func (manager *SModelBaseManager) FilterByHiddenSystemAttributes(q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject, scope rbacscope.TRbacScope) *sqlchemy.SQuery {
  240. return q
  241. }
  242. func (manager *SModelBaseManager) FilterByUniqValues(q *sqlchemy.SQuery, uniqValues jsonutils.JSONObject) *sqlchemy.SQuery {
  243. return q
  244. }
  245. func (manager *SModelBaseManager) FetchById(idStr string) (IModel, error) {
  246. return nil, sql.ErrNoRows
  247. }
  248. func (manager *SModelBaseManager) FetchByName(ctx context.Context, userCred mcclient.IIdentityProvider, idStr string) (IModel, error) {
  249. return nil, sql.ErrNoRows
  250. }
  251. func (manager *SModelBaseManager) FetchByIdOrName(ctx context.Context, userCred mcclient.IIdentityProvider, idStr string) (IModel, error) {
  252. return nil, sql.ErrNoRows
  253. }
  254. // create hooks
  255. func (manager *SModelBaseManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
  256. return false
  257. }
  258. func (manager *SModelBaseManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input apis.ModelBaseCreateInput) (apis.ModelBaseCreateInput, error) {
  259. return input, nil
  260. }
  261. func (manager *SModelBaseManager) OnCreateComplete(ctx context.Context, items []IModel, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data []jsonutils.JSONObject) {
  262. // do nothing
  263. }
  264. func (manager *SModelBaseManager) PerformAction(ctx context.Context, userCred mcclient.TokenCredential, action string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  265. return nil, httperrors.NewActionNotFoundError("Action %s not found, please check service version, current version: %s", action, version.GetShortString())
  266. }
  267. func (manager *SModelBaseManager) InitializeData() error {
  268. return nil
  269. }
  270. func (manager *SModelBaseManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) {
  271. q = q.AppendField(q.QueryFields()...)
  272. return q, nil
  273. }
  274. func (manager *SModelBaseManager) GetExportExtraKeys(ctx context.Context, keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict {
  275. return jsonutils.NewDict()
  276. }
  277. func (manager *SModelBaseManager) CustomizeHandlerInfo(info *appsrv.SHandlerInfo) {
  278. info.SetProcessTimeoutCallback(manager.GetIModelManager().SetHandlerProcessTimeout)
  279. }
  280. func (manager *SModelBaseManager) SetHandlerProcessTimeout(info *appsrv.SHandlerInfo, r *http.Request) time.Duration {
  281. splitableExportPath := fmt.Sprintf("/%s/splitable-export", manager.KeywordPlural())
  282. if r.Method == http.MethodGet && (len(r.URL.Query().Get("export_keys")) > 0 ||
  283. r.URL.Query().Has("force_no_paging") ||
  284. strings.HasSuffix(r.URL.Path, splitableExportPath)) {
  285. return time.Hour * 2
  286. }
  287. return -time.Second
  288. }
  289. func (manager *SModelBaseManager) FetchCreateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error) {
  290. return nil, nil
  291. }
  292. func (manager *SModelBaseManager) FetchUpdateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error) {
  293. return nil, nil
  294. }
  295. func (manager *SModelBaseManager) IsCustomizedGetDetailsBody() bool {
  296. return false
  297. }
  298. func (manager *SModelBaseManager) ListSkipLog(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
  299. return false
  300. }
  301. func (manager *SModelBaseManager) GetSkipLog(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
  302. return false
  303. }
  304. func (manager *SModelBaseManager) FetchCustomizeColumns(
  305. ctx context.Context,
  306. userCred mcclient.TokenCredential,
  307. query jsonutils.JSONObject,
  308. objs []interface{},
  309. fields stringutils2.SSortedStrings,
  310. isList bool,
  311. ) []apis.ModelBaseDetails {
  312. ret := make([]apis.ModelBaseDetails, len(objs))
  313. return ret
  314. }
  315. func (manager *SModelBaseManager) FetchOwnerId(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) {
  316. return nil, nil
  317. }
  318. func (manager *SModelBaseManager) FetchUniqValues(ctx context.Context, data jsonutils.JSONObject) jsonutils.JSONObject {
  319. return nil
  320. }
  321. func (manager *SModelBaseManager) NamespaceScope() rbacscope.TRbacScope {
  322. return rbacscope.ScopeSystem
  323. }
  324. func (manager *SModelBaseManager) ResourceScope() rbacscope.TRbacScope {
  325. return rbacscope.ScopeSystem
  326. }
  327. func (manager *SModelBaseManager) GetPagingConfig() *SPagingConfig {
  328. return nil
  329. }
  330. // +onecloud:swagger-gen-ignore
  331. func (manager *SModelBaseManager) GetPropertyDistinctField(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  332. im, ok := manager.GetVirtualObject().(IModelManager)
  333. if !ok {
  334. im = manager
  335. }
  336. fn, err := query.GetArray("field")
  337. efs, _ := query.GetArray("extra_field")
  338. fields := make([]string, len(fn))
  339. // validate field
  340. for i, f := range fn {
  341. fields[i], err = f.GetString()
  342. if err != nil {
  343. return nil, httperrors.NewInputParameterError("can't get string field")
  344. }
  345. var hasField = false
  346. for _, field := range manager.getTable().Fields() {
  347. if field.Name() == fields[i] {
  348. hasField = true
  349. break
  350. }
  351. }
  352. if !hasField {
  353. return nil, httperrors.NewBadRequestError("model has no field %s", fields[i])
  354. }
  355. }
  356. q := im.Query()
  357. q, err = ListItemQueryFilters(im, ctx, q, userCred, query, policy.PolicyActionList)
  358. if err != nil {
  359. return nil, err
  360. }
  361. var (
  362. backupQuery = *q
  363. res = jsonutils.NewDict()
  364. )
  365. // query field
  366. for i := 0; i < len(fields); i++ {
  367. var nq = backupQuery.SubQuery().Query()
  368. nq.AppendField(nq.Field(fields[i]))
  369. of, err := nq.Distinct().AllStringMap()
  370. if err == sql.ErrNoRows {
  371. continue
  372. }
  373. if err != nil && err != sql.ErrNoRows {
  374. return nil, httperrors.NewInternalServerError("Query database error %s", err)
  375. }
  376. ofa := make([]string, len(of))
  377. for j := 0; j < len(of); j++ {
  378. ofa[j] = of[j][fields[i]]
  379. }
  380. res.Set(fields[i], jsonutils.Marshal(ofa))
  381. }
  382. // query extra field
  383. for i := 0; i < len(efs); i++ {
  384. nq := backupQuery.SubQuery().Query()
  385. fe, _ := efs[i].GetString()
  386. nqp, err := im.QueryDistinctExtraField(nq, fe)
  387. if err != nil {
  388. continue
  389. }
  390. ef, err := nqp.AllStringMap()
  391. if errors.Cause(err) == sql.ErrNoRows {
  392. continue
  393. }
  394. efa := make([]string, len(ef))
  395. for i := 0; i < len(ef); i++ {
  396. efa[i] = ef[i][fe]
  397. }
  398. if err != nil && err != sql.ErrNoRows {
  399. return nil, httperrors.NewInternalServerError("Query database error %s", err)
  400. }
  401. res.Set(fe, jsonutils.Marshal(efa))
  402. }
  403. return res, nil
  404. }
  405. // +onecloud:swagger-gen-ignore
  406. func (manager *SModelBaseManager) GetPropertyDistinctFields(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  407. im, ok := manager.GetVirtualObject().(IModelManager)
  408. if !ok {
  409. im = manager
  410. }
  411. input := &apis.DistinctFieldsInput{}
  412. query.Unmarshal(input)
  413. if len(input.Field) == 0 && (len(input.ExtraResource) == 0 || len(input.ExtraField) == 0) {
  414. return nil, httperrors.NewMissingParameterError("field")
  415. }
  416. // validate field
  417. for _, fd := range input.Field {
  418. var hasField = false
  419. for _, field := range manager.getTable().Fields() {
  420. if field.Name() == fd {
  421. hasField = true
  422. break
  423. }
  424. }
  425. if !hasField {
  426. return nil, httperrors.NewBadRequestError("model has no field %s", fd)
  427. }
  428. }
  429. var err error
  430. q := im.Query()
  431. q, err = ListItemQueryFilters(im, ctx, q, userCred, query, policy.PolicyActionList)
  432. if err != nil {
  433. return nil, err
  434. }
  435. result := jsonutils.NewDict()
  436. fields := jsonutils.NewArray()
  437. if len(input.Field) > 0 {
  438. sq := q.Copy().ResetFields()
  439. // query field
  440. for i := 0; i < len(input.Field); i++ {
  441. sq = sq.AppendField(sq.Field(input.Field[i]))
  442. }
  443. rows, err := sq.Distinct().Rows()
  444. if err != nil {
  445. return nil, err
  446. }
  447. defer rows.Close()
  448. for rows.Next() {
  449. mMap, err := sq.Row2Map(rows)
  450. if err != nil {
  451. return nil, errors.Wrapf(err, "Row2Map")
  452. }
  453. fields.Add(jsonutils.Marshal(mMap))
  454. }
  455. }
  456. result.Set("fields", fields)
  457. extraFields := jsonutils.NewArray()
  458. if len(input.ExtraResource) > 0 && len(input.ExtraField) > 0 {
  459. // query extra field
  460. sq := q.Copy().ResetFields()
  461. em := GetModelManager(input.ExtraResource)
  462. if gotypes.IsNil(em) {
  463. return nil, httperrors.NewInputParameterError("invalid extra_resource %s", input.ExtraResource)
  464. }
  465. for _, field := range input.ExtraField {
  466. if gotypes.IsNil(em.TableSpec().ColumnSpec(field)) {
  467. return nil, httperrors.NewInputParameterError("resource %s does not have field %s", input.ExtraResource, field)
  468. }
  469. }
  470. sq, err := im.QueryDistinctExtraFields(sq, input.ExtraResource, input.ExtraField)
  471. if err != nil {
  472. return nil, err
  473. }
  474. rows, err := sq.Distinct().Rows()
  475. if err != nil {
  476. return nil, err
  477. }
  478. defer rows.Close()
  479. for rows.Next() {
  480. mMap, err := sq.Row2Map(rows)
  481. if err != nil {
  482. return nil, errors.Wrapf(err, "Row2Map")
  483. }
  484. extraFields.Add(jsonutils.Marshal(mMap))
  485. }
  486. }
  487. result.Set("extra_fields", extraFields)
  488. return result, nil
  489. }
  490. func (manager *SModelBaseManager) BatchPreValidate(
  491. ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider,
  492. query jsonutils.JSONObject, data *jsonutils.JSONDict, count int,
  493. ) error {
  494. return nil
  495. }
  496. func (manager *SModelBaseManager) BatchCreateValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
  497. return data, nil
  498. }
  499. func (manager *SModelBaseManager) OnCreateFailed(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  500. return nil
  501. }
  502. func (manager *SModelBaseManager) GetI18N(ctx context.Context, idstr string, resObj jsonutils.JSONObject) *jsonutils.JSONDict {
  503. return nil
  504. }
  505. // +onecloud:swagger-gen-ignore
  506. func (manager *SModelBaseManager) GetPropertySplitable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  507. stable := manager.GetIModelManager().GetImmutableInstance(ctx, userCred, query).GetSplitTable()
  508. if stable == nil {
  509. // generate a fake metadata tbl record
  510. man := manager.GetIModelManager().GetImmutableInstance(ctx, userCred, query)
  511. subq := man.Query().SubQuery()
  512. q := subq.Query(
  513. sqlchemy.MIN("start", subq.Field("id")),
  514. sqlchemy.MAX("end", subq.Field("id")),
  515. sqlchemy.MIN("start_date", subq.Field("ops_time")),
  516. sqlchemy.MAX("end_date", subq.Field("ops_time")),
  517. sqlchemy.COUNT("count", subq.Field("id")),
  518. sqlchemy.MIN("created_at", subq.Field("ops_time")),
  519. )
  520. meta := splitable.STableMetadata{
  521. Id: 1,
  522. Table: "action_tbl",
  523. }
  524. err := q.First(&meta)
  525. if err != nil {
  526. return nil, errors.Wrap(err, "Query metadata")
  527. }
  528. metas := []splitable.STableMetadata{meta}
  529. return jsonutils.Marshal(metas), nil
  530. }
  531. metas, err := stable.GetTableMetas()
  532. if err != nil {
  533. return nil, errors.Wrap(err, "GetTableMetas")
  534. }
  535. return jsonutils.Marshal(metas), nil
  536. }
  537. // +onecloud:swagger-gen-ignore
  538. func (manager *SModelBaseManager) GetPropertySplitableExport(ctx context.Context, userCred mcclient.TokenCredential, input apis.SplitTableExportInput) (jsonutils.JSONObject, error) {
  539. splitable := manager.GetIModelManager().GetImmutableInstance(ctx, userCred, jsonutils.Marshal(input)).GetSplitTable()
  540. if splitable == nil {
  541. return nil, errors.Wrap(httperrors.ErrNotSupported, "not splitable")
  542. }
  543. if len(input.Table) == 0 {
  544. return nil, httperrors.NewMissingParameterError("table")
  545. }
  546. metas, err := splitable.GetTableMetas()
  547. if err != nil {
  548. return nil, errors.Wrap(err, "GetTableMetas")
  549. }
  550. for i := 0; i < len(metas); i += 1 {
  551. if metas[i].Table == input.Table {
  552. if input.Limit <= 0 {
  553. input.Limit = apis.MAX_SPLITABLE_EXPORT_LIMIT
  554. }
  555. if input.Offset < 0 {
  556. input.Offset = 0
  557. }
  558. q := splitable.GetTableSpec(metas[i]).Query().Limit(input.Limit).Offset(input.Offset)
  559. resp, err := q.AllStringMap()
  560. if err != nil {
  561. return nil, errors.Wrapf(err, "q.AllStringMap")
  562. }
  563. exportId := fmt.Sprintf("%s(%d-%d)", metas[i].Table, metas[i].Start, metas[i].End)
  564. obj := logclient.NewSimpleObject(exportId, exportId, manager.Keyword())
  565. logclient.AddActionLogWithContext(ctx, obj, logclient.ACT_EXPORT, nil, userCred, true)
  566. return jsonutils.Marshal(resp), nil
  567. }
  568. }
  569. return nil, httperrors.NewResourceNotFoundError("table %s not found", input.Table)
  570. }
  571. // +onecloud:swagger-gen-ignore
  572. func (manager *SModelBaseManager) PerformPurgeSplitable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PurgeSplitTableInput) (jsonutils.JSONObject, error) {
  573. splitable := manager.GetIModelManager().GetImmutableInstance(ctx, userCred, query).GetSplitTable()
  574. if splitable == nil {
  575. return jsonutils.Marshal(map[string][]string{"tables": {}}), nil
  576. }
  577. ret, err := splitable.Purge(input.Tables)
  578. if err != nil {
  579. return nil, errors.Wrapf(err, "Purge")
  580. }
  581. return jsonutils.Marshal(map[string][]string{"tables": ret}), nil
  582. }
  583. func (manager *SModelBaseManager) CustomizedTotalCount(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, totalQ *sqlchemy.SQuery) (int, jsonutils.JSONObject, error) {
  584. ret := apis.TotalCountBase{}
  585. err := totalQ.First(&ret)
  586. if err != nil {
  587. return -1, nil, errors.Wrapf(err, "SModelBaseManager Query total %s", totalQ.DebugString())
  588. }
  589. return ret.Count, nil, nil
  590. }
  591. func (manager *SModelBaseManager) RegisterExtraHook(eh IModelManagerExtraHook) {
  592. manager.extraHook = eh
  593. }
  594. func (manager *SModelBaseManager) GetExtraHook() IModelManagerExtraHook {
  595. return manager.extraHook
  596. }
  597. func (model SModelBase) GetId() string {
  598. return ""
  599. }
  600. func (model SModelBase) Keyword() string {
  601. return model.GetModelManager().Keyword()
  602. }
  603. func (model SModelBase) KeywordPlural() string {
  604. return model.GetModelManager().KeywordPlural()
  605. }
  606. func (model SModelBase) GetName() string {
  607. return ""
  608. }
  609. func (model *SModelBase) GetUpdatedAt() time.Time {
  610. return time.Time{}
  611. }
  612. func (model *SModelBase) GetUpdateVersion() int {
  613. return 0
  614. }
  615. func (model *SModelBase) GetDeleted() bool {
  616. return false
  617. }
  618. func (model *SModelBase) SetModelManager(man IModelManager, virtual IModel) {
  619. model.manager = man
  620. model.SetVirtualObject(virtual)
  621. }
  622. func (model *SModelBase) GetModelManager() IModelManager {
  623. return model.manager
  624. }
  625. func (model *SModelBase) GetIModel() IModel {
  626. return model.GetVirtualObject().(IModel)
  627. }
  628. func (model *SModelBase) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
  629. desc := jsonutils.NewDict()
  630. desc.Add(jsonutils.NewString(model.Keyword()), "res_name")
  631. return desc
  632. }
  633. func (model *SModelBase) GetShortDescV2(ctx context.Context) *apis.ModelBaseShortDescDetail {
  634. return &apis.ModelBaseShortDescDetail{ResName: model.Keyword()}
  635. }
  636. func (model *SModelBase) GetExtraDetailsHeaders(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) map[string]string {
  637. return nil
  638. }
  639. // create hooks
  640. func (model *SModelBase) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  641. return nil
  642. }
  643. func (model *SModelBase) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  644. }
  645. func (model *SModelBase) PerformAction(ctx context.Context, userCred mcclient.TokenCredential, action string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  646. return nil, httperrors.NewActionNotFoundError("Action %s not found, please check service version, current version: %s", action, version.GetShortString())
  647. }
  648. func (model *SModelBase) PreCheckPerformAction(
  649. ctx context.Context, userCred mcclient.TokenCredential,
  650. action string, query jsonutils.JSONObject, data jsonutils.JSONObject,
  651. ) error {
  652. return nil
  653. }
  654. // update hooks
  655. func (model *SModelBase) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool {
  656. return false
  657. }
  658. func (model *SModelBase) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.ModelBaseUpdateInput) (apis.ModelBaseUpdateInput, error) {
  659. return input, nil
  660. }
  661. func (model *SModelBase) PreUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  662. // do nothing
  663. }
  664. func (model *SModelBase) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  665. // do nothing
  666. }
  667. // delete hooks
  668. func (model *SModelBase) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
  669. return false
  670. }
  671. func (model *SModelBase) ValidateUpdateCondition(ctx context.Context) error {
  672. return nil
  673. }
  674. func (model *SModelBase) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
  675. return nil
  676. }
  677. func (model *SModelBase) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  678. // do nothing
  679. return nil
  680. }
  681. func (model *SModelBase) cleanModelUsages(ctx context.Context, userCred mcclient.TokenCredential) {
  682. usages := model.GetIModel().GetUsages()
  683. if CancelUsages != nil && len(usages) > 0 {
  684. CancelUsages(ctx, userCred, usages)
  685. }
  686. }
  687. func (model *SModelBase) RecoverUsages(ctx context.Context, userCred mcclient.TokenCredential) {
  688. usages := model.GetIModel().GetUsages()
  689. if AddUsages != nil && len(usages) > 0 {
  690. AddUsages(ctx, userCred, usages)
  691. }
  692. }
  693. func (model *SModelBase) PreDelete(ctx context.Context, userCred mcclient.TokenCredential) {
  694. // clean usage on predelete
  695. // clean usage before fakedelete for pending delete models
  696. model.cleanModelUsages(ctx, userCred)
  697. }
  698. func (model *SModelBase) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
  699. // do nothing
  700. }
  701. func (model *SModelBase) MarkDelete() error {
  702. // do nothing
  703. return nil
  704. }
  705. func (model *SModelBase) MarkPendingDeleted() {
  706. return
  707. }
  708. func (model *SModelBase) CancelPendingDeleted() {
  709. return
  710. }
  711. func (model *SModelBase) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
  712. return nil
  713. }
  714. func (model *SModelBase) GetOwnerId() mcclient.IIdentityProvider {
  715. return nil
  716. }
  717. func (model *SModelBase) GetUniqValues() jsonutils.JSONObject {
  718. return nil
  719. }
  720. func (model *SModelBase) IsSharable(ownerId mcclient.IIdentityProvider) bool {
  721. return false
  722. }
  723. func (model *SModelBase) CustomizedGetDetailsBody(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  724. return nil, nil
  725. }
  726. func (model *SModelBase) UpdateInContext(ctx context.Context, userCred mcclient.TokenCredential, ctxObjs []IModel, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  727. return nil, nil
  728. }
  729. func (model *SModelBase) DeleteInContext(ctx context.Context, userCred mcclient.TokenCredential, ctxObjs []IModel, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  730. return nil, nil
  731. }
  732. func (model *SModelBase) GetUsages() []IUsage {
  733. return nil
  734. }
  735. func (model *SModelBase) GetI18N(ctx context.Context) *jsonutils.JSONDict {
  736. return nil
  737. }
  738. type SEmptyExtraHook struct{}
  739. func NewEmptyExtraHook() *SEmptyExtraHook {
  740. return new(SEmptyExtraHook)
  741. }
  742. func (e SEmptyExtraHook) AfterPostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, model IModel, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  743. return nil
  744. }
  745. func (e SEmptyExtraHook) AfterPostDelete(ctx context.Context, userCred mcclient.TokenCredential, model IModel, query jsonutils.JSONObject) error {
  746. return nil
  747. }
  748. func (e SEmptyExtraHook) AfterPostUpdate(ctx context.Context, userCred mcclient.TokenCredential, model IModel, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  749. return nil
  750. }