notify_internal.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  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 notifyclient
  15. import (
  16. "context"
  17. "fmt"
  18. "html/template"
  19. "regexp"
  20. "strings"
  21. "time"
  22. "golang.org/x/text/language"
  23. "yunion.io/x/jsonutils"
  24. "yunion.io/x/log"
  25. "yunion.io/x/pkg/appctx"
  26. "yunion.io/x/pkg/errors"
  27. "yunion.io/x/pkg/util/httputils"
  28. "yunion.io/x/pkg/util/sets"
  29. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  30. "yunion.io/x/onecloud/pkg/mcclient"
  31. "yunion.io/x/onecloud/pkg/mcclient/modulebase"
  32. "yunion.io/x/onecloud/pkg/mcclient/modules/identity"
  33. npk "yunion.io/x/onecloud/pkg/mcclient/modules/notify"
  34. )
  35. func notifySystemWarning(ctx context.Context, idstr string, name string, event string, reason string) {
  36. msg := SSystemEventMsg{
  37. Id: idstr,
  38. Name: name,
  39. Event: event,
  40. Reason: reason,
  41. Created: time.Now(),
  42. }
  43. systemNotify(ctx, npk.NotifyPriorityImportant, SYSTEM_WARNING, jsonutils.Marshal(msg))
  44. }
  45. func notifyGeneralSystemError(ctx context.Context, data jsonutils.JSONObject) {
  46. systemNotify(ctx, npk.NotifyPriorityCritical, SYSTEM_ERROR, data)
  47. }
  48. func notifySystemError(ctx context.Context, idstr string, name string, event string, reason string) {
  49. msg := SSystemEventMsg{
  50. Id: idstr,
  51. Name: name,
  52. Event: event,
  53. Reason: reason,
  54. Created: time.Now(),
  55. }
  56. systemNotify(ctx, npk.NotifyPriorityCritical, SYSTEM_ERROR, jsonutils.Marshal(msg))
  57. }
  58. func systemNotify(ctx context.Context, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) {
  59. systemNotifyWithTemplateFuncs(ctx, priority, event, data, nil)
  60. }
  61. func systemNotifyWithTemplateFuncs(ctx context.Context, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject, templateFuncs template.FuncMap) {
  62. // userId
  63. notifyWithTemplateFuncs(ctx, notifyAdminUsers, false, priority, event, data, templateFuncs)
  64. // groupId
  65. notifyWithTemplateFuncs(ctx, notifyAdminGroups, true, priority, event, data, templateFuncs)
  66. }
  67. func notifyAll(ctx context.Context, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
  68. return notifyAllWithTemplateFuncs(ctx, recipientId, isGroup, priority, event, data, nil)
  69. }
  70. func notifyAllWithTemplateFuncs(ctx context.Context, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject, templateFuncs template.FuncMap) error {
  71. s, err := AdminSessionGenerator(ctx, consts.GetRegion())
  72. if err != nil {
  73. return err
  74. }
  75. params := jsonutils.NewDict()
  76. result, err := npk.NotifyReceiver.PerformClassAction(s, "get-types", params)
  77. if err != nil {
  78. return err
  79. }
  80. if result == nil {
  81. return nil
  82. }
  83. jarray, err := result.Get("types")
  84. if err != nil {
  85. return err
  86. }
  87. cTypes := jarray.(*jsonutils.JSONArray).GetStringArray()
  88. for _, ct := range cTypes {
  89. RawNotifyWithCtxAndTemplateFuncs(ctx, recipientId, isGroup, npk.TNotifyChannel(ct), priority, event, data, templateFuncs)
  90. }
  91. return nil
  92. }
  93. type sTarget struct {
  94. reIds []string
  95. contacts []string
  96. robots []string
  97. }
  98. func langRobot(ctx context.Context, robots []string) (map[language.Tag]*sTarget, error) {
  99. contextLang := appctx.Lang(ctx)
  100. robotLang, err := getRobotLang(robots)
  101. if err != nil {
  102. return nil, err
  103. }
  104. ret := make(map[language.Tag]*sTarget)
  105. for robot, langStr := range robotLang {
  106. lang, err := language.Parse(langStr)
  107. if err != nil {
  108. log.Errorf("can't parse %s to language.Tag: %v", langStr, err)
  109. lang = contextLang
  110. }
  111. t := ret[lang]
  112. if t == nil {
  113. ret[lang] = &sTarget{}
  114. t = ret[lang]
  115. }
  116. t.robots = append(t.robots, robot)
  117. }
  118. return ret, nil
  119. }
  120. func lang(ctx context.Context, contactType npk.TNotifyChannel, reIds []string, contacts []string) (map[language.Tag]*sTarget, error) {
  121. contextLang := appctx.Lang(ctx)
  122. langMap := make(map[language.Tag]*sTarget)
  123. insertReid := func(lang language.Tag, id string) {
  124. t := langMap[lang]
  125. if t == nil {
  126. t = &sTarget{}
  127. langMap[lang] = t
  128. }
  129. t.reIds = append(t.reIds, id)
  130. }
  131. insertContact := func(lang language.Tag, id string) {
  132. t := langMap[lang]
  133. if t == nil {
  134. t = &sTarget{}
  135. langMap[lang] = t
  136. }
  137. t.contacts = append(t.contacts, id)
  138. }
  139. uids := append([]string{}, reIds...)
  140. if contactType == npk.NotifyByWebConsole {
  141. uids = append(uids, contacts...)
  142. }
  143. uidLang, err := UserLangFetcher(uids)
  144. if err != nil {
  145. return nil, errors.Wrap(err, "unable to feth UserLang")
  146. }
  147. insert := func(id string, insertFunc func(language.Tag, string)) {
  148. langStr := uidLang[id]
  149. if len(langStr) == 0 {
  150. insertFunc(contextLang, id)
  151. return
  152. }
  153. lang, err := language.Parse(langStr)
  154. if err != nil {
  155. log.Errorf("can't parse %s to language.Tag: %v", langStr, err)
  156. insertFunc(contextLang, id)
  157. return
  158. }
  159. insertFunc(lang, id)
  160. }
  161. for _, reid := range reIds {
  162. insert(reid, insertReid)
  163. }
  164. if contactType == npk.NotifyByWebConsole {
  165. for _, contact := range contacts {
  166. insert(contact, insertContact)
  167. }
  168. } else {
  169. for _, cs := range contacts {
  170. insertContact(contextLang, cs)
  171. }
  172. }
  173. return langMap, nil
  174. }
  175. func isEmptyRecipients(recipientId, robots []string) bool {
  176. var recvs []string
  177. for _, c := range recipientId {
  178. if len(c) > 0 {
  179. recvs = append(recvs, c)
  180. }
  181. }
  182. for _, robot := range robots {
  183. if len(robot) > 0 {
  184. recvs = append(recvs, robot)
  185. }
  186. }
  187. return len(recvs) == 0
  188. }
  189. func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage, error) {
  190. reIds := make([]string, 0)
  191. s, err := AdminSessionGenerator(context.Background(), consts.GetRegion())
  192. if err != nil {
  193. return nil, err
  194. }
  195. if p.isGroup {
  196. // fetch uid
  197. uidSet := sets.NewString()
  198. for _, gid := range p.recipientId {
  199. users, err := identity.Groups.GetUsers(s, gid, nil)
  200. if err != nil {
  201. return nil, errors.Wrapf(err, "Groups.GetUsers for group %q", gid)
  202. }
  203. for i := range users.Data {
  204. id, _ := users.Data[i].GetString("id")
  205. uidSet.Insert(id)
  206. }
  207. }
  208. reIds = append(reIds, uidSet.UnsortedList()...)
  209. } else {
  210. reIds = p.recipientId
  211. }
  212. if isEmptyRecipients(p.recipientId, p.robots) {
  213. return nil, errors.Wrap(errors.ErrEmpty, "empty receipients")
  214. }
  215. if !hasTemplateOfTopic(p.event) {
  216. msg := npk.SNotifyMessage{}
  217. msg.Uid = reIds
  218. msg.Priority = p.priority
  219. msg.Robots = p.robots
  220. msg.Contacts = p.contacts
  221. msg.ContactType = p.channel
  222. msg.Topic = p.event
  223. msg.Msg = p.data.String()
  224. msg.Tag = p.tag
  225. msg.Metadata = p.metadata
  226. msg.IgnoreNonexistentReceiver = p.ignoreNonexistentReceiver
  227. return []npk.SNotifyMessage{msg}, nil
  228. }
  229. var langMap map[language.Tag]*sTarget
  230. if p.channel == npk.NotifyByRobot {
  231. langMap, err = langRobot(ctx, p.robots)
  232. if err != nil {
  233. return nil, err
  234. }
  235. } else {
  236. langMap, err = lang(ctx, p.channel, reIds, p.contacts)
  237. if err != nil {
  238. return nil, err
  239. }
  240. }
  241. msgs := make([]npk.SNotifyMessage, 0, len(langMap))
  242. for lang, t := range langMap {
  243. langSuffix := notifyclientI18nTable.LookupByLang(lang, suffix)
  244. msg := npk.SNotifyMessage{}
  245. msg.Uid = t.reIds
  246. msg.Priority = p.priority
  247. msg.Robots = p.robots
  248. msg.Contacts = t.contacts
  249. msg.ContactType = p.channel
  250. topic, err := getContent(langSuffix, p.event, "title", p.channel, p.data, p.templateFuncs)
  251. if err != nil {
  252. log.Warningf("get title error: %s", err)
  253. }
  254. if len(topic) == 0 {
  255. topic = p.event
  256. }
  257. msg.Topic = topic
  258. body, err := getContent(langSuffix, p.event, "content", p.channel, p.data, p.templateFuncs)
  259. if err != nil {
  260. log.Errorf("get content error: %s", err)
  261. }
  262. if len(body) == 0 {
  263. body, _ = p.data.GetString()
  264. }
  265. msg.Msg = body
  266. msg.Tag = p.tag
  267. msg.Metadata = p.metadata
  268. msg.IgnoreNonexistentReceiver = p.ignoreNonexistentReceiver
  269. msgs = append(msgs, msg)
  270. }
  271. return msgs, nil
  272. }
  273. type sNotifyParams struct {
  274. recipientId []string
  275. robots []string
  276. isGroup bool
  277. contacts []string
  278. channel npk.TNotifyChannel
  279. priority npk.TNotifyPriority
  280. event string
  281. data jsonutils.JSONObject
  282. createReceiver bool
  283. tag string
  284. metadata map[string]interface{}
  285. ignoreNonexistentReceiver bool
  286. templateFuncs template.FuncMap
  287. }
  288. // newSNotifyParams 创建 sNotifyParams 结构体,event 和 data 是必需参数
  289. func newSNotifyParams(event string, data jsonutils.JSONObject) sNotifyParams {
  290. return sNotifyParams{
  291. event: event,
  292. data: data,
  293. }
  294. }
  295. // withRecipientAndPriority 设置 recipientId、isGroup、priority 和 templateFuncs(用于 notifyCritical/Important/Normal)
  296. func (p sNotifyParams) withRecipientAndPriority(recipientId []string, isGroup bool, priority npk.TNotifyPriority, templateFuncs template.FuncMap) sNotifyParams {
  297. p.recipientId = recipientId
  298. p.isGroup = isGroup
  299. p.priority = priority
  300. p.templateFuncs = templateFuncs
  301. return p
  302. }
  303. // withRecipientChannelAndPriority 设置 recipientId、isGroup、channel、priority 和 templateFuncs(用于 RawNotify)
  304. func (p sNotifyParams) withRecipientChannelAndPriority(recipientId []string, isGroup bool, channel npk.TNotifyChannel, priority npk.TNotifyPriority, templateFuncs template.FuncMap) sNotifyParams {
  305. p.recipientId = recipientId
  306. p.isGroup = isGroup
  307. p.channel = channel
  308. p.priority = priority
  309. p.templateFuncs = templateFuncs
  310. return p
  311. }
  312. // withRobotChannelAndPriority 设置 robots、channel、priority 和 templateFuncs(用于 NotifyRobot)
  313. func (p sNotifyParams) withRobotChannelAndPriority(robots []string, channel npk.TNotifyChannel, priority npk.TNotifyPriority, templateFuncs template.FuncMap) sNotifyParams {
  314. p.robots = robots
  315. p.channel = channel
  316. p.priority = priority
  317. p.templateFuncs = templateFuncs
  318. return p
  319. }
  320. // withContactChannelAndPriority 设置 contacts、channel 和 priority(用于 NotifyWithContact)
  321. func (p sNotifyParams) withContactChannelAndPriority(contacts []string, channel npk.TNotifyChannel, priority npk.TNotifyPriority) sNotifyParams {
  322. p.contacts = contacts
  323. p.channel = channel
  324. p.priority = priority
  325. return p
  326. }
  327. // withRecipientId 设置 recipientId
  328. func (p sNotifyParams) withRecipientId(recipientId []string) sNotifyParams {
  329. p.recipientId = recipientId
  330. return p
  331. }
  332. // withRobots 设置 robots
  333. func (p sNotifyParams) withRobots(robots []string) sNotifyParams {
  334. p.robots = robots
  335. return p
  336. }
  337. // withIsGroup 设置 isGroup
  338. func (p sNotifyParams) withIsGroup(isGroup bool) sNotifyParams {
  339. p.isGroup = isGroup
  340. return p
  341. }
  342. // withContacts 设置 contacts
  343. func (p sNotifyParams) withContacts(contacts []string) sNotifyParams {
  344. p.contacts = contacts
  345. return p
  346. }
  347. // withChannel 设置 channel
  348. func (p sNotifyParams) withChannel(channel npk.TNotifyChannel) sNotifyParams {
  349. p.channel = channel
  350. return p
  351. }
  352. // withPriority 设置 priority
  353. func (p sNotifyParams) withPriority(priority npk.TNotifyPriority) sNotifyParams {
  354. p.priority = priority
  355. return p
  356. }
  357. // withCreateReceiver 设置 createReceiver
  358. func (p sNotifyParams) withCreateReceiver(createReceiver bool) sNotifyParams {
  359. p.createReceiver = createReceiver
  360. return p
  361. }
  362. // withTag 设置 tag
  363. func (p sNotifyParams) withTag(tag string) sNotifyParams {
  364. p.tag = tag
  365. return p
  366. }
  367. // withMetadata 设置 metadata
  368. func (p sNotifyParams) withMetadata(metadata map[string]interface{}) sNotifyParams {
  369. p.metadata = metadata
  370. return p
  371. }
  372. // withIgnoreNonexistentReceiver 设置 ignoreNonexistentReceiver
  373. func (p sNotifyParams) withIgnoreNonexistentReceiver(ignoreNonexistentReceiver bool) sNotifyParams {
  374. p.ignoreNonexistentReceiver = ignoreNonexistentReceiver
  375. return p
  376. }
  377. // withTemplateFuncs 设置 templateFuncs
  378. func (p sNotifyParams) withTemplateFuncs(templateFuncs template.FuncMap) sNotifyParams {
  379. p.templateFuncs = templateFuncs
  380. return p
  381. }
  382. func rawNotify(ctx context.Context, p sNotifyParams) {
  383. intelliNotify(ctx, p)
  384. }
  385. func notifyCritical(ctx context.Context, recipientId []string, isGroup bool, event string, data jsonutils.JSONObject) {
  386. notifyCriticalWithTemplateFuncs(ctx, recipientId, isGroup, event, data, nil)
  387. }
  388. func notifyCriticalWithTemplateFuncs(ctx context.Context, recipientId []string, isGroup bool, event string, data jsonutils.JSONObject, templateFuncs template.FuncMap) {
  389. p := newSNotifyParams(event, data).
  390. withRecipientAndPriority(recipientId, isGroup, npk.NotifyPriorityNormal, templateFuncs)
  391. notifyWithChannel(ctx, p,
  392. npk.NotifyByEmail,
  393. npk.NotifyByDingTalk,
  394. npk.NotifyByMobile,
  395. npk.NotifyByWebConsole,
  396. npk.NotifyByFeishu,
  397. npk.NotifyByWorkwx,
  398. )
  399. }
  400. func notifyImportant(ctx context.Context, recipientId []string, isGroup bool, event string, data jsonutils.JSONObject) {
  401. notifyImportantWithTemplateFuncs(ctx, recipientId, isGroup, event, data, nil)
  402. }
  403. func notifyImportantWithTemplateFuncs(ctx context.Context, recipientId []string, isGroup bool, event string, data jsonutils.JSONObject, templateFuncs template.FuncMap) {
  404. p := newSNotifyParams(event, data).
  405. withRecipientAndPriority(recipientId, isGroup, npk.NotifyPriorityNormal, templateFuncs)
  406. notifyWithChannel(ctx, p,
  407. npk.NotifyByEmail,
  408. npk.NotifyByDingTalk,
  409. npk.NotifyByMobile,
  410. npk.NotifyByWebConsole,
  411. npk.NotifyByFeishu,
  412. npk.NotifyByWorkwx,
  413. )
  414. }
  415. func notifyNormal(ctx context.Context, recipientId []string, isGroup bool, event string, data jsonutils.JSONObject) {
  416. notifyNormalWithTemplateFuncs(ctx, recipientId, isGroup, event, data, nil)
  417. }
  418. func notifyNormalWithTemplateFuncs(ctx context.Context, recipientId []string, isGroup bool, event string, data jsonutils.JSONObject, templateFuncs template.FuncMap) {
  419. p := newSNotifyParams(event, data).
  420. withRecipientAndPriority(recipientId, isGroup, npk.NotifyPriorityNormal, templateFuncs)
  421. notifyWithChannel(ctx, p,
  422. npk.NotifyByEmail,
  423. npk.NotifyByDingTalk,
  424. npk.NotifyByFeishu,
  425. npk.NotifyByWorkwx,
  426. npk.NotifyByWebConsole,
  427. )
  428. }
  429. func notifyWithChannel(ctx context.Context, p sNotifyParams, channels ...npk.TNotifyChannel) {
  430. for _, c := range channels {
  431. np := p
  432. np.channel = c
  433. rawNotify(ctx, np)
  434. }
  435. }
  436. const noSuchReceiver = `no such receiver whose uid is '(.*)'`
  437. var noSuchReceiverRegexp = regexp.MustCompile(noSuchReceiver)
  438. type notifyTask struct {
  439. ctx context.Context
  440. msg npk.SNotifyMessage
  441. createReceiver bool
  442. }
  443. func (t *notifyTask) Dump() string {
  444. return fmt.Sprintf("msg: %v createReceiver: %v", t.msg, t.createReceiver)
  445. }
  446. func (t *notifyTask) Run() {
  447. s, err := AdminSessionGenerator(t.ctx, consts.GetRegion())
  448. if err != nil {
  449. log.Errorf("fail to get session: %v", err)
  450. }
  451. for {
  452. err := npk.Notifications.Send(s, t.msg)
  453. if err == nil {
  454. break
  455. }
  456. if !t.createReceiver {
  457. log.Errorf("unable to send notification to contacts %s uid %s gid %s @%s: %s", strings.Join(t.msg.Contacts, ","), strings.Join(t.msg.Uid, ","), strings.Join(t.msg.Gid, ","), t.msg.ContactType, jsonutils.Marshal(err))
  458. break
  459. }
  460. jerr, ok := err.(*httputils.JSONClientError)
  461. if !ok {
  462. log.Errorf("unable to send notification: %v", err)
  463. break
  464. }
  465. if jerr.Code > 500 {
  466. log.Errorf("unable to send notification: %v", err)
  467. break
  468. }
  469. match := noSuchReceiverRegexp.FindStringSubmatch(jerr.Details)
  470. if match == nil || len(match) <= 1 {
  471. log.Errorf("unable to send notification: %v", err)
  472. break
  473. }
  474. receiverId := match[1]
  475. createData := jsonutils.NewDict()
  476. createData.Set("uid", jsonutils.NewString(receiverId))
  477. _, err = npk.NotifyReceiver.Create(s, createData)
  478. if err != nil {
  479. log.Errorf("try to create receiver %q, but failed: %v", receiverId, err)
  480. break
  481. }
  482. log.Infof("create receiver %q successfully", receiverId)
  483. }
  484. }
  485. func intelliNotify(ctx context.Context, p sNotifyParams) {
  486. if isEmptyRecipients(p.recipientId, p.robots) {
  487. return
  488. }
  489. log.Infof("recipientId: %v, robots: %v, contacts: %v, event %s priority %s", p.recipientId, p.robots, p.contacts, p.event, p.priority)
  490. msgs, err := genMsgViaLang(ctx, p)
  491. if err != nil {
  492. log.Errorf("unable send notification: %v", err)
  493. }
  494. for i := range msgs {
  495. t := notifyTask{
  496. ctx: ctx,
  497. createReceiver: p.createReceiver,
  498. msg: msgs[i],
  499. }
  500. notifyClientWorkerMan.Run(&t, nil, nil)
  501. }
  502. // log.Debugf("send notification %s %s", topic, body)
  503. }
  504. func notify(ctx context.Context, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) {
  505. notifyWithTemplateFuncs(ctx, recipientId, isGroup, priority, event, data, nil)
  506. }
  507. func notifyWithTemplateFuncs(ctx context.Context, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject, templateFuncs template.FuncMap) {
  508. switch priority {
  509. case npk.NotifyPriorityCritical:
  510. notifyCriticalWithTemplateFuncs(ctx, recipientId, isGroup, event, data, templateFuncs)
  511. case npk.NotifyPriorityImportant:
  512. notifyImportantWithTemplateFuncs(ctx, recipientId, isGroup, event, data, templateFuncs)
  513. default:
  514. notifyNormalWithTemplateFuncs(ctx, recipientId, isGroup, event, data, templateFuncs)
  515. }
  516. }
  517. func parseIdName(idName string) (string, string) {
  518. pos := strings.Index(idName, "\\")
  519. if pos > 0 {
  520. return idName[:pos], idName[pos+1:]
  521. } else {
  522. return "", idName
  523. }
  524. }
  525. var (
  526. domainCache = make(map[string]string)
  527. )
  528. func getIdentityId(s *mcclient.ClientSession, idName string, manager modulebase.Manager) (string, error) {
  529. domain, idName := parseIdName(idName)
  530. query := jsonutils.NewDict()
  531. if len(domain) > 0 {
  532. domainId, ok := domainCache[domain]
  533. if !ok {
  534. var err error
  535. domainId, err = identity.Domains.GetId(s, domain, nil)
  536. if err != nil {
  537. log.Errorf("fail to find domainId for domain %s: %s", domain, err)
  538. return "", err
  539. }
  540. domainCache[domain] = domainId
  541. }
  542. query.Add(jsonutils.NewString(domainId), "domain_id")
  543. }
  544. return manager.GetId(s, idName, query)
  545. }