domainresource.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. "yunion.io/x/jsonutils"
  18. "yunion.io/x/log"
  19. "yunion.io/x/pkg/errors"
  20. "yunion.io/x/pkg/gotypes"
  21. "yunion.io/x/pkg/utils"
  22. "yunion.io/x/sqlchemy"
  23. "yunion.io/x/onecloud/pkg/apis"
  24. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  25. "yunion.io/x/onecloud/pkg/httperrors"
  26. "yunion.io/x/onecloud/pkg/mcclient"
  27. "yunion.io/x/onecloud/pkg/util/logclient"
  28. "yunion.io/x/onecloud/pkg/util/stringutils2"
  29. )
  30. type SDomainLevelResourceBaseManager struct {
  31. SStandaloneResourceBaseManager
  32. SDomainizedResourceBaseManager
  33. }
  34. func NewDomainLevelResourceBaseManager(
  35. dt interface{},
  36. tableName string,
  37. keyword string,
  38. keywordPlural string,
  39. ) SDomainLevelResourceBaseManager {
  40. return SDomainLevelResourceBaseManager{
  41. SStandaloneResourceBaseManager: NewStandaloneResourceBaseManager(dt, tableName, keyword, keywordPlural),
  42. }
  43. }
  44. type SDomainLevelResourceBase struct {
  45. SStandaloneResourceBase
  46. SDomainizedResourceBase
  47. // 归属Domain信息的来源, local: 本地设置, cloud: 从云上同步过来
  48. // example: local
  49. DomainSrc string `width:"10" charset:"ascii" nullable:"true" list:"user" default:"" json:"domain_src"`
  50. }
  51. func (manager *SDomainLevelResourceBaseManager) ListItemFilter(
  52. ctx context.Context,
  53. q *sqlchemy.SQuery,
  54. userCred mcclient.TokenCredential,
  55. query apis.DomainLevelResourceListInput,
  56. ) (*sqlchemy.SQuery, error) {
  57. var err error
  58. q, err = manager.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StandaloneResourceListInput)
  59. if err != nil {
  60. return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.ListItemFilter")
  61. }
  62. q, err = manager.SDomainizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.DomainizedResourceListInput)
  63. if err != nil {
  64. return nil, errors.Wrap(err, "SDomainizedResourceBaseManager.ListItemFilter")
  65. }
  66. return q, nil
  67. }
  68. func (manager *SDomainLevelResourceBaseManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  69. q, err := manager.SStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
  70. if err == nil {
  71. return q, nil
  72. }
  73. q, err = manager.SDomainizedResourceBaseManager.QueryDistinctExtraField(q, field)
  74. if err == nil {
  75. return q, nil
  76. }
  77. return q, httperrors.ErrNotFound
  78. }
  79. func (manager *SDomainLevelResourceBaseManager) OrderByExtraFields(
  80. ctx context.Context,
  81. q *sqlchemy.SQuery,
  82. userCred mcclient.TokenCredential,
  83. query apis.DomainLevelResourceListInput,
  84. ) (*sqlchemy.SQuery, error) {
  85. q, err := manager.SStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StandaloneResourceListInput)
  86. if err != nil {
  87. return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.OrderByExtraFields")
  88. }
  89. q, err = manager.SDomainizedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.DomainizedResourceListInput)
  90. if err != nil {
  91. return nil, errors.Wrap(err, "SDomainizedResourceBaseManager.OrderByExtraFields")
  92. }
  93. return q, nil
  94. }
  95. func (model *SDomainLevelResourceBase) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  96. model.DomainId = ownerId.GetProjectDomainId()
  97. model.DomainSrc = string(apis.OWNER_SOURCE_LOCAL)
  98. return model.SStandaloneResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
  99. }
  100. // 更改项目
  101. func (model *SDomainLevelResourceBase) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeDomainOwnerInput) (jsonutils.JSONObject, error) {
  102. if !consts.GetNonDefaultDomainProjects() {
  103. return nil, errors.Wrap(httperrors.ErrForbidden, "not allow to change owner of domain resource if non_default_domain_projects is turned off")
  104. }
  105. if model.GetIStandaloneModel().IsShared() {
  106. return nil, errors.Wrap(httperrors.ErrForbidden, "cannot change owner of shared resource")
  107. }
  108. manager := model.GetModelManager()
  109. data := jsonutils.Marshal(input)
  110. log.Debugf("SDomainLevelResourceBase change_owner %s %s %#v", query, data, manager)
  111. ownerId, err := manager.FetchOwnerId(ctx, data)
  112. if err != nil {
  113. return nil, httperrors.NewGeneralError(err)
  114. }
  115. if gotypes.IsNil(ownerId) {
  116. return nil, httperrors.NewMissingParameterError("domain_id")
  117. }
  118. if len(ownerId.GetProjectDomainId()) == 0 {
  119. return nil, httperrors.NewInputParameterError("missing new domain")
  120. }
  121. if ownerId.GetProjectDomainId() == model.DomainId {
  122. // do nothing
  123. Update(model, func() error {
  124. model.DomainSrc = string(apis.OWNER_SOURCE_LOCAL)
  125. return nil
  126. })
  127. return nil, nil
  128. }
  129. // change domain, do check
  130. candidates := model.GetIDomainLevelModel().GetChangeOwnerCandidateDomainIds()
  131. if len(candidates) > 0 && !utils.IsInStringArray(ownerId.GetProjectDomainId(), candidates) {
  132. return nil, errors.Wrap(httperrors.ErrForbidden, "target domain not in change owner candidate list")
  133. }
  134. requires := model.GetIDomainLevelModel().GetChangeOwnerRequiredDomainIds()
  135. log.Debugf("%s required domains: %s", model.Keyword(), requires)
  136. if len(requires) > 0 && !utils.IsInStringArray(ownerId.GetProjectDomainId(), requires) {
  137. return nil, errors.Wrap(httperrors.ErrForbidden, "target domain not in change owner required list")
  138. }
  139. if !IsAdminAllowPerform(ctx, userCred, model, "change-owner") {
  140. return nil, errors.Wrap(httperrors.ErrNotSufficientPrivilege, "require system privileges")
  141. }
  142. q := manager.Query().Equals("name", model.GetName())
  143. q = manager.FilterByOwner(ctx, q, manager, userCred, ownerId, manager.NamespaceScope())
  144. q = manager.FilterBySystemAttributes(q, nil, nil, manager.ResourceScope())
  145. q = q.NotEquals("id", model.GetId())
  146. cnt, err := q.CountWithError()
  147. if err != nil {
  148. return nil, httperrors.NewInternalServerError("check name duplication error: %s", err)
  149. }
  150. if cnt > 0 {
  151. return nil, httperrors.NewDuplicateNameError("name", model.GetName())
  152. }
  153. former, _ := TenantCacheManager.FetchDomainById(ctx, model.DomainId)
  154. if former == nil {
  155. log.Warningf("domain_id %s not found", model.DomainId)
  156. formerObj := NewDomain(model.DomainId, "unknown")
  157. former = &formerObj
  158. }
  159. // clean shared projects before update domain id
  160. if sharedModel, ok := model.GetIDomainLevelModel().(ISharableBaseModel); ok {
  161. if err := SharedResourceManager.CleanModelShares(ctx, userCred, sharedModel); err != nil {
  162. return nil, err
  163. }
  164. }
  165. // cancel usage
  166. model.cleanModelUsages(ctx, userCred)
  167. _, err = Update(model, func() error {
  168. model.DomainId = ownerId.GetProjectDomainId()
  169. model.DomainSrc = string(apis.OWNER_SOURCE_LOCAL)
  170. return nil
  171. })
  172. if err != nil {
  173. return nil, errors.Wrap(err, "Update")
  174. }
  175. // add usage
  176. model.RecoverUsages(ctx, userCred)
  177. OpsLog.SyncOwner(model, former, userCred)
  178. notes := struct {
  179. OldDomainId string
  180. OldDomain string
  181. NewDomainId string
  182. NewDomain string
  183. }{
  184. OldDomainId: former.DomainId,
  185. OldDomain: former.Domain,
  186. NewDomainId: ownerId.GetProjectDomainId(),
  187. NewDomain: ownerId.GetProjectDomain(),
  188. }
  189. logclient.AddActionLogWithContext(ctx, model, logclient.ACT_CHANGE_OWNER, notes, userCred, true)
  190. return nil, nil
  191. }
  192. func (manager *SDomainLevelResourceBaseManager) FetchCustomizeColumns(
  193. ctx context.Context,
  194. userCred mcclient.TokenCredential,
  195. query jsonutils.JSONObject,
  196. objs []interface{},
  197. fields stringutils2.SSortedStrings,
  198. isList bool,
  199. ) []apis.DomainLevelResourceDetails {
  200. rows := make([]apis.DomainLevelResourceDetails, len(objs))
  201. stdRows := manager.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  202. domainRows := manager.SDomainizedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  203. for i := range rows {
  204. rows[i] = apis.DomainLevelResourceDetails{
  205. StandaloneResourceDetails: stdRows[i],
  206. DomainizedResourceInfo: domainRows[i],
  207. }
  208. }
  209. return rows
  210. }
  211. func (manager *SDomainLevelResourceBaseManager) GetIDomainLevelModelManager() IDomainLevelModelManager {
  212. return manager.GetVirtualObject().(IDomainLevelModelManager)
  213. }
  214. func (manager *SDomainLevelResourceBaseManager) ValidateCreateData(
  215. ctx context.Context,
  216. userCred mcclient.TokenCredential,
  217. ownerId mcclient.IIdentityProvider,
  218. query jsonutils.JSONObject,
  219. input apis.DomainLevelResourceCreateInput,
  220. ) (apis.DomainLevelResourceCreateInput, error) {
  221. var err error
  222. input.StandaloneResourceCreateInput, err = manager.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StandaloneResourceCreateInput)
  223. if err != nil {
  224. return input, errors.Wrap(err, "SStandaloneResourceBaseManager.ValidateCreateData")
  225. }
  226. return input, nil
  227. }
  228. func (model *SDomainLevelResourceBase) DomainLevelModelManager() IDomainLevelModelManager {
  229. return model.GetModelManager().(IDomainLevelModelManager)
  230. }
  231. func (model *SDomainLevelResourceBase) IsOwner(userCred mcclient.TokenCredential) bool {
  232. return model.DomainId == userCred.GetProjectDomainId()
  233. }
  234. func (model *SDomainLevelResourceBase) SyncCloudDomainId(userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider) {
  235. if model.DomainSrc != string(apis.OWNER_SOURCE_LOCAL) && ownerId != nil && len(ownerId.GetProjectDomainId()) > 0 {
  236. diff, _ := Update(model, func() error {
  237. model.DomainSrc = string(apis.OWNER_SOURCE_CLOUD)
  238. model.DomainId = ownerId.GetProjectDomainId()
  239. return nil
  240. })
  241. if len(diff) > 0 {
  242. OpsLog.LogEvent(model, ACT_SYNC_OWNER, diff, userCred)
  243. }
  244. }
  245. }
  246. func (model *SDomainLevelResourceBase) GetIDomainLevelModel() IDomainLevelModel {
  247. return model.GetVirtualObject().(IDomainLevelModel)
  248. }
  249. func (model *SDomainLevelResourceBase) ValidateUpdateData(
  250. ctx context.Context,
  251. userCred mcclient.TokenCredential,
  252. query jsonutils.JSONObject,
  253. input apis.DomainLevelResourceBaseUpdateInput,
  254. ) (apis.DomainLevelResourceBaseUpdateInput, error) {
  255. var err error
  256. input.StandaloneResourceBaseUpdateInput, err = model.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput)
  257. if err != nil {
  258. return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData")
  259. }
  260. return input, nil
  261. }
  262. // +onecloud:swagger-gen-ignore
  263. func (model *SDomainLevelResourceBase) GetDetailsChangeOwnerCandidateDomains(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (apis.ChangeOwnerCandidateDomainsOutput, error) {
  264. return IOwnerResourceBaseModelGetChangeOwnerCandidateDomains(model.GetIDomainLevelModel())
  265. }
  266. func (manager *SDomainLevelResourceBaseManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) {
  267. q, err := manager.SStandaloneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  268. if err != nil {
  269. return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.ListItemExportKeys")
  270. }
  271. q, err = manager.SDomainizedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  272. if err != nil {
  273. return nil, errors.Wrap(err, "SDomainizedResourceBaseManager.ListItemExportKeys")
  274. }
  275. return q, nil
  276. }
  277. // +onecloud:swagger-gen-ignore
  278. func (manager *SDomainLevelResourceBaseManager) GetPropertyDomainTagValuePairs(
  279. ctx context.Context,
  280. userCred mcclient.TokenCredential,
  281. query jsonutils.JSONObject,
  282. ) (jsonutils.JSONObject, error) {
  283. return GetPropertyTagValuePairs(
  284. manager.GetIDomainLevelModelManager(),
  285. "domain",
  286. "domain_id",
  287. ctx,
  288. userCred,
  289. query,
  290. )
  291. }
  292. // +onecloud:swagger-gen-ignore
  293. func (manager *SDomainLevelResourceBaseManager) GetPropertyDomainTagValueTree(
  294. ctx context.Context,
  295. userCred mcclient.TokenCredential,
  296. query jsonutils.JSONObject,
  297. ) (jsonutils.JSONObject, error) {
  298. return GetPropertyTagValueTree(
  299. manager.GetIDomainLevelModelManager(),
  300. "domain",
  301. "domain_id",
  302. "",
  303. ctx,
  304. userCred,
  305. query,
  306. )
  307. }