dnsrecords.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  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 models
  15. import (
  16. "context"
  17. "database/sql"
  18. "fmt"
  19. "regexp"
  20. "sort"
  21. "strings"
  22. "yunion.io/x/cloudmux/pkg/cloudprovider"
  23. "yunion.io/x/jsonutils"
  24. "yunion.io/x/log"
  25. "yunion.io/x/pkg/errors"
  26. "yunion.io/x/pkg/tristate"
  27. "yunion.io/x/pkg/util/compare"
  28. "yunion.io/x/pkg/util/rbacscope"
  29. "yunion.io/x/pkg/util/regutils"
  30. "yunion.io/x/sqlchemy"
  31. "yunion.io/x/onecloud/pkg/apis"
  32. api "yunion.io/x/onecloud/pkg/apis/compute"
  33. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  34. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  35. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  36. "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
  37. "yunion.io/x/onecloud/pkg/cloudcommon/validators"
  38. "yunion.io/x/onecloud/pkg/httperrors"
  39. "yunion.io/x/onecloud/pkg/mcclient"
  40. "yunion.io/x/onecloud/pkg/util/stringutils2"
  41. )
  42. // +onecloud:swagger-gen-model-singular=dnsrecord
  43. // +onecloud:swagger-gen-model-plural=dnsrecords
  44. type SDnsRecordManager struct {
  45. db.SEnabledStatusStandaloneResourceBaseManager
  46. db.SExternalizedResourceBaseManager
  47. SDnsZoneResourceBaseManager
  48. }
  49. var DnsRecordManager *SDnsRecordManager
  50. func init() {
  51. DnsRecordManager = &SDnsRecordManager{
  52. SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager(
  53. SDnsRecord{},
  54. "dns_records_tbl",
  55. "dnsrecord",
  56. "dnsrecords",
  57. ),
  58. }
  59. DnsRecordManager.SetVirtualObject(DnsRecordManager)
  60. }
  61. type SDnsRecord struct {
  62. db.SEnabledStatusStandaloneResourceBase
  63. db.SExternalizedResourceBase
  64. SDnsZoneResourceBase
  65. DnsType string `width:"36" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"`
  66. DnsValue string `width:"256" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"`
  67. TTL int64 `nullable:"false" list:"user" update:"user" create:"required" json:"ttl"`
  68. MxPriority int64 `nullable:"false" list:"user" update:"user" create:"optional"`
  69. // cloudflare 特有
  70. Proxied tristate.TriState `default:"false" list:"user" create:"optional"`
  71. // 解析线路类型
  72. PolicyType string `width:"36" charset:"ascii" nullable:"false" list:"user" update:"user" create:"optional"`
  73. // 解析线路
  74. PolicyValue string `width:"256" charset:"ascii" nullable:"false" list:"user" update:"user" create:"optional"`
  75. // 目前存储阿里云GTM设置地址及AWS TrafficPolicy端点地址, 仅支持同步
  76. ExtraAddresses []string `width:"512" charset:"utf8" nullable:"true" list:"user"`
  77. }
  78. func (manager *SDnsRecordManager) EnableGenerateName() bool {
  79. return false
  80. }
  81. // 创建
  82. func (manager *SDnsRecordManager) ValidateCreateData(
  83. ctx context.Context,
  84. userCred mcclient.TokenCredential,
  85. ownerId mcclient.IIdentityProvider,
  86. query jsonutils.JSONObject,
  87. input *api.DnsRecordCreateInput,
  88. ) (*api.DnsRecordCreateInput, error) {
  89. var err error
  90. input.EnabledStatusStandaloneResourceCreateInput, err = manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusStandaloneResourceCreateInput)
  91. if err != nil {
  92. return nil, err
  93. }
  94. input.Name = strings.ToLower(input.Name)
  95. rrRegexp := regexp.MustCompile(`^(([a-zA-Z0-9_][a-zA-Z0-9_-]{0,61}[a-zA-Z0-9_])|[a-zA-Z0-9_]|\*{1}){1}(\.(([a-zA-Z0-9_][a-zA-Z0-9_-]{0,61}[a-zA-Z0-9_])|[a-zA-Z0-9_]))*$|^@{1}$`)
  96. if !rrRegexp.MatchString(input.Name) {
  97. return nil, httperrors.NewInputParameterError("invalid record name %s", input.Name)
  98. }
  99. _, err = validators.ValidateModel(ctx, userCred, DnsZoneManager, &input.DnsZoneId)
  100. if err != nil {
  101. return nil, err
  102. }
  103. record := api.SDnsRecord{}
  104. record.DnsZoneId = input.DnsZoneId
  105. record.DnsType = input.DnsType
  106. record.DnsValue = input.DnsValue
  107. record.TTL = input.TTL
  108. record.MxPriority = input.MxPriority
  109. err = record.ValidateDnsrecordValue()
  110. if err != nil {
  111. return input, err
  112. }
  113. // 处理重复的记录
  114. // CNAME dnsName不能和其他类型record相同
  115. // 同dnsName 同dnsType重复检查
  116. // 检查dnsrecord 是否通过policy重复
  117. // simple类型不能重复,不能和其他policy重复
  118. // 不同类型policy不能重复
  119. // 同类型policy的dnsrecord重复时,需要通过policyvalue区别
  120. // validate name type
  121. q := DnsRecordManager.Query().Equals("dns_zone_id", input.DnsZoneId).Equals("name", input.Name)
  122. recordTypeQuery := q
  123. switch input.DnsType {
  124. case "CNAME":
  125. recordTypeQuery = recordTypeQuery.NotEquals("dns_type", "CNAME")
  126. default:
  127. recordTypeQuery = recordTypeQuery.Equals("dns_type", "CNAME")
  128. }
  129. cnt, err := recordTypeQuery.CountWithError()
  130. if err != nil {
  131. return input, httperrors.NewGeneralError(err)
  132. }
  133. if cnt > 0 {
  134. return input, httperrors.NewNotSupportedError("duplicated with CNAME dnsrecord name not support")
  135. }
  136. input.Status = api.DNS_RECORDSET_STATUS_CREATING
  137. return input, nil
  138. }
  139. func (self *SDnsRecord) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  140. self.SEnabledStatusStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
  141. self.StartCreateTask(ctx, userCred, "")
  142. }
  143. func (self *SDnsRecord) StartCreateTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  144. params := jsonutils.NewDict()
  145. task, err := taskman.TaskManager.NewTask(ctx, "DnsRecordCreateTask", self, userCred, params, parentTaskId, "", nil)
  146. if err != nil {
  147. return errors.Wrap(err, "NewTask")
  148. }
  149. self.SetStatus(ctx, userCred, api.DNS_RECORDSET_STATUS_CREATING, "")
  150. return task.ScheduleRun(nil)
  151. }
  152. // DNS记录列表
  153. func (manager *SDnsRecordManager) ListItemFilter(
  154. ctx context.Context,
  155. q *sqlchemy.SQuery,
  156. userCred mcclient.TokenCredential,
  157. query api.DnsRecordListInput,
  158. ) (*sqlchemy.SQuery, error) {
  159. var err error
  160. q, err = manager.SEnabledStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusStandaloneResourceListInput)
  161. if err != nil {
  162. return nil, err
  163. }
  164. q, err = manager.SDnsZoneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.DnsZoneFilterListBase)
  165. if err != nil {
  166. return nil, err
  167. }
  168. return q, nil
  169. }
  170. // 解析详情
  171. func (manager *SDnsRecordManager) FetchCustomizeColumns(
  172. ctx context.Context,
  173. userCred mcclient.TokenCredential,
  174. query jsonutils.JSONObject,
  175. objs []interface{},
  176. fields stringutils2.SSortedStrings,
  177. isList bool,
  178. ) []api.DnsRecordDetails {
  179. rows := make([]api.DnsRecordDetails, len(objs))
  180. enRows := manager.SEnabledStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  181. zoneIds := make([]string, len(objs))
  182. for i := range rows {
  183. rows[i] = api.DnsRecordDetails{
  184. EnabledStatusStandaloneResourceDetails: enRows[i],
  185. }
  186. record := objs[i].(*SDnsRecord)
  187. zoneIds[i] = record.DnsZoneId
  188. }
  189. zoneNames, err := db.FetchIdNameMap2(DnsZoneManager, zoneIds)
  190. if err != nil {
  191. return rows
  192. }
  193. for i := range rows {
  194. rows[i].DnsZone, _ = zoneNames[zoneIds[i]]
  195. }
  196. return rows
  197. }
  198. func (self *SDnsRecord) ToZoneLine() string {
  199. result := self.Name + "\t" + fmt.Sprint(self.TTL) + "\tIN\t" + self.DnsType + "\t"
  200. if self.MxPriority != 0 {
  201. result += fmt.Sprint(self.MxPriority) + "\t"
  202. }
  203. result += self.DnsValue
  204. if self.DnsType == "CNAME" || self.DnsType == "MX" || self.DnsType == "SRV" {
  205. result += "."
  206. }
  207. return result
  208. }
  209. func (self *SDnsRecord) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
  210. return nil
  211. }
  212. func (self *SDnsRecord) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  213. return self.SEnabledStatusStandaloneResourceBase.Delete(ctx, userCred)
  214. }
  215. type sRecordUniqValues struct {
  216. DnsZoneId string
  217. DnsType string
  218. DnsName string
  219. DnsValue string
  220. PolicyType string
  221. PolicyValue string
  222. }
  223. func (self *SDnsRecord) GetUniqValues() jsonutils.JSONObject {
  224. return jsonutils.Marshal(sRecordUniqValues{
  225. DnsZoneId: self.DnsZoneId,
  226. DnsName: self.Name,
  227. DnsType: self.DnsType,
  228. DnsValue: self.DnsValue,
  229. PolicyType: self.PolicyType,
  230. PolicyValue: self.PolicyValue,
  231. })
  232. }
  233. func (manager *SDnsRecordManager) FetchUniqValues(ctx context.Context, data jsonutils.JSONObject) jsonutils.JSONObject {
  234. values := &sRecordUniqValues{}
  235. data.Unmarshal(values)
  236. return jsonutils.Marshal(values)
  237. }
  238. func (manager *SDnsRecordManager) FilterByUniqValues(q *sqlchemy.SQuery, values jsonutils.JSONObject) *sqlchemy.SQuery {
  239. uniq := &sRecordUniqValues{}
  240. values.Unmarshal(uniq)
  241. if len(uniq.DnsZoneId) > 0 {
  242. q = q.Equals("dns_zone_id", uniq.DnsZoneId)
  243. }
  244. if len(uniq.DnsName) > 0 {
  245. q = q.Equals("name", uniq.DnsName)
  246. }
  247. if len(uniq.DnsType) > 0 {
  248. q = q.Equals("dns_type", uniq.DnsType)
  249. }
  250. if len(uniq.DnsValue) > 0 {
  251. q = q.Equals("dns_value", uniq.DnsValue)
  252. }
  253. if len(uniq.PolicyType) > 0 {
  254. q = q.Equals("policy_type", uniq.PolicyType)
  255. }
  256. if len(uniq.PolicyValue) > 0 {
  257. q = q.Equals("policy_value", uniq.PolicyValue)
  258. }
  259. return q
  260. }
  261. func (manager *SDnsRecordManager) FetchOwnerId(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) {
  262. dnsZoneId, _ := data.GetString("dns_zone_id")
  263. if len(dnsZoneId) > 0 {
  264. dnsZone, err := db.FetchById(DnsZoneManager, dnsZoneId)
  265. if err != nil {
  266. return nil, errors.Wrapf(err, "db.FetchById(DnsZoneManager, %s)", dnsZoneId)
  267. }
  268. return dnsZone.(*SDnsZone).GetOwnerId(), nil
  269. }
  270. return db.FetchDomainInfo(ctx, data)
  271. }
  272. func (manager *SDnsRecordManager) FilterByOwner(ctx context.Context, q *sqlchemy.SQuery, man db.FilterByOwnerProvider, userCred mcclient.TokenCredential, owner mcclient.IIdentityProvider, scope rbacscope.TRbacScope) *sqlchemy.SQuery {
  273. sq := DnsZoneManager.Query("id")
  274. sq = db.SharableManagerFilterByOwner(ctx, DnsZoneManager, sq, userCred, owner, scope)
  275. return q.In("dns_zone_id", sq.SubQuery())
  276. }
  277. func (manager *SDnsRecordManager) ResourceScope() rbacscope.TRbacScope {
  278. return rbacscope.ScopeDomain
  279. }
  280. func (self *SDnsRecord) IsSharable(reqUsrId mcclient.IIdentityProvider) bool {
  281. dnsZone, err := self.GetDnsZone()
  282. if err != nil {
  283. return false
  284. }
  285. return dnsZone.IsSharable(reqUsrId)
  286. }
  287. func (self *SDnsRecord) GetOwnerId() mcclient.IIdentityProvider {
  288. dnsZone, err := self.GetDnsZone()
  289. if err != nil {
  290. return nil
  291. }
  292. return dnsZone.GetOwnerId()
  293. }
  294. func (self *SDnsRecord) syncRemove(ctx context.Context, userCred mcclient.TokenCredential) error {
  295. lockman.LockObject(ctx, self)
  296. defer lockman.ReleaseObject(ctx, self)
  297. return self.RealDelete(ctx, userCred)
  298. }
  299. func (self *SDnsRecord) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  300. return self.StartDeleteTask(ctx, userCred, "")
  301. }
  302. func (self *SDnsRecord) StartDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  303. params := jsonutils.NewDict()
  304. task, err := taskman.TaskManager.NewTask(ctx, "DnsRecordDeleteTask", self, userCred, params, parentTaskId, "", nil)
  305. if err != nil {
  306. return errors.Wrap(err, "NewTask")
  307. }
  308. self.SetStatus(ctx, userCred, apis.STATUS_DELETING, "")
  309. return task.ScheduleRun(nil)
  310. }
  311. // 更新
  312. func (self *SDnsRecord) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.DnsRecordUpdateInput) (*api.DnsRecordUpdateInput, error) {
  313. var err error
  314. input.EnabledStatusStandaloneResourceBaseUpdateInput, err = self.SEnabledStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.EnabledStatusStandaloneResourceBaseUpdateInput)
  315. if err != nil {
  316. return input, err
  317. }
  318. dnsZone, err := self.GetDnsZone()
  319. if err != nil {
  320. return input, httperrors.NewGeneralError(errors.Wrapf(err, "GetDnsZone"))
  321. }
  322. record := api.SDnsRecord{
  323. DnsType: self.DnsType,
  324. DnsValue: self.DnsValue,
  325. }
  326. if len(input.DnsType) == 0 {
  327. record.DnsType = input.DnsType
  328. }
  329. if len(input.DnsValue) == 0 {
  330. record.DnsValue = input.DnsValue
  331. }
  332. err = record.ValidateDnsrecordValue()
  333. if err != nil {
  334. return input, err
  335. }
  336. // 处理重复的记录
  337. // CNAME dnsName不能和其他类型record相同
  338. // 同dnsName 同dnsType重复检查
  339. // 检查dnsrecord 是否通过policy重复
  340. // simple类型不能重复,不能和其他policy重复
  341. // 不同类型policy不能重复
  342. // 同类型policy的dnsrecord重复时,需要通过policyvalue区别
  343. // validate name type
  344. q := DnsRecordManager.Query().Equals("dns_zone_id", dnsZone.Id).NotEquals("id", self.Id).Equals("name", input.Name)
  345. recordTypeQuery := q
  346. switch input.DnsType {
  347. case "CNAME":
  348. recordTypeQuery = recordTypeQuery.NotEquals("dns_type", "CNAME")
  349. default:
  350. recordTypeQuery = recordTypeQuery.Equals("dns_type", "CNAME")
  351. }
  352. cnt, err := recordTypeQuery.CountWithError()
  353. if err != nil {
  354. return input, httperrors.NewGeneralError(err)
  355. }
  356. if cnt > 0 {
  357. return input, httperrors.NewNotSupportedError("duplicated with CNAME dnsrecord name not support")
  358. }
  359. return input, nil
  360. }
  361. func (self *SDnsRecord) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  362. self.SEnabledStatusStandaloneResourceBase.PostUpdate(ctx, userCred, query, data)
  363. self.StartUpdateTask(ctx, userCred, "")
  364. }
  365. func (self *SDnsRecord) StartUpdateTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  366. params := jsonutils.NewDict()
  367. task, err := taskman.TaskManager.NewTask(ctx, "DnsRecordUpdateTask", self, userCred, params, parentTaskId, "", nil)
  368. if err != nil {
  369. return errors.Wrap(err, "NewTask")
  370. }
  371. self.SetStatus(ctx, userCred, apis.STATUS_SYNC_STATUS, "")
  372. return task.ScheduleRun(nil)
  373. }
  374. func (self *SDnsRecord) GetDnsZone() (*SDnsZone, error) {
  375. dnsZone, err := DnsZoneManager.FetchById(self.DnsZoneId)
  376. if err != nil {
  377. return nil, errors.Wrapf(err, "DnsZoneManager.FetchById(%s)", self.DnsZoneId)
  378. }
  379. return dnsZone.(*SDnsZone), nil
  380. }
  381. func (self *SDnsZone) SyncRecords(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudDnsZone, xor bool) compare.SyncResult {
  382. lockman.LockRawObject(ctx, self.Keyword(), self.Id)
  383. defer lockman.ReleaseRawObject(ctx, self.Keyword(), self.Id)
  384. result := compare.SyncResult{}
  385. records, err := ext.GetIDnsRecords()
  386. if err != nil {
  387. result.Error(errors.Wrapf(err, "GetIDnsRecords"))
  388. return result
  389. }
  390. dbRecords, err := self.GetDnsRecords()
  391. if err != nil {
  392. result.Error(err)
  393. return result
  394. }
  395. removed := make([]SDnsRecord, 0)
  396. commondb := make([]SDnsRecord, 0)
  397. commonext := make([]cloudprovider.ICloudDnsRecord, 0)
  398. added := make([]cloudprovider.ICloudDnsRecord, 0)
  399. err = compare.CompareSets(dbRecords, records, &removed, &commondb, &commonext, &added)
  400. if err != nil {
  401. result.Error(err)
  402. return result
  403. }
  404. for i := 0; i < len(removed); i += 1 {
  405. err = removed[i].syncRemove(ctx, userCred)
  406. if err != nil {
  407. result.DeleteError(err)
  408. continue
  409. }
  410. result.Delete()
  411. }
  412. if !xor {
  413. for i := 0; i < len(commondb); i += 1 {
  414. err = commondb[i].syncWithDnsRecord(ctx, userCred, commonext[i])
  415. if err != nil {
  416. result.UpdateError(err)
  417. continue
  418. }
  419. result.Update()
  420. }
  421. }
  422. for i := 0; i < len(added); i += 1 {
  423. err := self.newFromCloudDnsRecord(ctx, userCred, added[i])
  424. if err != nil {
  425. result.AddError(err)
  426. continue
  427. }
  428. result.Add()
  429. }
  430. return result
  431. }
  432. func (self *SDnsRecord) syncWithDnsRecord(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudDnsRecord) error {
  433. diff, err := db.Update(self, func() error {
  434. self.Name = ext.GetDnsName()
  435. self.Enabled = tristate.NewFromBool(ext.GetEnabled())
  436. self.Proxied = tristate.NewFromBool(ext.IsProxied())
  437. self.Status = ext.GetStatus()
  438. self.TTL = ext.GetTTL()
  439. self.MxPriority = ext.GetMxPriority()
  440. self.DnsType = string(ext.GetDnsType())
  441. self.DnsValue = ext.GetDnsValue()
  442. self.PolicyType = string(ext.GetPolicyType())
  443. self.PolicyValue = string(ext.GetPolicyValue())
  444. extraAddresses, err := ext.GetExtraAddresses()
  445. if err != nil {
  446. log.Errorf("GetExtraAddresses for record %s error: %v", self.Name, err)
  447. return nil
  448. }
  449. self.ExtraAddresses = extraAddresses
  450. return nil
  451. })
  452. if err != nil {
  453. return errors.Wrapf(err, "update")
  454. }
  455. if len(diff) > 0 {
  456. notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
  457. Obj: self,
  458. Action: notifyclient.ActionSyncUpdate,
  459. })
  460. }
  461. return nil
  462. }
  463. func (self *SDnsZone) newFromCloudDnsRecord(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudDnsRecord) error {
  464. record := &SDnsRecord{}
  465. record.SetModelManager(DnsRecordManager, record)
  466. record.DnsZoneId = self.Id
  467. record.Name = ext.GetDnsName()
  468. record.Status = ext.GetStatus()
  469. record.Enabled = tristate.NewFromBool(ext.GetEnabled())
  470. record.Proxied = tristate.NewFromBool(ext.IsProxied())
  471. record.TTL = ext.GetTTL()
  472. record.MxPriority = ext.GetMxPriority()
  473. record.DnsType = string(ext.GetDnsType())
  474. record.DnsValue = ext.GetDnsValue()
  475. record.ExternalId = ext.GetGlobalId()
  476. record.PolicyType = string(ext.GetPolicyType())
  477. record.PolicyValue = string(ext.GetPolicyValue())
  478. record.ExtraAddresses, _ = ext.GetExtraAddresses()
  479. err := DnsRecordManager.TableSpec().Insert(ctx, record)
  480. if err != nil {
  481. return errors.Wrapf(err, "Insert")
  482. }
  483. notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
  484. Obj: record,
  485. Action: notifyclient.ActionSyncCreate,
  486. })
  487. return nil
  488. }
  489. type sDnsResolveResults []api.SDnsResolveResult
  490. func (a sDnsResolveResults) Len() int { return len(a) }
  491. func (a sDnsResolveResults) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  492. func (a sDnsResolveResults) Less(i, j int) bool {
  493. partsI := strings.Split(a[i].DnsName, ".")
  494. partsJ := strings.Split(a[j].DnsName, ".")
  495. if len(partsI) > len(partsJ) {
  496. return true
  497. } else if len(partsI) < len(partsJ) {
  498. return false
  499. }
  500. if len(partsI) > 0 && partsI[0] != partsJ[0] {
  501. if partsI[0] == "*" {
  502. return false
  503. } else if partsJ[0] == "*" {
  504. return true
  505. }
  506. }
  507. if a[i].DnsName < a[j].DnsName {
  508. return true
  509. } else if a[i].DnsName > a[j].DnsName {
  510. return false
  511. }
  512. return false
  513. }
  514. func (man *SDnsRecordManager) QueryPtr(projectId, ip string) ([]api.SDnsResolveResult, error) {
  515. zonesQ := DnsZoneManager.Query().IsNullOrEmpty("manager_id").IsTrue("enabled")
  516. if len(projectId) == 0 {
  517. zonesQ = zonesQ.IsTrue("is_public")
  518. } else {
  519. zonesQ = zonesQ.Filter(sqlchemy.OR(
  520. sqlchemy.IsTrue(zonesQ.Field("is_public")),
  521. sqlchemy.Equals(zonesQ.Field("tenant_id"), projectId),
  522. ))
  523. }
  524. zones := zonesQ.SubQuery()
  525. recQ := DnsRecordManager.Query().IsTrue("enabled")
  526. if regutils.MatchIP6Addr(ip) {
  527. recQ = recQ.Equals("dns_type", "AAAA")
  528. } else {
  529. recQ = recQ.Equals("dns_type", "A")
  530. }
  531. recSQ := recQ.SubQuery()
  532. rec := recSQ.Query(
  533. recSQ.Field("dns_value"),
  534. recSQ.Field("ttl"),
  535. sqlchemy.CONCAT("dns_name", recSQ.Field("name"), sqlchemy.NewStringField("."), zones.Field("name")),
  536. ).Join(zones, sqlchemy.Equals(recSQ.Field("dns_zone_id"), zones.Field("id")))
  537. sq := rec.SubQuery()
  538. q := sq.Query().Equals("dns_value", ip)
  539. results := make([]api.SDnsResolveResult, 0)
  540. err := q.All(&results)
  541. if err != nil && errors.Cause(err) != sql.ErrNoRows {
  542. return nil, errors.Wrap(err, "FetchModelObjects")
  543. }
  544. return results, nil
  545. }
  546. func (man *SDnsRecordManager) QueryDns(projectId, name, kind string) ([]api.SDnsResolveResult, error) {
  547. name = strings.TrimSuffix(name, ".")
  548. zonesQ := DnsZoneManager.Query().IsNullOrEmpty("manager_id").IsTrue("enabled")
  549. if len(projectId) == 0 {
  550. zonesQ = zonesQ.IsTrue("is_public")
  551. } else {
  552. zonesQ = zonesQ.Filter(sqlchemy.OR(
  553. sqlchemy.IsTrue(zonesQ.Field("is_public")),
  554. sqlchemy.Equals(zonesQ.Field("tenant_id"), projectId),
  555. ))
  556. }
  557. zones := zonesQ.SubQuery()
  558. recQ := DnsRecordManager.Query().IsTrue("enabled")
  559. if len(kind) > 0 {
  560. recQ = recQ.Equals("dns_type", kind)
  561. }
  562. recSQ := recQ.SubQuery()
  563. rec := recSQ.Query(
  564. recSQ.Field("dns_value"),
  565. recSQ.Field("ttl"),
  566. sqlchemy.CONCAT("dns_name", recSQ.Field("name"), sqlchemy.NewStringField("."), zones.Field("name")),
  567. ).Join(zones, sqlchemy.Equals(recSQ.Field("dns_zone_id"), zones.Field("id")))
  568. sq := rec.SubQuery()
  569. filters := sqlchemy.OR(
  570. // example match
  571. sqlchemy.Equals(sq.Field("dns_name"), name),
  572. // support *.example.com resolve
  573. sqlchemy.Equals(sq.Field("dns_name"), "*."+name),
  574. )
  575. strs := strings.Split(name, ".")
  576. if len(strs) > 2 {
  577. filters = sqlchemy.OR(
  578. // example match
  579. sqlchemy.Equals(sq.Field("dns_name"), name),
  580. // root match, support resolve example.com to *.example.com
  581. sqlchemy.Equals(sq.Field("dns_name"), "*."+name),
  582. // support resolve office.example.com to *.example.com
  583. sqlchemy.Equals(sq.Field("dns_name"), "*."+strings.Join(strs[1:], ".")),
  584. // support resolve saml.office.example.com to office.example.com
  585. sqlchemy.Equals(sq.Field("dns_name"), strings.Join(strs[1:], ".")),
  586. )
  587. }
  588. q := sq.Query().Filter(filters)
  589. results := make([]api.SDnsResolveResult, 0)
  590. err := q.All(&results)
  591. if err != nil && errors.Cause(err) != sql.ErrNoRows {
  592. return nil, errors.Wrap(err, "FetchModelObjects")
  593. }
  594. if len(results) == 0 {
  595. return results, nil
  596. }
  597. sort.Sort(sDnsResolveResults(results))
  598. ret := make([]api.SDnsResolveResult, 0, len(results))
  599. for i := range results {
  600. if i == 0 || results[i].DnsName == ret[0].DnsName {
  601. ret = append(ret, results[i])
  602. } else {
  603. break
  604. }
  605. }
  606. return ret, nil
  607. }
  608. func (self *SDnsRecord) IsCNAME() bool {
  609. return strings.ToUpper(self.DnsType) == "CNAME"
  610. }
  611. func (self *SDnsRecord) GetCNAME() string {
  612. return self.DnsValue
  613. }
  614. // 启用
  615. func (self *SDnsRecord) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DnsRecordEnableInput) (jsonutils.JSONObject, error) {
  616. _, err := self.SEnabledStatusStandaloneResourceBase.PerformEnable(ctx, userCred, query, input.PerformEnableInput)
  617. if err != nil {
  618. return nil, err
  619. }
  620. if err != nil {
  621. return nil, httperrors.NewGeneralError(errors.Wrapf(err, "GetDnsZone"))
  622. }
  623. return nil, self.StartSetEnabledTask(ctx, userCred, "")
  624. }
  625. func (self *SDnsRecord) StartSetEnabledTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  626. params := jsonutils.NewDict()
  627. task, err := taskman.TaskManager.NewTask(ctx, "DnsRecordSetEnabledTask", self, userCred, params, parentTaskId, "", nil)
  628. if err != nil {
  629. return errors.Wrap(err, "NewTask")
  630. }
  631. self.SetStatus(ctx, userCred, apis.STATUS_SYNC_STATUS, "")
  632. return task.ScheduleRun(nil)
  633. }
  634. // 禁用
  635. func (self *SDnsRecord) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DnsRecordDisableInput) (jsonutils.JSONObject, error) {
  636. _, err := self.SEnabledStatusStandaloneResourceBase.PerformDisable(ctx, userCred, query, input.PerformDisableInput)
  637. if err != nil {
  638. return nil, err
  639. }
  640. if err != nil {
  641. return nil, httperrors.NewGeneralError(errors.Wrapf(err, "GetDnsZone"))
  642. }
  643. return nil, self.StartSetEnabledTask(ctx, userCred, "")
  644. }