dnszone.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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 aws
  15. import (
  16. "strings"
  17. "time"
  18. "yunion.io/x/jsonutils"
  19. "yunion.io/x/log"
  20. "yunion.io/x/pkg/errors"
  21. "yunion.io/x/pkg/util/stringutils"
  22. api "yunion.io/x/cloudmux/pkg/apis/compute"
  23. "yunion.io/x/cloudmux/pkg/cloudprovider"
  24. "yunion.io/x/cloudmux/pkg/multicloud"
  25. )
  26. type DnsZoneConfig struct {
  27. Comment string `xml:"Comment"`
  28. PrivateZone bool `xml:"PrivateZone"`
  29. }
  30. type AssociatedVPC struct {
  31. VPCId string `xml:"VPCId"`
  32. VPCRegion string `xml:"VPCRegion"`
  33. }
  34. type SDnsZone struct {
  35. multicloud.SDnsZoneBase
  36. AwsTags
  37. client *SAwsClient
  38. Id string `xml:"Id"`
  39. Name string `xml:"Name"`
  40. CallerReference string `xml:"CallerReference"`
  41. Config DnsZoneConfig `xml:"Config"`
  42. ResourceRecordSetCount int64 `xml:"ResourceRecordSetCount"`
  43. locations []GeoLocationDetails
  44. }
  45. type SDnsZoneDetails struct {
  46. HostedZone SDnsZone `xml:"HostedZone"`
  47. VPC []struct {
  48. VPCRegion string `xml:"VPCRegion"`
  49. VPCId string `xml:"VPCId"`
  50. } `xml:"VPCs>VPC"`
  51. }
  52. func (self *SDnsZone) GetId() string {
  53. return self.Id
  54. }
  55. func (self *SDnsZone) GetName() string {
  56. return strings.TrimSuffix(self.Name, ".")
  57. }
  58. func (self *SDnsZone) GetGlobalId() string {
  59. return self.Id
  60. }
  61. func (self *SDnsZone) GetStatus() string {
  62. return api.DNS_ZONE_STATUS_AVAILABLE
  63. }
  64. func (self *SDnsZone) Refresh() error {
  65. zone, err := self.client.GetDnsZone(self.Id)
  66. if err != nil {
  67. return errors.Wrapf(err, "GetDnsZone(%s)", self.Id)
  68. }
  69. return jsonutils.Update(self, zone.HostedZone)
  70. }
  71. type GeoLocationDetails struct {
  72. ContinentCode string `xml:"ContinentCode"`
  73. ContinentName string `xml:"ContinentName"`
  74. CountryCode string `xml:"CountryCode"`
  75. CountryName string `xml:"CountryName"`
  76. SubdivisionCode string `xml:"SubdivisionCode"`
  77. SubdivisionName string `xml:"SubdivisionName"`
  78. }
  79. func (self GeoLocationDetails) GetPolicyValue() cloudprovider.TDnsPolicyValue {
  80. if len(self.SubdivisionName) > 0 {
  81. return cloudprovider.TDnsPolicyValue(self.SubdivisionName)
  82. }
  83. if len(self.ContinentName) > 0 {
  84. return cloudprovider.TDnsPolicyValue(self.ContinentName)
  85. }
  86. return cloudprovider.TDnsPolicyValue(self.CountryName)
  87. }
  88. func (self GeoLocationDetails) equals(lo GeoLocationDetails) bool {
  89. return self.CountryCode == lo.CountryCode && self.ContinentCode == self.ContinentCode && self.SubdivisionCode == self.SubdivisionCode
  90. }
  91. func (client *SAwsClient) ListGeoLocations() ([]GeoLocationDetails, error) {
  92. ret := []GeoLocationDetails{}
  93. params := map[string]string{
  94. "maxitems": "100",
  95. }
  96. for {
  97. part := struct {
  98. Locations []GeoLocationDetails `xml:"GeoLocationDetailsList>GeoLocationDetails"`
  99. NextCountryCode string `xml:"NextCountryCode"`
  100. NextContinentCode string `xml:"NextContinentCode"`
  101. NextSubdivisionCode string `xml:"NextSubdivisionCode"`
  102. }{}
  103. err := client.dnsRequest("ListGeoLocations", params, &part)
  104. if err != nil {
  105. return nil, err
  106. }
  107. ret = append(ret, part.Locations...)
  108. if len(part.NextCountryCode) == 0 && len(part.NextContinentCode) == 0 && len(part.NextSubdivisionCode) == 0 {
  109. break
  110. }
  111. for k, v := range map[string]string{
  112. "startcountrycode": part.NextCountryCode,
  113. "startcontinentcode": part.NextContinentCode,
  114. "startsubdivisioncode": part.NextSubdivisionCode,
  115. } {
  116. if len(v) > 0 {
  117. params[k] = v
  118. }
  119. }
  120. }
  121. return ret, nil
  122. }
  123. func (client *SAwsClient) CreateDnsZone(opts *cloudprovider.SDnsZoneCreateOptions) (*SDnsZone, error) {
  124. params := map[string]string{
  125. "CallerReference": time.Now().Format(time.RFC3339),
  126. "HostedZoneConfig.PrivateZone": "true",
  127. "Name": opts.Name,
  128. }
  129. if opts.ZoneType == cloudprovider.PrivateZone {
  130. params["HostedZoneConfig.PrivateZone"] = "true"
  131. }
  132. if len(opts.Desc) > 0 {
  133. params["HostedZoneConfig.Comment"] = opts.Desc
  134. }
  135. for i, vpc := range opts.Vpcs {
  136. if i == 0 {
  137. params["VPC.VPCRegion"] = vpc.RegionId
  138. params["VPC.VPCId"] = vpc.Id
  139. }
  140. }
  141. ret := SDnsZoneDetails{}
  142. ret.HostedZone.client = client
  143. err := client.dnsRequest("CreateHostedZone", params, &ret)
  144. if err != nil {
  145. return nil, err
  146. }
  147. for i, vpc := range opts.Vpcs {
  148. if i != 0 {
  149. err = ret.HostedZone.AddVpc(&vpc)
  150. if err != nil {
  151. log.Errorf("add vpc %s(%s) error: %v", vpc.Id, vpc.RegionId, err)
  152. }
  153. }
  154. }
  155. return &ret.HostedZone, nil
  156. }
  157. func (client *SAwsClient) DeleteDnsZone(Id string) error {
  158. params := map[string]string{
  159. "Id": Id,
  160. }
  161. return client.dnsRequest("DeleteHostedZone", params, nil)
  162. }
  163. func (client *SAwsClient) CreateICloudDnsZone(opts *cloudprovider.SDnsZoneCreateOptions) (cloudprovider.ICloudDnsZone, error) {
  164. return client.CreateDnsZone(opts)
  165. }
  166. func (client *SAwsClient) GetDnsZones() ([]SDnsZone, error) {
  167. params := map[string]string{
  168. "maxitems": "1",
  169. }
  170. ret := []SDnsZone{}
  171. for {
  172. part := struct {
  173. DnsZones []SDnsZone `xml:"HostedZones>HostedZone"`
  174. NextMarker string `xml:"NextMarker"`
  175. }{}
  176. err := client.dnsRequest("ListHostedZones", params, &part)
  177. if err != nil {
  178. return nil, err
  179. }
  180. ret = append(ret, part.DnsZones...)
  181. if len(part.NextMarker) == 0 {
  182. break
  183. }
  184. params["marker"] = part.NextMarker
  185. }
  186. return ret, nil
  187. }
  188. func (client *SAwsClient) GetICloudDnsZones() ([]cloudprovider.ICloudDnsZone, error) {
  189. zones, err := client.GetDnsZones()
  190. if err != nil {
  191. return nil, errors.Wrap(err, "GetDnsZones()")
  192. }
  193. result := []cloudprovider.ICloudDnsZone{}
  194. for i := 0; i < len(zones); i++ {
  195. zones[i].client = client
  196. result = append(result, &zones[i])
  197. }
  198. return result, nil
  199. }
  200. func (client *SAwsClient) GetDnsZone(id string) (*SDnsZoneDetails, error) {
  201. params := map[string]string{"Id": id}
  202. ret := SDnsZoneDetails{}
  203. err := client.dnsRequest("GetHostedZone", params, &ret)
  204. if err != nil {
  205. return nil, err
  206. }
  207. ret.HostedZone.client = client
  208. return &ret, nil
  209. }
  210. func (client *SAwsClient) AssociateVPCWithHostedZone(vpcId string, regionId string, zoneId string) error {
  211. params := map[string]string{
  212. "Id": zoneId,
  213. "VPC.VPCId": vpcId,
  214. "VPC.VPCRegion": regionId,
  215. }
  216. ret := struct{}{}
  217. return client.dnsRequest("AssociateVPCWithHostedZone", params, &ret)
  218. }
  219. func (client *SAwsClient) DisassociateVPCFromHostedZone(vpcId string, regionId string, zoneId string) error {
  220. params := map[string]string{
  221. "Id": zoneId,
  222. "VPC.VPCId": vpcId,
  223. "VPC.VPCRegion": regionId,
  224. }
  225. ret := struct{}{}
  226. return client.dnsRequest("DisassociateVPCFromHostedZone", params, &ret)
  227. }
  228. func (self *SDnsZone) Delete() error {
  229. records, err := self.client.ListResourceRecordSet(self.Id)
  230. if err != nil {
  231. return errors.Wrapf(err, "ListResourceRecordSet")
  232. }
  233. for i := range records {
  234. if records[i].Type == "NS" || records[i].Type == "SOA" {
  235. continue
  236. }
  237. records[i].zone = self
  238. err = records[i].Delete()
  239. if err != nil {
  240. return errors.Wrapf(err, "Delete record %s", records[i].GetGlobalId())
  241. }
  242. }
  243. return self.client.DeleteDnsZone(self.Id)
  244. }
  245. func (self *SDnsZone) GetZoneType() cloudprovider.TDnsZoneType {
  246. if self.Config.PrivateZone {
  247. return cloudprovider.PrivateZone
  248. }
  249. return cloudprovider.PublicZone
  250. }
  251. func (self *SDnsZone) GetICloudVpcIds() ([]string, error) {
  252. zone, err := self.client.GetDnsZone(self.Id)
  253. if err != nil {
  254. return nil, err
  255. }
  256. ret := []string{}
  257. for _, vpc := range zone.VPC {
  258. ret = append(ret, vpc.VPCId)
  259. }
  260. return ret, nil
  261. }
  262. func (self *SDnsZone) AddVpc(vpc *cloudprovider.SPrivateZoneVpc) error {
  263. return self.client.AssociateVPCWithHostedZone(vpc.Id, vpc.RegionId, self.Id)
  264. }
  265. func (self *SDnsZone) RemoveVpc(vpc *cloudprovider.SPrivateZoneVpc) error {
  266. return self.client.DisassociateVPCFromHostedZone(vpc.Id, vpc.RegionId, self.Id)
  267. }
  268. func (self *SDnsZone) GetIDnsRecords() ([]cloudprovider.ICloudDnsRecord, error) {
  269. recordSets, err := self.client.ListResourceRecordSet(self.Id)
  270. if err != nil {
  271. return nil, errors.Wrapf(err, "ListResourceRecordSet(%s)", self.Id)
  272. }
  273. result := []cloudprovider.ICloudDnsRecord{}
  274. for i := 0; i < len(recordSets); i++ {
  275. recordSets[i].zone = self
  276. result = append(result, &recordSets[i])
  277. }
  278. return result, nil
  279. }
  280. func (self *SDnsZone) GetIDnsRecordById(id string) (cloudprovider.ICloudDnsRecord, error) {
  281. records, err := self.GetIDnsRecords()
  282. if err != nil {
  283. return nil, err
  284. }
  285. for i := range records {
  286. if records[i].GetGlobalId() == id {
  287. return records[i], nil
  288. }
  289. }
  290. return nil, cloudprovider.ErrNotFound
  291. }
  292. func (self *SDnsZone) GetGeoLocations() ([]GeoLocationDetails, error) {
  293. if len(self.locations) > 0 {
  294. return self.locations, nil
  295. }
  296. var err error
  297. self.locations, err = self.client.ListGeoLocations()
  298. return self.locations, err
  299. }
  300. func (self *SDnsZone) AddDnsRecord(opts *cloudprovider.DnsRecord) (string, error) {
  301. name := opts.DnsName
  302. if len(opts.DnsName) > 1 && opts.DnsName != "@" {
  303. name = opts.DnsName + "." + self.Name
  304. }
  305. id := ""
  306. if opts.PolicyType != cloudprovider.DnsPolicyTypeSimple {
  307. id = stringutils.UUID4()
  308. }
  309. err := self.client.ChangeResourceRecordSets("CREATE", self.Id, name, id, *opts)
  310. if err != nil {
  311. return "", errors.Wrapf(err, "ChangeResourceRecordSets")
  312. }
  313. record := &SDnsRecord{Name: name, Type: string(opts.DnsType), SetIdentifier: id}
  314. record.ResourceRecords = []SResourceRecord{
  315. {Value: opts.DnsValue},
  316. }
  317. record.zone = self
  318. return record.GetGlobalId(), nil
  319. }
  320. func (self *SDnsZone) GetDnsProductType() cloudprovider.TDnsProductType {
  321. return ""
  322. }