| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package db
- import (
- "context"
- "database/sql"
- "fmt"
- "net/http"
- "reflect"
- "strings"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/gotypes"
- "yunion.io/x/pkg/util/printutils"
- "yunion.io/x/pkg/util/rbacscope"
- "yunion.io/x/pkg/util/version"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/sqlchemy"
- "yunion.io/x/onecloud/pkg/appsrv"
- "yunion.io/x/onecloud/pkg/appsrv/dispatcher"
- "yunion.io/x/onecloud/pkg/cloudcommon/consts"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
- "yunion.io/x/onecloud/pkg/cloudcommon/policy"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/auth"
- "yunion.io/x/onecloud/pkg/util/filterclause"
- "yunion.io/x/onecloud/pkg/util/logclient"
- "yunion.io/x/onecloud/pkg/util/rbacutils"
- "yunion.io/x/onecloud/pkg/util/stringutils2"
- "yunion.io/x/onecloud/pkg/util/tagutils"
- )
- type DBModelDispatcher struct {
- manager IModelManager
- }
- func NewModelHandler(manager IModelManager) *DBModelDispatcher {
- // registerModelManager(manager)
- return &DBModelDispatcher{manager: manager}
- }
- func (dispatcher *DBModelDispatcher) Keyword() string {
- return dispatcher.manager.Keyword()
- }
- func (dispatcher *DBModelDispatcher) KeywordPlural() string {
- return dispatcher.manager.KeywordPlural()
- }
- func (dispatcher *DBModelDispatcher) ContextKeywordPlurals() [][]string {
- ctxMans := dispatcher.manager.GetContextManagers()
- if ctxMans != nil {
- keys := make([][]string, len(ctxMans))
- for i := 0; i < len(ctxMans); i += 1 {
- keys[i] = make([]string, len(ctxMans[i]))
- for j := 0; j < len(ctxMans[i]); j += 1 {
- keys[i][j] = ctxMans[i][j].KeywordPlural()
- }
- }
- return keys
- }
- return nil
- }
- func (dispatcher *DBModelDispatcher) Filter(f appsrv.FilterHandler) appsrv.FilterHandler {
- return auth.AuthenticateWithDelayDecision(f, true)
- }
- func (dispatcher *DBModelDispatcher) CustomizeHandlerInfo(handler *appsrv.SHandlerInfo) {
- dispatcher.manager.CustomizeHandlerInfo(handler)
- }
- func fetchUserCredential(ctx context.Context) mcclient.TokenCredential {
- return policy.FetchUserCredential(ctx)
- }
- var (
- searchOps = map[string]string{
- "contains": "contains",
- "startswith": "startswith",
- "endswith": "endswith",
- "empty": "isnullorempty",
- }
- )
- func parseSearchFieldkey(key string) (string, string) {
- for op, fn := range searchOps {
- if strings.HasSuffix(key, "__"+op) {
- key = key[:len(key)-(2+len(op))]
- return key, fn
- } else if strings.HasSuffix(key, "__i"+op) {
- key = key[:len(key)-(3+len(op))]
- return key, fn
- }
- }
- return key, ""
- }
- func listItemsQueryByColumn(manager IModelManager, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
- if query == nil {
- return q, nil
- }
- qdata, err := query.GetMap()
- if err != nil {
- return nil, errors.Wrapf(err, "query.GetMap %s", query.String())
- }
- listF := searchFields(manager, userCred)
- for key := range qdata {
- if !strings.HasPrefix(key, "@") {
- continue
- }
- fn, op := parseSearchFieldkey(key[1:])
- if listF.Contains(fn) {
- colSpec := manager.TableSpec().ColumnSpec(fn)
- if colSpec != nil {
- arrV := jsonutils.GetQueryStringArray(query, key)
- if len(op) > 0 {
- strV := strings.Join(arrV, ",")
- filter := fmt.Sprintf("%s.%s(%s)", fn, op, strV)
- fc := filterclause.ParseFilterClause(filter)
- if fc != nil {
- cond := fc.QueryCondition(q)
- if cond != nil {
- q = q.Filter(cond)
- }
- }
- } else if len(arrV) > 1 {
- for i := range arrV {
- arrV[i] = sqlchemy.GetStringValue(colSpec.ConvertFromString(arrV[i]))
- }
- q = q.In(fn, arrV)
- } else if len(arrV) == 1 {
- strV := colSpec.ConvertFromString(arrV[0])
- q = q.Equals(fn, strV)
- }
- }
- }
- }
- return q, nil
- }
- func applyListItemsSearchFilters(manager IModelManager, ctx context.Context, q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential, likes []string) (*sqlchemy.SQuery, error) {
- conds := make([]sqlchemy.ICondition, 0)
- for _, like := range likes {
- like = strings.TrimSpace(like)
- if len(like) > 0 {
- isAscii := utils.IsAscii(like)
- for _, colName := range searchFields(manager, userCred) {
- colSpec := manager.TableSpec().ColumnSpec(colName)
- if colSpec != nil && colSpec.IsSearchable() && (!colSpec.IsAscii() || (colSpec.IsAscii() && isAscii)) {
- conds = append(conds, sqlchemy.Contains(q.Field(colName), like))
- }
- }
- extraConds := manager.ExtraSearchConditions(ctx, q, like)
- if len(extraConds) > 0 {
- conds = append(conds, extraConds...)
- }
- }
- }
- if len(conds) > 0 {
- q = q.Filter(sqlchemy.OR(conds...))
- }
- return q, nil
- }
- func ApplyListItemsGeneralFilters(manager IModelManager, q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential, filters []string, filterAny bool) (*sqlchemy.SQuery, error) {
- conds := make([]sqlchemy.ICondition, 0)
- schFields := searchFields(manager, userCred) // only filter searchable fields
- for _, f := range filters {
- fc := filterclause.ParseFilterClause(f)
- if fc != nil {
- if schFields.Contains(fc.GetField()) {
- cond := fc.QueryCondition(q)
- if cond != nil {
- conds = append(conds, cond)
- }
- }
- }
- }
- if len(conds) > 0 {
- if filterAny {
- q = q.Filter(sqlchemy.OR(conds...))
- } else {
- q = q.Filter(sqlchemy.AND(conds...))
- }
- }
- return q, nil
- }
- func applyListItemsGeneralJointFilters(manager IModelManager, q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential, jointFilters []string, filterAny bool) (*sqlchemy.SQuery, error) {
- for _, f := range jointFilters {
- jfc := filterclause.ParseJointFilterClause(f)
- if jfc != nil {
- jointModelManager := GetModelManager(jfc.GetJointModelName())
- if jointModelManager == nil {
- return nil, httperrors.NewResourceNotFoundError("invalid joint resources %s", jfc.GetJointModelName())
- }
- hasKey := false
- for _, colume := range manager.TableSpec().Columns() {
- if colume.Name() == jfc.OriginKey {
- hasKey = true
- }
- }
- if !hasKey {
- return q, httperrors.NewInputParameterError("invalid joint filter %s, because %s doesn't have %s field", f, manager.Keyword(), jfc.OriginKey)
- }
- schFields := searchFields(jointModelManager, userCred)
- if schFields.Contains(jfc.GetField()) {
- sq := jointModelManager.Query(jfc.RelatedKey)
- cond := jfc.GetJointFilter(sq)
- if cond != nil {
- sq = sq.Filter(cond)
- if filterAny {
- q = q.Filter(sqlchemy.OR(sqlchemy.In(q.Field(jfc.OriginKey), sq)))
- } else {
- q = q.Filter(sqlchemy.AND(sqlchemy.In(q.Field(jfc.OriginKey), sq)))
- }
- }
- }
- }
- }
- return q, nil
- }
- func ListItemQueryFilters(manager IModelManager,
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- action string,
- ) (*sqlchemy.SQuery, error) {
- return listItemQueryFilters(manager, ctx, q, userCred, query, action, false)
- }
- func listItemQueryFiltersRaw(
- manager IModelManager,
- ctx context.Context, q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- action string,
- doCheckRbac bool,
- useRawQuery bool,
- ) (*sqlchemy.SQuery, error) {
- ownerId, queryScope, err, policyTagFilters := FetchCheckQueryOwnerScope(ctx, userCred, query, manager, action, doCheckRbac)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- if !policyTagFilters.IsEmpty() {
- query.(*jsonutils.JSONDict).Update(policyTagFilters.Json())
- log.Debugf("policyTagFilers: %s", query)
- }
- if query.Contains("export_keys") {
- exportKeys, _ := query.GetString("export_keys")
- keys := stringutils2.NewSortedStrings(strings.Split(exportKeys, ","))
- q, err = manager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "ListItemExportKeys")
- }
- }
- q, err = ListItemFilter(manager, ctx, q, userCred, query)
- if err != nil {
- return nil, errors.Wrap(err, "ListItemFilter")
- }
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- // TURN ON automatic filter by column name, ONLY if query key starts with @!!!!
- // example: @name=abc&@city=111
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- q, err = listItemsQueryByColumn(manager, q, userCred, query)
- if err != nil {
- return nil, errors.Wrap(err, "listItemsQueryByColumn")
- }
- searches := jsonutils.GetQueryStringArray(query, "search")
- if len(searches) > 0 {
- q, err = applyListItemsSearchFilters(manager, ctx, q, userCred, searches)
- if err != nil {
- return nil, errors.Wrap(err, "applyListItemsSearchFilters")
- }
- }
- filterAny, _ := query.Bool("filter_any")
- filters := jsonutils.GetQueryStringArray(query, "filter")
- if len(filters) > 0 {
- q, err = ApplyListItemsGeneralFilters(manager, q, userCred, filters, filterAny)
- if err != nil {
- return nil, errors.Wrap(err, "ApplyListItemsGeneralFilters")
- }
- }
- jointFilter := jsonutils.GetQueryStringArray(query, "joint_filter")
- if len(jointFilter) > 0 {
- q, err = applyListItemsGeneralJointFilters(manager, q, userCred, jointFilter, filterAny)
- if err != nil {
- return nil, errors.Wrap(err, "applyListItemsGeneralJointFilters")
- }
- }
- if !useRawQuery {
- // Specifically for joint resource, these filters will exclude
- // deleted resources by joining with master/slave tables
- q = manager.FilterByOwner(ctx, q, manager, userCred, ownerId, queryScope)
- q = manager.FilterBySystemAttributes(q, userCred, query, queryScope)
- q = manager.FilterByHiddenSystemAttributes(q, userCred, query, queryScope)
- }
- if isShowDetails(query) {
- managerVal := reflect.ValueOf(manager)
- fName := "ExtendListQuery"
- funcVal := managerVal.MethodByName(fName)
- if funcVal.IsValid() && !funcVal.IsNil() {
- oldq := q
- fields, _ := GetDetailFields(manager, userCred)
- for _, f := range fields {
- q = q.AppendField(q.Field(f).Label(f))
- }
- q, err = ExtendListQuery(manager, ctx, q, userCred, query)
- if err != nil {
- if errors.Cause(err) != MethodNotFoundError {
- return nil, errors.Wrap(err, "ExtendQuery")
- } else {
- // else ignore
- q = oldq
- }
- } else {
- // force query no details
- query.(*jsonutils.JSONDict).Set("details", jsonutils.JSONFalse)
- }
- }
- }
- return q, nil
- }
- func isShowDetails(query jsonutils.JSONObject) bool {
- showDetails := false
- showDetailsJson, _ := query.Get("details")
- if showDetailsJson != nil {
- showDetails, _ = showDetailsJson.Bool()
- } else {
- showDetails = true
- }
- return showDetails
- }
- func listItemQueryFilters(manager IModelManager,
- ctx context.Context, q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- action string,
- doCheckRbac bool,
- ) (*sqlchemy.SQuery, error) {
- return listItemQueryFiltersRaw(manager, ctx, q, userCred, query, action, doCheckRbac, false)
- }
- func mergeFields(metaFields, queryFields []string, isSysAdmin bool) stringutils2.SSortedStrings {
- meta := stringutils2.NewSortedStrings(metaFields)
- if len(queryFields) == 0 {
- return meta
- }
- query := stringutils2.NewSortedStrings(queryFields)
- _, mAndQ, qNoM := stringutils2.Split(meta, query)
- if !isSysAdmin {
- return mAndQ
- }
- // only sysadmin can specify list Fields
- return stringutils2.Merge(mAndQ, qNoM)
- }
- func Query2List(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, q *sqlchemy.SQuery, query jsonutils.JSONObject, delayFetch bool) ([]jsonutils.JSONObject, error) {
- metaFields, excludeFields := listFields(manager, userCred)
- fieldFilter := jsonutils.GetQueryStringArray(query, "field")
- allowListResult := IsAllowList(rbacscope.ScopeSystem, userCred, manager)
- listF := mergeFields(metaFields, fieldFilter, allowListResult.Result.IsAllow())
- listExcludes, _, _ := stringutils2.Split(stringutils2.NewSortedStrings(excludeFields), listF)
- showDetails := isShowDetails(query)
- var items []interface{}
- extraResults := make([]*jsonutils.JSONDict, 0)
- rows, err := q.Rows()
- if err != nil && err != sql.ErrNoRows {
- return nil, err
- }
- defer rows.Close()
- var exportKeys stringutils2.SSortedStrings
- if query.Contains("export_keys") {
- exportKeyStr, _ := query.GetString("export_keys")
- exportKeys = stringutils2.NewSortedStrings(strings.Split(exportKeyStr, ","))
- }
- for rows.Next() {
- item, err := NewModelObject(manager)
- if err != nil {
- return nil, err
- }
- if len(exportKeys) > 0 {
- rowMap, err := q.Row2Map(rows)
- if err != nil {
- return nil, err
- }
- extraData := jsonutils.NewDict()
- for k, v := range rowMap {
- if len(v) > 0 {
- extraData.Add(jsonutils.NewString(v), k)
- }
- }
- extraKeys := manager.GetExportExtraKeys(ctx, exportKeys, rowMap)
- if extraKeys != nil {
- extraData.Update(extraKeys)
- }
- err = q.RowMap2Struct(rowMap, item)
- if err != nil {
- return nil, err
- }
- extraResults = append(extraResults, extraData)
- } else {
- err = q.Row2Struct(rows, item)
- if err != nil {
- return nil, err
- }
- if delayFetch {
- err = Fetch(item)
- if err != nil {
- return nil, err
- }
- }
- }
- if err := CheckRecordChecksumConsistent(item); err != nil {
- return nil, err
- }
- items = append(items, item)
- }
- results := make([]*jsonutils.JSONDict, len(items))
- if showDetails || len(exportKeys) > 0 {
- var sortedListFields stringutils2.SSortedStrings
- if len(exportKeys) > 0 {
- sortedListFields = exportKeys
- } else if len(fieldFilter) > 0 {
- sortedListFields = stringutils2.NewSortedStrings(fieldFilter)
- }
- extraRows, err := FetchCustomizeColumns(manager, ctx, userCred, query, items, sortedListFields, true)
- if err != nil {
- return nil, errors.Wrap(err, "FetchCustomizeColumns")
- }
- if len(extraRows) == len(results) {
- for i := range results {
- jsonDict := extraRows[i].CopyExcludes(listExcludes...)
- if i < len(extraResults) {
- extraResults[i].Update(jsonDict)
- jsonDict = extraResults[i]
- }
- if len(fieldFilter) > 0 {
- jsonDict = jsonDict.CopyIncludes(fieldFilter...)
- }
- results[i] = jsonDict
- }
- } else {
- return nil, httperrors.NewInternalServerError("FetchCustomizeColumns return incorrect number of results")
- }
- } else {
- for i := range items {
- jsonDict := jsonutils.Marshal(items[i]).(*jsonutils.JSONDict).CopyExcludes(listExcludes...)
- if len(fieldFilter) > 0 {
- jsonDict = jsonDict.CopyIncludes(fieldFilter...)
- }
- results[i] = jsonDict
- }
- }
- for i := range items {
- i18nDict := items[i].(IModel).GetI18N(ctx)
- if i18nDict != nil {
- jsonDict := results[i]
- jsonDict.Set("_i18n", i18nDict)
- }
- }
- r := make([]jsonutils.JSONObject, len(items))
- for i := range results {
- r[i] = results[i]
- }
- return r, nil
- }
- func fetchContextObjectsIds(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, ctxIds []dispatcher.SResourceContext, queryDict *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
- var err error
- for i := 0; i < len(ctxIds); i += 1 {
- queryDict, err = fetchContextObjectIds(manager, ctx, userCred, ctxIds[i], queryDict)
- if err != nil {
- return nil, err
- }
- }
- return queryDict, nil
- }
- func fetchContextObjectIds(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, ctxId dispatcher.SResourceContext, queryDict *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
- ctxObj, err := fetchContextObject(manager, ctx, userCred, ctxId)
- if err != nil {
- return nil, err
- }
- queryDict.Add(jsonutils.NewString(ctxObj.GetId()), fmt.Sprintf("%s_id", ctxObj.GetModelManager().Keyword()))
- if len(ctxObj.GetModelManager().Alias()) > 0 {
- queryDict.Add(jsonutils.NewString(ctxObj.GetId()), fmt.Sprintf("%s_id", ctxObj.GetModelManager().Alias()))
- }
- return queryDict, nil
- }
- func fetchContextObjects(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, ctxIds []dispatcher.SResourceContext) ([]IModel, error) {
- ctxObjs := make([]IModel, len(ctxIds))
- for i := 0; i < len(ctxIds); i += 1 {
- ctxObj, err := fetchContextObject(manager, ctx, userCred, ctxIds[i])
- if err != nil {
- return nil, err
- }
- ctxObjs[i] = ctxObj
- }
- return ctxObjs, nil
- }
- func fetchContextObject(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, ctxId dispatcher.SResourceContext) (IModel, error) {
- ctxMans := manager.GetContextManagers()
- if ctxMans == nil {
- return nil, httperrors.NewInternalServerError("No context manager")
- }
- for i := 0; i < len(ctxMans); i += 1 {
- for j := 0; j < len(ctxMans[i]); j += 1 {
- if ctxMans[i][j].KeywordPlural() == ctxId.Type {
- ctxObj, err := fetchItem(ctxMans[i][j], ctx, userCred, ctxId.Id, nil)
- if err != nil {
- if err == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2(ctxMans[i][j].Keyword(), ctxId.Id)
- } else {
- return nil, err
- }
- }
- return ctxObj, nil
- }
- }
- }
- return nil, httperrors.NewInternalServerError("No such context %s(%s)", ctxId.Type, ctxId.Id)
- }
- func ListItems(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, ctxIds []dispatcher.SResourceContext) (*printutils.ListResult, error) {
- // 获取常规参数
- var err error
- var maxLimit int64 = consts.GetMaxPagingLimit()
- limit, _ := query.Int("limit")
- forceNoPaging := jsonutils.QueryBoolean(query, "force_no_paging", false)
- offset, _ := query.Int("offset")
- pagingMarker, _ := query.GetString("paging_marker")
- pagingOrderStr, _ := query.GetString("paging_order")
- pagingOrder := sqlchemy.QueryOrderType(strings.ToUpper(pagingOrderStr))
- // export data only
- exportLimit, err := query.Int("export_limit")
- if query.Contains("export_keys") && err == nil {
- limit = exportLimit
- }
- var q *sqlchemy.SQuery
- useRawQuery := isRawQuery(manager, userCred, query, policy.PolicyActionList)
- queryDict, ok := query.(*jsonutils.JSONDict)
- if !ok {
- return nil, fmt.Errorf("invalid query format")
- }
- if len(ctxIds) > 0 {
- queryDict, err = fetchContextObjectsIds(manager, ctx, userCred, ctxIds, queryDict)
- if err != nil {
- return nil, err
- }
- }
- queryDict, err = manager.ValidateListConditions(ctx, userCred, queryDict)
- if err != nil {
- return nil, err
- }
- pagingConf := manager.GetPagingConfig()
- if pagingConf == nil {
- if limit <= 0 {
- limit = consts.GetDefaultPagingLimit()
- }
- } else {
- if limit <= 0 {
- limit = int64(pagingConf.DefaultLimit)
- }
- if pagingOrder != sqlchemy.SQL_ORDER_ASC && pagingOrder != sqlchemy.SQL_ORDER_DESC {
- pagingOrder = pagingConf.Order
- }
- }
- splitable := manager.GetSplitTable()
- if splitable != nil {
- // handle splitable query, query each subtable, then union results
- metas, err := splitable.GetTableMetas()
- if err != nil {
- return nil, errors.Wrap(err, "splitable.GetTableMetas")
- }
- var subqs []sqlchemy.IQuery
- for _, meta := range metas {
- ts := splitable.GetTableSpec(meta)
- subq := ts.Query()
- subq, err = listItemQueryFiltersRaw(manager, ctx, subq, userCred, queryDict.Copy(), policy.PolicyActionList, true, useRawQuery)
- if err != nil {
- return nil, errors.Wrap(err, "listItemQueryFiltersRaw")
- }
- if pagingConf != nil {
- if limit > 0 {
- subq = subq.Limit(int(limit) + 1)
- }
- if len(pagingMarker) > 0 {
- markers := decodePagingMarker(pagingMarker)
- for markerIdx, marker := range markers {
- if markerIdx < len(pagingConf.MarkerFields) {
- if pagingOrder == sqlchemy.SQL_ORDER_ASC {
- subq = subq.GE(pagingConf.MarkerFields[markerIdx], marker)
- } else {
- subq = subq.LE(pagingConf.MarkerFields[markerIdx], marker)
- }
- }
- }
- }
- for _, f := range pagingConf.MarkerFields {
- if pagingOrder == sqlchemy.SQL_ORDER_ASC {
- subq = subq.Asc(f)
- } else {
- subq = subq.Desc(f)
- }
- }
- }
- if limit > 0 {
- subq = subq.Limit(int(limit) + 1)
- }
- subqs = append(subqs, subq)
- }
- union, err := sqlchemy.UnionWithError(subqs...)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- emptyList := printutils.ListResult{Data: []jsonutils.JSONObject{}}
- return &emptyList, nil
- } else {
- return nil, errors.Wrap(err, "sqlchemy.UnionWithError")
- }
- }
- q = union.Query()
- } else {
- q = manager.NewQuery(ctx, userCred, queryDict, useRawQuery)
- }
- q, err = listItemQueryFiltersRaw(manager, ctx, q, userCred, queryDict, policy.PolicyActionList, true, useRawQuery)
- if err != nil {
- return nil, errors.Wrap(err, "listItemQueryFiltersRaw")
- }
- var totalCnt int
- var totalJson jsonutils.JSONObject
- if pagingConf == nil {
- summaryStats := jsonutils.QueryBoolean(query, "summary_stats", false)
- if summaryStats {
- // calculate total
- totalQ := q.CountQuery()
- totalCnt, totalJson, err = manager.CustomizedTotalCount(ctx, userCred, query, totalQ)
- if err != nil {
- return nil, errors.Wrap(err, "CustomizedTotalCount")
- }
- } else {
- totalCnt, err = q.CountWithError()
- if err != nil {
- return nil, errors.Wrap(err, "CountWithError")
- }
- }
- //log.Debugf("total count %d", totalCnt)
- if totalCnt == 0 {
- emptyList := printutils.ListResult{Data: []jsonutils.JSONObject{}}
- return &emptyList, nil
- }
- }
- if int64(totalCnt) > maxLimit && (limit <= 0 || limit > maxLimit) && !forceNoPaging {
- limit = maxLimit
- }
- // orders defined in pagingConf should have the highest priority
- if pagingConf != nil {
- for _, f := range pagingConf.MarkerFields {
- if pagingOrder == sqlchemy.SQL_ORDER_ASC {
- q = q.Asc(f)
- } else {
- q = q.Desc(f)
- }
- }
- }
- var primaryCol sqlchemy.IColumnSpec
- primaryCols := manager.TableSpec().PrimaryColumns()
- if len(primaryCols) == 1 {
- primaryCol = primaryCols[0]
- }
- orderBy := jsonutils.GetQueryStringArray(queryDict, "order_by")
- order := sqlchemy.SQL_ORDER_DESC
- orderStr, _ := queryDict.GetString("order")
- if orderStr == "asc" {
- order = sqlchemy.SQL_ORDER_ASC
- }
- orderQuery := query.(*jsonutils.JSONDict).Copy()
- for _, orderByField := range orderBy {
- if pagingConf != nil && utils.IsInStringArray(orderByField, pagingConf.MarkerFields) {
- // skip markerField in pagingConf
- continue
- }
- colSpec := manager.TableSpec().ColumnSpec(orderByField)
- if colSpec == nil {
- orderQuery.Set(fmt.Sprintf("order_by_%s", orderByField), jsonutils.NewString(string(order)))
- }
- }
- q, err = OrderByExtraFields(manager, ctx, q, userCred, orderQuery)
- if err != nil {
- return nil, errors.Wrap(err, "OrderByExtraFields")
- }
- if orderBy == nil {
- orderBy = []string{}
- }
- if !q.IsGroupBy() {
- if primaryCol != nil && primaryCol.IsNumeric() {
- orderBy = append(orderBy, primaryCol.Name())
- } else if manager.TableSpec().ColumnSpec("created_at") != nil {
- orderBy = append(orderBy, "created_at")
- if manager.TableSpec().ColumnSpec("name") != nil {
- orderBy = append(orderBy, "name")
- }
- if primaryCol != nil {
- orderBy = append(orderBy, primaryCol.Name())
- }
- }
- }
- for _, orderByField := range orderBy {
- if pagingConf != nil && utils.IsInStringArray(orderByField, pagingConf.MarkerFields) {
- // skip markerField in pagingConf
- continue
- }
- for _, field := range q.QueryFields() {
- if orderByField == field.Name() {
- if order == sqlchemy.SQL_ORDER_ASC {
- q = q.Asc(field)
- } else {
- q = q.Desc(field)
- }
- break
- }
- }
- }
- if forceNoPaging {
- limit = 0
- }
- if pagingConf != nil {
- if limit > 0 {
- q = q.Limit(int(limit) + 1)
- }
- if len(pagingMarker) > 0 {
- markers := decodePagingMarker(pagingMarker)
- for markerIdx, marker := range markers {
- if markerIdx < len(pagingConf.MarkerFields) {
- if pagingOrder == sqlchemy.SQL_ORDER_ASC {
- q = q.GE(pagingConf.MarkerFields[markerIdx], marker)
- } else {
- q = q.LE(pagingConf.MarkerFields[markerIdx], marker)
- }
- }
- }
- }
- retList, err := Query2List(manager, ctx, userCred, q, queryDict, false)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- nextMarkers := make([]string, 0)
- if limit > 0 && int64(len(retList)) > limit {
- for _, markerField := range pagingConf.MarkerFields {
- nextMarker, _ := retList[limit].GetString(markerField)
- nextMarkers = append(nextMarkers, nextMarker)
- }
- retList = retList[:limit]
- }
- nextMarker := encodePagingMarker(nextMarkers)
- retResult := printutils.ListResult{
- Data: retList, Limit: int(limit),
- NextMarker: nextMarker,
- MarkerField: strings.Join(pagingConf.MarkerFields, ","),
- MarkerOrder: string(pagingOrder),
- }
- return &retResult, nil
- }
- customizeFilters, err := manager.CustomizeFilterList(ctx, q, userCred, queryDict)
- if err != nil {
- return nil, errors.Wrap(err, "CustomizeFilterList")
- }
- delayFetch := false
- if customizeFilters.IsEmpty() {
- if limit > 0 {
- q = q.Limit(int(limit))
- }
- if offset > 0 {
- q = q.Offset(int(offset))
- if primaryCol != nil && !query.Contains("export_keys") && consts.QueryOffsetOptimization {
- q.AppendField(q.Field(primaryCol.Name())) // query primary key only
- delayFetch = true
- log.Debugf("apply queryOffsetOptimization")
- }
- }
- }
- retList, err := Query2List(manager, ctx, userCred, q, queryDict, delayFetch)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- retCount := len(retList)
- // apply customizeFilters
- retList, err = customizeFilters.DoApply(retList)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- if len(retList) != retCount {
- totalCnt = len(retList)
- }
- if query.Contains("export_keys") {
- retList = getExportCols(query, retList)
- }
- paginate := false
- if !customizeFilters.IsEmpty() {
- // query not use Limit and Offset, do manual pagination
- paginate = true
- }
- return calculateListResult(retList, totalCnt, totalJson, int(limit), int(offset), paginate), nil
- }
- // 构造list返回详情
- func calculateListResult(data []jsonutils.JSONObject, total int, totalJson jsonutils.JSONObject, limit, offset int, paginate bool) *printutils.ListResult {
- if paginate {
- // do offset first
- if offset > 0 {
- if total > offset {
- data = data[offset:]
- } else {
- data = []jsonutils.JSONObject{}
- }
- }
- // do limit
- if limit > 0 && total-offset > limit {
- data = data[:limit]
- } else {
- data = data[:total-offset]
- }
- }
- retResult := printutils.ListResult{
- Data: data,
- Total: total,
- Limit: limit,
- Offset: offset,
- Totals: totalJson,
- }
- return &retResult
- }
- func getExportCols(query jsonutils.JSONObject, retList []jsonutils.JSONObject) []jsonutils.JSONObject {
- var exportKeys stringutils2.SSortedStrings
- exportKeyStr, _ := query.GetString("export_keys")
- exportKeys = stringutils2.NewSortedStrings(strings.Split(exportKeyStr, ","))
- for i := range retList {
- if len(exportKeys) > 0 {
- retList[i] = retList[i].(*jsonutils.JSONDict).CopyIncludes(exportKeys...)
- }
- }
- return retList
- }
- func (dispatcher *DBModelDispatcher) List(ctx context.Context, query jsonutils.JSONObject, ctxIds []dispatcher.SResourceContext) (*printutils.ListResult, error) {
- // 获取用户信息
- userCred := fetchUserCredential(ctx)
- manager := dispatcher.manager.GetImmutableInstance(ctx, userCred, query)
- ctx = manager.PrepareQueryContext(ctx, userCred, query)
- // list详情
- items, err := ListItems(manager, ctx, userCred, query, ctxIds)
- if err != nil {
- return nil, httperrors.NewGeneralError(errors.Wrapf(err, "ListItems"))
- }
- if userCred != nil && userCred.HasSystemAdminPrivilege() && manager.ListSkipLog(ctx, userCred, query) {
- appParams := appsrv.AppContextGetParams(ctx)
- if appParams != nil {
- appParams.SkipLog = true
- }
- }
- return items, nil
- }
- func getModelItemDetails(manager IModelManager, item IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, isHead bool) (jsonutils.JSONObject, error) {
- appParams := appsrv.AppContextGetParams(ctx)
- if appParams == nil && isHead {
- return nil, httperrors.NewInternalServerError("fail to get http response writer from context")
- }
- hdrs := item.GetExtraDetailsHeaders(ctx, userCred, query)
- for k, v := range hdrs {
- appParams.Response.Header().Add(k, v)
- }
- if isHead {
- appParams.Response.Header().Add("Content-Length", "0")
- appParams.Response.Write([]byte{})
- return nil, nil
- }
- if manager.IsCustomizedGetDetailsBody() {
- return item.CustomizedGetDetailsBody(ctx, userCred, query)
- } else {
- return getItemDetails(manager, item, ctx, userCred, query)
- }
- }
- func GetItemDetails(manager IModelManager, item IModel, ctx context.Context, userCred mcclient.TokenCredential) (jsonutils.JSONObject, error) {
- return getItemDetails(manager, item, ctx, userCred, jsonutils.NewDict())
- }
- func getItemDetails(manager IModelManager, item IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- metaFields, excludeFields := GetDetailFields(manager, userCred)
- fieldFilter := jsonutils.GetQueryStringArray(query, "field")
- var sortedListFields stringutils2.SSortedStrings
- if len(fieldFilter) > 0 {
- sortedListFields = stringutils2.NewSortedStrings(fieldFilter)
- }
- extraRows, err := FetchCustomizeColumns(manager, ctx, userCred, query, []interface{}{item}, sortedListFields, false)
- if err != nil {
- return nil, errors.Wrap(err, "FetchCustomizeColumns")
- }
- if len(extraRows) == 1 {
- getFields := mergeFields(metaFields, fieldFilter, IsAllowGet(ctx, rbacscope.ScopeSystem, userCred, item))
- excludes, _, _ := stringutils2.Split(stringutils2.NewSortedStrings(excludeFields), getFields)
- return extraRows[0].CopyExcludes(excludes...), nil
- }
- return nil, httperrors.NewInternalServerError("FetchCustomizeColumns returns incorrect results(expect 1 actual %d)", len(extraRows))
- }
- func tryGetModelProperty(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, property string, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- funcName := fmt.Sprintf("GetProperty%s", utils.Kebab2Camel(property, "-"))
- modelValue := reflect.ValueOf(manager)
- // params := []interface{}{ctx, userCred, query}
- funcValue := modelValue.MethodByName(funcName)
- if !funcValue.IsValid() || funcValue.IsNil() {
- return nil, nil
- }
- // _, _, err, _ := FetchCheckQueryOwnerScope(ctx, userCred, query, manager, policy.PolicyActionList, true)
- // if err != nil {
- // return nil, err
- // }
- outs, err := callFunc(funcValue, funcName, ctx, userCred, query)
- if err != nil {
- return nil, httperrors.NewInternalServerError("reflect call %s fail %s", funcName, err)
- }
- if len(outs) != 2 {
- return nil, httperrors.NewInternalServerError("Invald %s return value", funcName)
- }
- resVal := outs[0]
- errVal := outs[1].Interface()
- if !gotypes.IsNil(errVal) {
- return nil, errVal.(error)
- } else {
- if gotypes.IsNil(resVal) {
- return nil, httperrors.NewBadRequestError("No return value, so why query?")
- } else {
- return ValueToJSONObject(resVal), nil
- }
- }
- }
- func (dispatcher *DBModelDispatcher) Get(ctx context.Context, idStr string, query jsonutils.JSONObject, isHead bool) (jsonutils.JSONObject, error) {
- // log.Debugf("Get %s", idStr)
- userCred := fetchUserCredential(ctx)
- manager := dispatcher.manager.GetImmutableInstance(ctx, userCred, query)
- ctx = manager.PrepareQueryContext(ctx, userCred, query)
- data, err := tryGetModelProperty(manager, ctx, userCred, idStr, query)
- if err != nil {
- return nil, err
- } else if data != nil {
- if dataDict, ok := data.(*jsonutils.JSONDict); ok {
- i18nDict := manager.GetI18N(ctx, idStr, data)
- if i18nDict != nil {
- dataDict.Set("_i18n", i18nDict)
- }
- }
- return data, nil
- }
- model, err := fetchItem(manager, ctx, userCred, idStr, query)
- if err == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2(manager.Keyword(), idStr)
- } else if err != nil {
- return nil, errors.Wrapf(err, "fetchItem")
- }
- err = isObjectRbacAllowed(ctx, model, userCred, policy.PolicyActionGet)
- if err != nil {
- return nil, err
- }
- if userCred.HasSystemAdminPrivilege() && manager.GetSkipLog(ctx, userCred, query) {
- appParams := appsrv.AppContextGetParams(ctx)
- if appParams != nil {
- appParams.SkipLog = true
- }
- }
- return getModelItemDetails(manager, model, ctx, userCred, query, isHead)
- }
- func (dispatcher *DBModelDispatcher) GetSpecific(ctx context.Context, idStr string, spec string, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- userCred := fetchUserCredential(ctx)
- manager := dispatcher.manager.GetImmutableInstance(ctx, userCred, query)
- ctx = manager.PrepareQueryContext(ctx, userCred, query)
- model, err := fetchItem(manager, ctx, userCred, idStr, query)
- if err == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2(manager.Keyword(), idStr)
- } else if err != nil {
- return nil, err
- }
- params := []interface{}{ctx, userCred, query}
- specCamel := utils.Kebab2Camel(spec, "-")
- modelValue := reflect.ValueOf(model)
- err = isObjectRbacAllowed(ctx, model, userCred, policy.PolicyActionGet, spec)
- if err != nil {
- return nil, err
- }
- funcName := fmt.Sprintf("GetDetails%s", specCamel)
- funcValue := modelValue.MethodByName(funcName)
- log.Errorf("MethodByName %s", funcName)
- if !funcValue.IsValid() || funcValue.IsNil() {
- log.Errorf("MethodByName2 %s", funcName)
- return nil, httperrors.NewSpecNotFoundError("%s %s %s not found", dispatcher.Keyword(), idStr, spec)
- }
- outs, err := callFunc(funcValue, funcName, params...)
- if err != nil {
- log.Errorf("MethodByName4 %s", funcName)
- return nil, err
- }
- if len(outs) != 2 {
- return nil, httperrors.NewInternalServerError("Invald %s return value", funcName)
- }
- resVal := outs[0]
- errVal := outs[1].Interface()
- if !gotypes.IsNil(errVal) {
- log.Errorf("MethodByName3 %s", funcName)
- return nil, errVal.(error)
- } else {
- if gotypes.IsNil(resVal.Interface()) {
- return nil, nil
- } else {
- return ValueToJSONObject(resVal), nil
- }
- }
- }
- func fetchOwnerId(ctx context.Context, manager IModelManager, userCred mcclient.TokenCredential, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) {
- var ownerId mcclient.IIdentityProvider
- var err error
- if manager.ResourceScope() != rbacscope.ScopeSystem {
- ownerId, err = manager.FetchOwnerId(ctx, data)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- }
- if ownerId == nil {
- ownerId = userCred
- }
- return ownerId, nil
- }
- /*func fetchOwnerProjectId(ctx context.Context, manager IModelManager, userCred mcclient.TokenCredential, data jsonutils.JSONObject) (string, error) {
- var projId string
- if data != nil {
- // projId = jsonutils.GetAnyString(data, []string{"project", "tenant", "project_id", "tenant_id"})
- projId = manager.FetchOwnerId(data)
- }
- ownerProjId := manager.GetOwnerId(userCred)
- if len(projId) == 0 {
- return ownerProjId, nil
- }
- tid, err := manager.ValidateOwnerId(ctx, projId)
- if err != nil {
- return "", httperrors.NewInputParameterError("Invalid owner %s", projId)
- }
- if tid == ownerProjId {
- return ownerProjId, nil
- }
- var isAllow bool
- if consts.IsRbacEnabled() {
- result := policy.PolicyManager.Allow(true, userCred,
- consts.GetServiceType(), policy.PolicyDelegation, "")
- if result == rbacutils.AdminAllow {
- isAllow = true
- }
- } else {
- isAllow = userCred.IsAdminAllow(consts.GetServiceType(), policy.PolicyDelegation, "")
- }
- if !isAllow {
- return "", httperrors.NewForbiddenError("Delegation not allowed")
- }
- return tid, nil
- }*/
- func NewModelObject(modelManager IModelManager) (IModel, error) {
- m, ok := reflect.New(modelManager.TableSpec().DataType()).Interface().(IModel)
- if !ok {
- return nil, ErrInconsistentDataType
- }
- m.SetModelManager(modelManager, m)
- return m, nil
- }
- func FetchModelObjects(modelManager IModelManager, query *sqlchemy.SQuery, targets interface{}) error {
- rows, err := query.Rows()
- if err != nil {
- if err == sql.ErrNoRows {
- return nil
- }
- return err
- }
- defer rows.Close()
- targetsValue := reflect.Indirect(reflect.ValueOf(targets))
- for rows.Next() {
- m, err := NewModelObject(modelManager)
- if err != nil {
- return err
- }
- err = query.Row2Struct(rows, m)
- if err != nil {
- return err
- }
- newTargets := reflect.Append(targetsValue, reflect.Indirect(reflect.ValueOf(m)))
- targetsValue.Set(newTargets)
- }
- return nil
- }
- func FetchIModelObjects(modelManager IModelManager, query *sqlchemy.SQuery) ([]IModel, error) {
- // TODO: refactor below duplicated code from FetchModelObjects
- rows, err := query.Rows()
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- objs := make([]IModel, 0)
- for rows.Next() {
- m, err := NewModelObject(modelManager)
- if err != nil {
- return nil, err
- }
- err = query.Row2Struct(rows, m)
- if err != nil {
- return nil, err
- }
- objs = append(objs, m)
- }
- return objs, nil
- }
- func DoCreate(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject, ownerId mcclient.IIdentityProvider) (IModel, error) {
- // 锁住一类实例
- lockman.LockClass(ctx, manager, GetLockClassKey(manager, ownerId))
- defer lockman.ReleaseClass(ctx, manager, GetLockClassKey(manager, ownerId))
- return doCreateItem(manager, ctx, userCred, ownerId, nil, data)
- }
- func doCreateItem(
- manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential,
- ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) (IModel, error) {
- return _doCreateItem(manager, ctx, userCred, ownerId, query, data, false, 1)
- }
- // 批量创建
- func batchCreateDoCreateItem(
- manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider,
- query jsonutils.JSONObject, data jsonutils.JSONObject, baseIndex int) (IModel, error) {
- return _doCreateItem(manager, ctx, userCred, ownerId, query, data, true, baseIndex)
- }
- // 对于modelManager的实际创建过程
- func _doCreateItem(
- manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider,
- query jsonutils.JSONObject, data jsonutils.JSONObject, batchCreate bool, baseIndex int) (IModel, error) {
- dataDict, ok := data.(*jsonutils.JSONDict)
- if !ok {
- return nil, httperrors.NewGeneralError(fmt.Errorf("fail to decode json data %s", data))
- }
- var err error
- var generateName string
- // 若manager存在name字段且请求包含generate_name,则根据name从数据库中获取相同名称添加后缀
- if manager.HasName() {
- if dataDict.Contains("generate_name") {
- generateName, _ = dataDict.GetString("generate_name")
- if len(generateName) > 0 {
- if manager.EnableGenerateName() {
- lockman.LockRawObject(ctx, manager.Keyword(), "name")
- defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
- // if enable generateName, alway generate name
- newName, err := GenerateName2(ctx, manager, ownerId, generateName, nil, baseIndex)
- if err != nil {
- return nil, errors.Wrap(err, "GenerateName2")
- }
- dataDict.Add(jsonutils.NewString(newName), "name")
- } else {
- // if no name but generate_name provided, use generate_name as name instead
- oldName, _ := dataDict.GetString("name")
- if len(oldName) == 0 {
- dataDict.Add(jsonutils.NewString(generateName), "name")
- }
- }
- }
- // cleanup generate_name
- dataDict.Remove("generate_name")
- }
- }
- funcName := "ValidateCreateData"
- if batchCreate {
- funcName = "BatchCreateValidateCreateData"
- }
- // 校验创建请求入参
- dataDict, err = ValidateCreateData(funcName, manager, ctx, userCred, ownerId, query, dataDict)
- if err != nil {
- return nil, err
- }
- // 若manager用于name字段,确保name唯一
- if manager.HasName() {
- // run name validation after validate create data
- name, _ := dataDict.GetString("name")
- if len(name) > 0 {
- uniqValues := manager.FetchUniqValues(ctx, dataDict)
- err = NewNameValidator(ctx, manager, ownerId, name, uniqValues)
- if err != nil {
- return nil, err
- }
- }
- }
- // 检查models定义中tag指定required
- err = jsonutils.CheckRequiredFields(dataDict, createRequireFields(manager, userCred))
- if err != nil {
- return nil, httperrors.NewInputParameterError("%v", err)
- }
- // 初始化model
- model, err := NewModelObject(manager)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- // 检查models定义中tag指定create
- filterData := dataDict.CopyIncludes(createFields(manager, userCred)...)
- err = filterData.Unmarshal(model)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- // 实际创建前钩子
- err = model.CustomizeCreate(ctx, userCred, ownerId, query, dataDict)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- dryRun := struct {
- DryRun bool
- }{}
- data.Unmarshal(&dryRun)
- if dryRun.DryRun {
- return model, nil
- }
- // 插入数据库记录
- if manager.CreateByInsertOrUpdate() {
- err = manager.TableSpec().InsertOrUpdate(ctx, model)
- } else {
- err = manager.TableSpec().Insert(ctx, model)
- }
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- if manager.HasName() {
- // HACK: save generateName
- if len(generateName) > 0 && manager.EnableGenerateName() {
- if standaloneMode, ok := model.(IStandaloneModel); ok {
- standaloneMode.SetMetadata(ctx, "generate_name", generateName, userCred)
- }
- }
- }
- // HACK: set data same as dataDict
- data.(*jsonutils.JSONDict).Update(dataDict)
- return model, nil
- }
- func (dispatcher *DBModelDispatcher) FetchCreateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error) {
- return dispatcher.manager.FetchCreateHeaderData(ctx, header)
- }
- func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils.JSONObject, data jsonutils.JSONObject, ctxIds []dispatcher.SResourceContext) (jsonutils.JSONObject, error) {
- // 获取用户信息
- userCred := fetchUserCredential(ctx)
- manager := dispatcher.manager.GetMutableInstance(ctx, userCred, query, data)
- ownerId, err := fetchOwnerId(ctx, manager, userCred, data)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- if len(ctxIds) > 0 {
- dataDict, ok := data.(*jsonutils.JSONDict)
- if !ok {
- return nil, httperrors.NewGeneralError(fmt.Errorf("fail to parse body %s", data))
- }
- data, err = fetchContextObjectsIds(manager, ctx, userCred, ctxIds, dataDict)
- if err != nil {
- return nil, httperrors.NewGeneralError(errors.Wrapf(err, "fetchContextObjectsIds"))
- }
- }
- // 用户角色校验
- var policyResult rbacutils.SPolicyResult
- policyResult, err = isClassRbacAllowed(ctx, manager, userCred, ownerId, policy.PolicyActionCreate)
- if err != nil {
- return nil, errors.Wrap(err, "isClassRbacAllowed")
- }
- // initialize pending usage in context any way
- if InitPendingUsagesInContext != nil {
- ctx = InitPendingUsagesInContext(ctx)
- }
- dryRun := jsonutils.QueryBoolean(data, "dry_run", false)
- // inject tag filters imposed by policy
- data.(*jsonutils.JSONDict).Update(policyResult.Json())
- // 资源实际创建函数
- model, err := DoCreate(manager, ctx, userCred, query, data, ownerId)
- if err != nil {
- // validate failed, clean pending usage
- if CancelPendingUsagesInContext != nil {
- e := CancelPendingUsagesInContext(ctx, userCred)
- if e != nil {
- err = errors.Wrapf(err, "CancelPendingUsagesInContext fail %s", e.Error())
- }
- }
- failErr := manager.OnCreateFailed(ctx, userCred, ownerId, query, data)
- if failErr != nil {
- err = errors.Wrapf(err, "%s", failErr.Error())
- }
- return nil, httperrors.NewGeneralError(err)
- }
- // 伪创建
- if dryRun {
- // dry run, clean pending usage
- if CancelPendingUsagesInContext != nil {
- err := CancelPendingUsagesInContext(ctx, userCred)
- if err != nil {
- return nil, errors.Wrap(err, "CancelPendingUsagesInContext")
- }
- }
- return getItemDetails(manager, model, ctx, userCred, query)
- }
- // 资源创建完成后所需执行的任务(创建完成指在数据库中存在数据)
- func() {
- lockman.LockObject(ctx, model)
- defer lockman.ReleaseObject(ctx, model)
- model.PostCreate(ctx, userCred, ownerId, query, data)
- if err := manager.GetExtraHook().AfterPostCreate(ctx, userCred, ownerId, model, query, data); err != nil {
- logclient.AddActionLogWithContext(ctx, model, logclient.ACT_POST_CREATE_HOOK, err, userCred, false)
- }
- }()
- // 添加操作日志与消息通知
- {
- notes := model.GetShortDesc(ctx)
- OpsLog.LogEvent(model, ACT_CREATE, notes, userCred)
- logclient.AddActionLogWithContext(ctx, model, logclient.ACT_CREATE, notes, userCred, true)
- }
- manager.OnCreateComplete(ctx, []IModel{model}, userCred, ownerId, query, []jsonutils.JSONObject{data})
- return getItemDetails(manager, model, ctx, userCred, query)
- }
- func expandMultiCreateParams(manager IModelManager,
- ctx context.Context,
- userCred mcclient.TokenCredential,
- ownerId mcclient.IIdentityProvider,
- query jsonutils.JSONObject,
- data jsonutils.JSONObject,
- count int,
- ) ([]jsonutils.JSONObject, error) {
- jsonDict, ok := data.(*jsonutils.JSONDict)
- if !ok {
- return nil, httperrors.NewInputParameterError("body is not a json?")
- }
- if manager.HasName() {
- name, _ := jsonDict.GetString("generate_name")
- if len(name) == 0 {
- name, _ = jsonDict.GetString("name")
- if len(name) == 0 {
- return nil, httperrors.NewInputParameterError("Missing name or generate_name")
- }
- jsonDict.Add(jsonutils.NewString(name), "generate_name")
- jsonDict.RemoveIgnoreCase("name")
- }
- }
- ret := make([]jsonutils.JSONObject, count)
- for i := 0; i < count; i += 1 {
- input, err := ExpandBatchCreateData(manager, ctx, userCred, ownerId, query, jsonDict.Copy(), i)
- if err != nil {
- if errors.Cause(err) == MethodNotFoundError {
- ret[i] = jsonDict.Copy()
- } else {
- return nil, errors.Wrap(err, "ExpandBatchCreateData")
- }
- } else {
- ret[i] = input
- }
- }
- return ret, nil
- }
- func (dispatcher *DBModelDispatcher) BatchCreate(ctx context.Context, query jsonutils.JSONObject, data jsonutils.JSONObject, count int, ctxIds []dispatcher.SResourceContext) ([]printutils.SubmitResult, error) {
- userCred := fetchUserCredential(ctx)
- manager := dispatcher.manager.GetMutableInstance(ctx, userCred, query, data)
- ownerId, err := fetchOwnerId(ctx, manager, userCred, data)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- if len(ctxIds) > 0 {
- dataDict, ok := data.(*jsonutils.JSONDict)
- if !ok {
- return nil, fmt.Errorf("fail to parse body")
- }
- data, err = fetchContextObjectsIds(manager, ctx, userCred, ctxIds, dataDict)
- if err != nil {
- return nil, err
- }
- }
- var policyResult rbacutils.SPolicyResult
- policyResult, err = isClassRbacAllowed(ctx, manager, userCred, ownerId, policy.PolicyActionCreate)
- if err != nil {
- return nil, errors.Wrap(err, "isClassRbacAllowd")
- }
- data.(*jsonutils.JSONDict).Update(policyResult.Json())
- type sCreateResult struct {
- model IModel
- err error
- }
- var (
- multiData []jsonutils.JSONObject
- // onBatchCreateFail func()
- // validateError error
- )
- if InitPendingUsagesInContext != nil {
- ctx = InitPendingUsagesInContext(ctx)
- }
- createResults, err := func() ([]sCreateResult, error) {
- lockman.LockClass(ctx, manager, GetLockClassKey(manager, ownerId))
- defer lockman.ReleaseClass(ctx, manager, GetLockClassKey(manager, ownerId))
- // invoke only Once
- err = manager.BatchPreValidate(ctx, userCred, ownerId, query, data.(*jsonutils.JSONDict), count)
- if err != nil {
- return nil, errors.Wrap(err, "manager.BatchPreValidate")
- }
- multiData, err = expandMultiCreateParams(manager, ctx, userCred, ownerId, query, data, count)
- if err != nil {
- return nil, errors.Wrap(err, "expandMultiCreateParams")
- }
- // one fail, then all fail
- ret := make([]sCreateResult, len(multiData))
- for i := range multiData {
- var model IModel
- log.Debugf("batchCreateDoCreateItem %d %s", i, multiData[i].String())
- model, err = batchCreateDoCreateItem(manager, ctx, userCred, ownerId, query, multiData[i], i+1)
- if err == nil {
- ret[i] = sCreateResult{model: model, err: nil}
- } else {
- break
- }
- }
- if err != nil {
- for i := range ret {
- if ret[i].model != nil {
- DeleteModel(ctx, userCred, ret[i].model)
- ret[i].model = nil
- }
- ret[i].err = err
- }
- return nil, errors.Wrap(err, "batchCreateDoCreateItem")
- } else {
- return ret, nil
- }
- }()
- if err != nil {
- failErr := manager.OnCreateFailed(ctx, userCred, ownerId, query, data)
- if failErr != nil {
- err = errors.Wrapf(err, "%s", failErr.Error())
- }
- return nil, httperrors.NewGeneralError(errors.Wrap(err, "createResults"))
- }
- results := make([]printutils.SubmitResult, count)
- models := make([]IModel, 0)
- for i := range createResults {
- res := createResults[i]
- result := printutils.SubmitResult{}
- if res.err != nil {
- jsonErr := httperrors.NewGeneralError(res.err)
- result.Status = jsonErr.Code
- result.Data = jsonutils.Marshal(jsonErr)
- } else {
- func() {
- lockman.LockObject(ctx, res.model)
- defer lockman.ReleaseObject(ctx, res.model)
- res.model.PostCreate(ctx, userCred, ownerId, query, data)
- }()
- models = append(models, res.model)
- body, err := getItemDetails(manager, res.model, ctx, userCred, query)
- if err != nil {
- result.Status = 500
- result.Data = jsonutils.NewString(err.Error()) // no translation here
- } else {
- result.Status = 200
- result.Data = body
- }
- }
- results[i] = result
- }
- if len(models) > 0 {
- func() {
- lockman.LockClass(ctx, manager, GetLockClassKey(manager, ownerId))
- defer lockman.ReleaseClass(ctx, manager, GetLockClassKey(manager, ownerId))
- manager.OnCreateComplete(ctx, models, userCred, ownerId, query, multiData)
- }()
- }
- return results, nil
- }
- func (dispatcher *DBModelDispatcher) PerformClassAction(ctx context.Context, action string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- // 伪创建,校验创建参数
- if action == "check-create-data" {
- dataDict := data.(*jsonutils.JSONDict)
- dataDict.Set("dry_run", jsonutils.JSONTrue)
- return dispatcher.Create(ctx, query, dataDict, nil)
- }
- // 获取用户信息
- userCred := fetchUserCredential(ctx)
- manager := dispatcher.manager.GetMutableInstance(ctx, userCred, query, data)
- ownerId, err := fetchOwnerId(ctx, manager, userCred, data)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- // 锁住一类
- lockman.LockClass(ctx, manager, GetLockClassKey(manager, ownerId))
- defer lockman.ReleaseClass(ctx, manager, GetLockClassKey(manager, ownerId))
- managerValue := reflect.ValueOf(manager)
- return objectPerformAction(manager, nil, managerValue, ctx, userCred, action, query, data)
- }
- func (dispatcher *DBModelDispatcher) PerformAction(ctx context.Context, idStr string, action string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- userCred := fetchUserCredential(ctx)
- manager := dispatcher.manager.GetMutableInstance(ctx, userCred, query, data)
- model, err := fetchItem(manager, ctx, userCred, idStr, nil)
- if err == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2(manager.Keyword(), idStr)
- } else if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- ret, err := func() (jsonutils.JSONObject, error) {
- lockman.LockObject(ctx, model)
- defer lockman.ReleaseObject(ctx, model)
- if err := model.PreCheckPerformAction(ctx, userCred, action, query, data); err != nil {
- return nil, errors.Wrap(err, "PreCheckPerformAction")
- }
- // 通过action与实例执行请求
- return objectPerformAction(manager, model, reflect.ValueOf(model), ctx, userCred, action, query, data)
- }()
- if err != nil {
- logclient.AddActionLogWithContext(ctx, model, action, err, userCred, false)
- }
- return ret, err
- }
- func objectPerformAction(manager IModelManager, model IModel, modelValue reflect.Value, ctx context.Context, userCred mcclient.TokenCredential, action string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- return reflectDispatcher(manager, model, modelValue, ctx, userCred, policy.PolicyActionPerform, "PerformAction", "Perform", action, query, data)
- }
- func reflectDispatcher(
- // dispatcher *DBModelDispatcher,
- manager IModelManager,
- model IModel,
- modelValue reflect.Value,
- ctx context.Context,
- userCred mcclient.TokenCredential,
- operator string,
- generalFuncName string,
- funcPrefix string,
- spec string,
- query jsonutils.JSONObject,
- data jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- result, err := reflectDispatcherInternal(
- manager, model, modelValue, ctx, userCred, operator, generalFuncName, funcPrefix, spec, query, data)
- if model != nil && err == nil && result == nil {
- return getItemDetails(manager, model, ctx, userCred, query)
- } else {
- return result, err
- }
- }
- func reflectDispatcherInternal(
- // dispatcher *DBModelDispatcher,
- manager IModelManager,
- model IModel,
- modelValue reflect.Value,
- ctx context.Context,
- userCred mcclient.TokenCredential,
- operator string,
- generalFuncName string,
- funcPrefix string,
- spec string,
- query jsonutils.JSONObject,
- data jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- isGeneral := false
- // 优先通过action查找该model下的PerformXXX方法
- funcName := fmt.Sprintf("%s%s", funcPrefix, utils.Kebab2Camel(spec, "-"))
- funcValue := modelValue.MethodByName(funcName)
- // 若不存在该方法则根据generalFuncName查找model下的PerformAction方法
- if !funcValue.IsValid() || funcValue.IsNil() {
- funcValue = modelValue.MethodByName(generalFuncName)
- if !funcValue.IsValid() || funcValue.IsNil() {
- return nil, httperrors.NewActionNotFoundError("%s %s %s not found, please check service version, current version: %s",
- manager.Keyword(), operator, spec, version.GetShortString())
- } else {
- isGeneral = true
- funcName = generalFuncName
- }
- }
- var params []interface{}
- if isGeneral {
- params = []interface{}{ctx, userCred, spec, query, data}
- } else {
- params = []interface{}{ctx, userCred, query, data}
- }
- // 若perform指定一类资源,则当前用户对一类资源的权限,否则校验用户对该资源的权限
- var result rbacutils.SPolicyResult
- if model == nil {
- ownerId, err := fetchOwnerId(ctx, manager, userCred, data)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- _, err = isClassRbacAllowed(ctx, manager, userCred, ownerId, operator, spec)
- if err != nil {
- return nil, err
- }
- } else {
- var err error
- result, err = isObjectRbacAllowedResult(ctx, model, userCred, operator, spec)
- if err != nil {
- return nil, err
- }
- }
- // 调用反射的方法
- outs, err := callFunc(funcValue, funcName, params...)
- if err != nil {
- return nil, err
- }
- // perform方法返回值为jsonutils.JSONObject,error
- // 对于perform方法返回值数量不为2时,默认不合法
- if len(outs) != 2 {
- return nil, httperrors.NewInternalServerError("Invald %s return value", funcName)
- }
- resVal := outs[0]
- errVal := outs[1].Interface()
- if !gotypes.IsNil(errVal) {
- return nil, errVal.(error)
- } else {
- if model != nil {
- if _, ok := model.(IStandaloneModel); ok {
- Metadata.rawSetValues(ctx, model.Keyword(), model.GetId(), tagutils.TagsetMap2MapString(result.ObjectTags.Flattern()), false, "")
- if model.Keyword() == "project" {
- Metadata.rawSetValues(ctx, model.Keyword(), model.GetId(), tagutils.TagsetMap2MapString(result.ProjectTags.Flattern()), false, "")
- } else if model.Keyword() == "domain" {
- Metadata.rawSetValues(ctx, model.Keyword(), model.GetId(), tagutils.TagsetMap2MapString(result.DomainTags.Flattern()), false, "")
- }
- }
- }
- if gotypes.IsNil(resVal.Interface()) {
- return nil, nil
- } else {
- return ValueToJSONObject(resVal), nil
- }
- }
- }
- func DoUpdate(manager IModelManager, item IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- lockman.LockObject(ctx, item)
- defer lockman.ReleaseObject(ctx, item)
- return updateItem(manager, item, ctx, userCred, query, data)
- }
- func updateItem(manager IModelManager, item IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- var err error
- // 校验update入参钩子
- err = item.ValidateUpdateCondition(ctx)
- if err != nil {
- return nil, httperrors.NewGeneralError(errors.Wrapf(err, "ValidateUpdateCondition"))
- }
- dataDict, ok := data.(*jsonutils.JSONDict)
- if !ok {
- return nil, httperrors.NewInternalServerError("Invalid data JSONObject")
- }
- dataDict, err = ValidateUpdateData(item, ctx, userCred, query, dataDict)
- if err != nil {
- return nil, httperrors.NewGeneralError(errors.Wrapf(err, "ValidateUpdateData"))
- }
- if manager.HasName() && dataDict.Contains("name") {
- // validate altername
- nameStr, _ := dataDict.GetString("name")
- err := alterNameValidator(ctx, item, nameStr)
- if err != nil {
- return nil, errors.Wrap(err, "alterNameValidator")
- }
- }
- item.PreUpdate(ctx, userCred, query, dataDict)
- diff, err := Update(item, func() error {
- filterData := dataDict.CopyIncludes(updateFields(manager, userCred)...)
- err = filterData.Unmarshal(item)
- if err != nil {
- return httperrors.NewGeneralError(errors.Wrapf(err, "filterData.Unmarshal"))
- }
- return nil
- })
- if err != nil {
- return nil, httperrors.NewGeneralError(errors.Wrapf(err, "Update"))
- }
- for _, skip := range skipLogFields(manager) {
- delete(diff, skip)
- }
- if len(diff) > 0 {
- OpsLog.LogEvent(item, ACT_UPDATE, diff, userCred)
- logclient.AddActionLogWithContext(ctx, item, logclient.ACT_UPDATE, diff, userCred, true)
- CallUpdateNotifyHook(ctx, userCred, item)
- }
- item.PostUpdate(ctx, userCred, query, data)
- if err := manager.GetExtraHook().AfterPostUpdate(ctx, userCred, item, query, data); err != nil {
- logclient.AddActionLogWithContext(ctx, item, logclient.ACT_POST_UPDATE_HOOK, err, userCred, false)
- }
- return getItemDetails(manager, item, ctx, userCred, query)
- }
- func (dispatcher *DBModelDispatcher) FetchUpdateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error) {
- return dispatcher.manager.FetchUpdateHeaderData(ctx, header)
- }
- func (dispatcher *DBModelDispatcher) Update(ctx context.Context, idStr string, query jsonutils.JSONObject, data jsonutils.JSONObject, ctxIds []dispatcher.SResourceContext) (jsonutils.JSONObject, error) {
- userCred := fetchUserCredential(ctx)
- if data == nil {
- data = jsonutils.NewDict()
- }
- manager := dispatcher.manager.GetMutableInstance(ctx, userCred, query, data)
- model, err := fetchItem(manager, ctx, userCred, idStr, nil)
- if err == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2(manager.Keyword(), idStr)
- } else if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- result, err := isObjectRbacAllowedResult(ctx, model, userCred, policy.PolicyActionUpdate)
- if err != nil {
- return nil, err
- }
- data.(*jsonutils.JSONDict).Update(result.Json())
- if len(ctxIds) > 0 {
- ctxObjs, err := fetchContextObjects(manager, ctx, userCred, ctxIds)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- return model.UpdateInContext(ctx, userCred, ctxObjs, query, data)
- } else {
- lockman.LockObject(ctx, model)
- defer lockman.ReleaseObject(ctx, model)
- return updateItem(manager, model, ctx, userCred, query, data)
- }
- }
- func (dispatcher *DBModelDispatcher) UpdateSpec(ctx context.Context, idStr string, spec string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- userCred := fetchUserCredential(ctx)
- manager := dispatcher.manager.GetMutableInstance(ctx, userCred, query, data)
- model, err := fetchItem(manager, ctx, userCred, idStr, nil)
- if err == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2(manager.Keyword(), idStr)
- } else if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- lockman.LockObject(ctx, model)
- defer lockman.ReleaseObject(ctx, model)
- return objectUpdateSpec(manager, model, reflect.ValueOf(model), ctx, userCred, spec, query, data)
- }
- func objectUpdateSpec(manager IModelManager, model IModel, modelValue reflect.Value, ctx context.Context, userCred mcclient.TokenCredential, spec string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- return reflectDispatcher(manager, model, modelValue, ctx, userCred, policy.PolicyActionUpdate, "UpdateSpec", "Update", spec, query, data)
- }
- func DeleteModel(ctx context.Context, userCred mcclient.TokenCredential, item IModel) error {
- // cleanModelUsages(ctx, userCred, item)
- _, err := Update(item, func() error {
- return item.MarkDelete()
- })
- if err != nil {
- return httperrors.NewGeneralError(errors.Wrapf(err, "db.Update"))
- }
- if userCred != nil {
- OpsLog.LogEvent(item, ACT_DELETE, item.GetShortDesc(ctx), userCred)
- logclient.AddSimpleActionLog(item, logclient.ACT_DELETE, item.GetShortDesc(ctx), userCred, true)
- }
- if _, ok := item.(IStandaloneModel); ok && len(item.GetId()) > 0 {
- err := Metadata.RemoveAll(ctx, item, userCred)
- if err != nil {
- return errors.Wrapf(err, "Metadata.RemoveAll")
- }
- }
- return nil
- }
- func RealDeleteModel(ctx context.Context, userCred mcclient.TokenCredential, item IModel) error {
- if len(item.GetId()) == 0 {
- return DeleteModel(ctx, userCred, item)
- }
- _, err := sqlchemy.GetDB().Exec(
- fmt.Sprintf(
- "delete from %s where id = ?",
- item.GetModelManager().TableSpec().Name(),
- ), item.GetId(),
- )
- if err != nil {
- return httperrors.NewGeneralError(errors.Wrapf(err, "db.Update"))
- }
- if userCred != nil {
- OpsLog.LogEvent(item, ACT_DELETE, item.GetShortDesc(ctx), userCred)
- logclient.AddSimpleActionLog(item, logclient.ACT_DELETE, item.GetShortDesc(ctx), userCred, true)
- }
- if _, ok := item.(IStandaloneModel); ok && len(item.GetId()) > 0 {
- err := Metadata.RemoveAll(ctx, item, userCred)
- if err != nil {
- return errors.Wrapf(err, "Metadata.RemoveAll")
- }
- }
- return nil
- }
- func deleteItem(manager IModelManager, model IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- // 获取实例详情
- details, err := getItemDetails(manager, model, ctx, userCred, query)
- if err != nil {
- return nil, httperrors.NewGeneralError(errors.Wrapf(err, "getItemDetails"))
- }
- // 删除校验
- err = ValidateDeleteCondition(model, ctx, details)
- if err != nil {
- return nil, err
- }
- // 删除前钩子
- err = CustomizeDelete(model, ctx, userCred, query, data)
- if err != nil {
- return nil, httperrors.NewGeneralError(errors.Wrapf(err, "CustomizeDelete"))
- }
- model.PreDelete(ctx, userCred)
- // 实际删除
- err = model.Delete(ctx, userCred)
- if err != nil {
- return nil, errors.Wrapf(err, "Delete")
- }
- // 删除后钩子
- model.PostDelete(ctx, userCred)
- if err := model.GetModelManager().GetExtraHook().AfterPostDelete(ctx, userCred, model, query); err != nil {
- logclient.AddActionLogWithContext(ctx, model, logclient.ACT_POST_DELETE_HOOK, err, userCred, false)
- }
- // 避免设置删除状态没有正常返回
- jsonutils.Update(details, model)
- return details, nil
- }
- func (dispatcher *DBModelDispatcher) Delete(ctx context.Context, idstr string, query jsonutils.JSONObject, data jsonutils.JSONObject, ctxIds []dispatcher.SResourceContext) (jsonutils.JSONObject, error) {
- userCred := fetchUserCredential(ctx)
- manager := dispatcher.manager.GetMutableInstance(ctx, userCred, query, data)
- // 找到实例
- model, err := fetchItem(manager, ctx, userCred, idstr, nil)
- if err == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2(manager.Keyword(), idstr)
- } else if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- // 校验角色
- err = isObjectRbacAllowed(ctx, model, userCred, policy.PolicyActionDelete)
- if err != nil {
- return nil, err
- }
- if len(ctxIds) > 0 {
- ctxObjs, err := fetchContextObjects(manager, ctx, userCred, ctxIds)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- return model.DeleteInContext(ctx, userCred, ctxObjs, query, data)
- } else {
- lockman.LockObject(ctx, model)
- defer lockman.ReleaseObject(ctx, model)
- return deleteItem(manager, model, ctx, userCred, query, data)
- }
- }
- func (dispatcher *DBModelDispatcher) DeleteSpec(ctx context.Context, idstr string, spec string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- userCred := fetchUserCredential(ctx)
- manager := dispatcher.manager.GetMutableInstance(ctx, userCred, query, data)
- model, err := fetchItem(manager, ctx, userCred, idstr, nil)
- if err == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2(manager.Keyword(), idstr)
- } else if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- lockman.LockObject(ctx, model)
- defer lockman.ReleaseObject(ctx, model)
- return objectDeleteSpec(manager, model, reflect.ValueOf(model), ctx, userCred, spec, query, data)
- }
- func objectDeleteSpec(manager IModelManager, model IModel, modelValue reflect.Value, ctx context.Context, userCred mcclient.TokenCredential, spec string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- return reflectDispatcher(manager, model, modelValue, ctx, userCred, policy.PolicyActionDelete, "DeleteSpec", "Delete", spec, query, data)
- }
|