From 25ddca860978a96a0677ee14f1d495bf26c4b225 Mon Sep 17 00:00:00 2001 From: lvyangyang Date: Tue, 8 Sep 2020 10:28:27 +0800 Subject: [PATCH] dns access --- pkg/apis/compute/dnsrecordsets.go | 9 +- pkg/cloudprovider/cloudprovider.go | 5 + pkg/cloudprovider/dnszone.go | 420 +++++++++++++++++++ pkg/cloudprovider/dnszone_test.go | 16 + pkg/cloudprovider/resources.go | 3 + pkg/compute/models/dns_recordsets.go | 17 +- pkg/compute/models/dns_zonecaches.go | 19 +- pkg/compute/models/dns_zones.go | 27 +- pkg/mcclient/options/dnszone_recordsets.go | 5 +- pkg/multicloud/aliyun/aliyun.go | 27 +- pkg/multicloud/aliyun/dns_domain.go | 413 ++++++++++++++++++ pkg/multicloud/aliyun/dns_domain_record.go | 308 ++++++++++++++ pkg/multicloud/aliyun/private_zone.go | 404 ++++++++++++++++++ pkg/multicloud/aliyun/private_zone_record.go | 198 +++++++++ pkg/multicloud/aliyun/provider/provider.go | 69 ++- pkg/multicloud/aliyun/shell/dns_domain.go | 171 ++++++++ pkg/multicloud/aliyun/shell/private_zone.go | 193 +++++++++ pkg/multicloud/aws/dnshostedzone.go | 49 ++- pkg/multicloud/aws/dnsrecordset.go | 136 +++++- pkg/multicloud/aws/provider/provider.go | 9 +- pkg/multicloud/aws/shell/hostedzone.go | 21 + pkg/multicloud/qcloud/dnspod_domain.go | 50 ++- pkg/multicloud/qcloud/dnspod_record.go | 53 ++- pkg/multicloud/qcloud/provider/provider.go | 23 +- pkg/multicloud/qcloud/shell/dnspod.go | 15 +- 25 files changed, 2580 insertions(+), 80 deletions(-) create mode 100644 pkg/multicloud/aliyun/dns_domain.go create mode 100644 pkg/multicloud/aliyun/dns_domain_record.go create mode 100644 pkg/multicloud/aliyun/private_zone.go create mode 100644 pkg/multicloud/aliyun/private_zone_record.go create mode 100644 pkg/multicloud/aliyun/shell/dns_domain.go create mode 100644 pkg/multicloud/aliyun/shell/private_zone.go diff --git a/pkg/apis/compute/dnsrecordsets.go b/pkg/apis/compute/dnsrecordsets.go index 445545ff71..c699822576 100644 --- a/pkg/apis/compute/dnsrecordsets.go +++ b/pkg/apis/compute/dnsrecordsets.go @@ -34,10 +34,11 @@ type DnsRecordPolicy struct { type DnsRecordSetCreateInput struct { apis.EnabledStatusStandaloneResourceCreateInput - DnsZoneId string `json:"dns_zone_id"` - DnsType string `json:"dns_type"` - DnsValue string `json:"dns_value"` - TTL int64 `json:"ttl"` + DnsZoneId string `json:"dns_zone_id"` + DnsType string `json:"dns_type"` + DnsValue string `json:"dns_value"` + TTL int64 `json:"ttl"` + MxPriority int64 `json:"mx_priority"` TrafficPolicies []DnsRecordPolicy `json:"traffic_policies"` } diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go index dcd9253779..9804348f17 100644 --- a/pkg/cloudprovider/cloudprovider.go +++ b/pkg/cloudprovider/cloudprovider.go @@ -208,6 +208,7 @@ type ICloudProviderFactory interface { GetSupportedDnsTypes() map[TDnsZoneType][]TDnsType GetSupportedDnsPolicyTypes() map[TDnsZoneType][]TDnsPolicyType GetSupportedDnsPolicyValues() map[TDnsPolicyType][]TDnsPolicyValue + GetTTLRange(zoneType TDnsZoneType, productType TDnsProductType) TTlRange } type ICloudProvider interface { @@ -613,6 +614,10 @@ func (factory *baseProviderFactory) GetSupportedDnsPolicyValues() map[TDnsPolicy return map[TDnsPolicyType][]TDnsPolicyValue{} } +func (factory *baseProviderFactory) GetTTLRange(zoneType TDnsZoneType, productType TDnsProductType) TTlRange { + return TTlRange{} +} + type SDnsCapability struct { ZoneTypes []TDnsZoneType DnsTypes map[TDnsZoneType][]TDnsType diff --git a/pkg/cloudprovider/dnszone.go b/pkg/cloudprovider/dnszone.go index 100ba7d3b0..9840bc4aae 100644 --- a/pkg/cloudprovider/dnszone.go +++ b/pkg/cloudprovider/dnszone.go @@ -28,11 +28,20 @@ type TDnsType string type TDnsPolicyValue string +type TDnsTTLRangeType string + +type TDnsProductType string + const ( PublicZone = TDnsZoneType("PublicZone") PrivateZone = TDnsZoneType("PrivateZone") ) +const ( + ContinuousTTlRange = TDnsTTLRangeType("ContinuousTTlRange") + DiscreteTTlRange = TDnsTTLRangeType("DiscreteTTlRange") +) + const ( DnsPolicyTypeSimple = TDnsPolicyType("Simple") //简单 DnsPolicyTypeByCarrier = TDnsPolicyType("ByCarrier") //运营商 @@ -67,6 +76,14 @@ const ( DnsTypeFORWARD_URL = TDnsType("FORWARD_URL") //隐性URL转发 ) +const ( + DnsProductEnterpriseUltimate = TDnsProductType("DP_EnterpriseUltimate") + DnsProductEnterpriseStandard = TDnsProductType("DP_EnterpriseStandard") + DnsProductEnterpriseBasic = TDnsProductType("DP_EnterpriseBasic") + DnsProductPersonalProfessional = TDnsProductType("DP_PersonalProfessional") + DnsProductFree = TDnsProductType("DP_Free") +) + var ( SUPPORTED_DNS_TYPES = []TDnsType{ DnsTypeA, @@ -98,6 +115,8 @@ var ( DnsPolicyValueTelecom = TDnsPolicyValue("telecom") DnsPolicyValueChinaMobile = TDnsPolicyValue("chinamobile") DnsPolicyValueCernet = TDnsPolicyValue("cernet") + DnsPolicyValueDrPeng = TDnsPolicyValue("drpeng") + DnsPolicyValueBtvn = TDnsPolicyValue("btvn") DnsPolicyValueOversea = TDnsPolicyValue("oversea") DnsPolicyValueMainland = TDnsPolicyValue("mainland") @@ -111,6 +130,398 @@ var ( DnsPolicyValueQihu360 = TDnsPolicyValue("qihu360") ) +var AwsGeoLocations = []TDnsPolicyValue{ + TDnsPolicyValue("Default"), + TDnsPolicyValue("Andorra"), + TDnsPolicyValue("United Arab Emirates"), + TDnsPolicyValue("Afghanistan"), + TDnsPolicyValue("Antigua and Barbuda"), + TDnsPolicyValue("Anguilla"), + TDnsPolicyValue("Albania"), + TDnsPolicyValue("Armenia"), + TDnsPolicyValue("Angola"), + TDnsPolicyValue("Antarctica"), + TDnsPolicyValue("Argentina"), + TDnsPolicyValue("American Samoa"), + TDnsPolicyValue("Austria"), + TDnsPolicyValue("Australia"), + TDnsPolicyValue("Aruba"), + TDnsPolicyValue("Åland"), + TDnsPolicyValue("Azerbaijan"), + TDnsPolicyValue("Bosnia and Herzegovina"), + TDnsPolicyValue("Barbados"), + TDnsPolicyValue("Bangladesh"), + TDnsPolicyValue("Belgium"), + TDnsPolicyValue("Burkina Faso"), + TDnsPolicyValue("Bulgaria"), + TDnsPolicyValue("Bahrain"), + TDnsPolicyValue("Burundi"), + TDnsPolicyValue("Benin"), + TDnsPolicyValue("Saint Barthélemy"), + TDnsPolicyValue("Bermuda"), + TDnsPolicyValue("Brunei"), + TDnsPolicyValue("Bolivia"), + TDnsPolicyValue("Bonaire"), + TDnsPolicyValue("Brazil"), + TDnsPolicyValue("Bahamas"), + TDnsPolicyValue("Bhutan"), + TDnsPolicyValue("Botswana"), + TDnsPolicyValue("Belarus"), + TDnsPolicyValue("Belize"), + TDnsPolicyValue("Canada"), + TDnsPolicyValue("Cocos [Keeling] Islands"), + TDnsPolicyValue("Congo"), + TDnsPolicyValue("Central African Republic"), + TDnsPolicyValue("Republic of the Congo"), + TDnsPolicyValue("Switzerland"), + TDnsPolicyValue("Ivory Coast"), + TDnsPolicyValue("Cook Islands"), + TDnsPolicyValue("Chile"), + TDnsPolicyValue("Cameroon"), + TDnsPolicyValue("China"), + TDnsPolicyValue("Colombia"), + TDnsPolicyValue("Costa Rica"), + TDnsPolicyValue("Cuba"), + TDnsPolicyValue("Cape Verde"), + TDnsPolicyValue("Curaçao"), + TDnsPolicyValue("Cyprus"), + TDnsPolicyValue("Czech Republic"), + TDnsPolicyValue("Germany"), + TDnsPolicyValue("Djibouti"), + TDnsPolicyValue("Denmark"), + TDnsPolicyValue("Dominica"), + TDnsPolicyValue("Dominican Republic"), + TDnsPolicyValue("Algeria"), + TDnsPolicyValue("Ecuador"), + TDnsPolicyValue("Estonia"), + TDnsPolicyValue("Egypt"), + TDnsPolicyValue("Eritrea"), + TDnsPolicyValue("Spain"), + TDnsPolicyValue("Ethiopia"), + TDnsPolicyValue("Finland"), + TDnsPolicyValue("Fiji"), + TDnsPolicyValue("Falkland Islands"), + TDnsPolicyValue("Federated States of Micronesia"), + TDnsPolicyValue("Faroe Islands"), + TDnsPolicyValue("France"), + TDnsPolicyValue("Gabon"), + TDnsPolicyValue("United Kingdom"), + TDnsPolicyValue("Grenada"), + TDnsPolicyValue("Georgia"), + TDnsPolicyValue("French Guiana"), + TDnsPolicyValue("Guernsey"), + TDnsPolicyValue("Ghana"), + TDnsPolicyValue("Gibraltar"), + TDnsPolicyValue("Greenland"), + TDnsPolicyValue("Gambia"), + TDnsPolicyValue("Guinea"), + TDnsPolicyValue("Guadeloupe"), + TDnsPolicyValue("Equatorial Guinea"), + TDnsPolicyValue("Greece"), + TDnsPolicyValue("South Georgia and the South Sandwich Islands"), + TDnsPolicyValue("Guatemala"), + TDnsPolicyValue("Guam"), + TDnsPolicyValue("Guinea-Bissau"), + TDnsPolicyValue("Guyana"), + TDnsPolicyValue("Hong Kong"), + TDnsPolicyValue("Honduras"), + TDnsPolicyValue("Croatia"), + TDnsPolicyValue("Haiti"), + TDnsPolicyValue("Hungary"), + TDnsPolicyValue("Indonesia"), + TDnsPolicyValue("Ireland"), + TDnsPolicyValue("Israel"), + TDnsPolicyValue("Isle of Man"), + TDnsPolicyValue("India"), + TDnsPolicyValue("British Indian Ocean Territory"), + TDnsPolicyValue("Iraq"), + TDnsPolicyValue("Iran"), + TDnsPolicyValue("Iceland"), + TDnsPolicyValue("Italy"), + TDnsPolicyValue("Jersey"), + TDnsPolicyValue("Jamaica"), + TDnsPolicyValue("Hashemite Kingdom of Jordan"), + TDnsPolicyValue("Japan"), + TDnsPolicyValue("Kenya"), + TDnsPolicyValue("Kyrgyzstan"), + TDnsPolicyValue("Cambodia"), + TDnsPolicyValue("Kiribati"), + TDnsPolicyValue("Comoros"), + TDnsPolicyValue("Saint Kitts and Nevis"), + TDnsPolicyValue("North Korea"), + TDnsPolicyValue("Republic of Korea"), + TDnsPolicyValue("Kuwait"), + TDnsPolicyValue("Cayman Islands"), + TDnsPolicyValue("Kazakhstan"), + TDnsPolicyValue("Laos"), + TDnsPolicyValue("Lebanon"), + TDnsPolicyValue("Saint Lucia"), + TDnsPolicyValue("Liechtenstein"), + TDnsPolicyValue("Sri Lanka"), + TDnsPolicyValue("Liberia"), + TDnsPolicyValue("Lesotho"), + TDnsPolicyValue("Lithuania"), + TDnsPolicyValue("Luxembourg"), + TDnsPolicyValue("Latvia"), + TDnsPolicyValue("Libya"), + TDnsPolicyValue("Morocco"), + TDnsPolicyValue("Monaco"), + TDnsPolicyValue("Republic of Moldova"), + TDnsPolicyValue("Montenegro"), + TDnsPolicyValue("Saint Martin"), + TDnsPolicyValue("Madagascar"), + TDnsPolicyValue("Marshall Islands"), + TDnsPolicyValue("Macedonia"), + TDnsPolicyValue("Mali"), + TDnsPolicyValue("Myanmar [Burma]"), + TDnsPolicyValue("Mongolia"), + TDnsPolicyValue("Macao"), + TDnsPolicyValue("Northern Mariana Islands"), + TDnsPolicyValue("Martinique"), + TDnsPolicyValue("Mauritania"), + TDnsPolicyValue("Montserrat"), + TDnsPolicyValue("Malta"), + TDnsPolicyValue("Mauritius"), + TDnsPolicyValue("Maldives"), + TDnsPolicyValue("Malawi"), + TDnsPolicyValue("Mexico"), + TDnsPolicyValue("Malaysia"), + TDnsPolicyValue("Mozambique"), + TDnsPolicyValue("Namibia"), + TDnsPolicyValue("New Caledonia"), + TDnsPolicyValue("Niger"), + TDnsPolicyValue("Norfolk Island"), + TDnsPolicyValue("Nigeria"), + TDnsPolicyValue("Nicaragua"), + TDnsPolicyValue("Netherlands"), + TDnsPolicyValue("Norway"), + TDnsPolicyValue("Nepal"), + TDnsPolicyValue("Nauru"), + TDnsPolicyValue("Niue"), + TDnsPolicyValue("New Zealand"), + TDnsPolicyValue("Oman"), + TDnsPolicyValue("Panama"), + TDnsPolicyValue("Peru"), + TDnsPolicyValue("French Polynesia"), + TDnsPolicyValue("Papua New Guinea"), + TDnsPolicyValue("Philippines"), + TDnsPolicyValue("Pakistan"), + TDnsPolicyValue("Poland"), + TDnsPolicyValue("Saint Pierre and Miquelon"), + TDnsPolicyValue("Pitcairn Islands"), + TDnsPolicyValue("Puerto Rico"), + TDnsPolicyValue("Palestine"), + TDnsPolicyValue("Portugal"), + TDnsPolicyValue("Palau"), + TDnsPolicyValue("Paraguay"), + TDnsPolicyValue("Qatar"), + TDnsPolicyValue("Réunion"), + TDnsPolicyValue("Romania"), + TDnsPolicyValue("Serbia"), + TDnsPolicyValue("Russia"), + TDnsPolicyValue("Rwanda"), + TDnsPolicyValue("Saudi Arabia"), + TDnsPolicyValue("Solomon Islands"), + TDnsPolicyValue("Seychelles"), + TDnsPolicyValue("Sudan"), + TDnsPolicyValue("Sweden"), + TDnsPolicyValue("Singapore"), + TDnsPolicyValue("Saint Helena"), + TDnsPolicyValue("Slovenia"), + TDnsPolicyValue("Svalbard and Jan Mayen"), + TDnsPolicyValue("Slovakia"), + TDnsPolicyValue("Sierra Leone"), + TDnsPolicyValue("San Marino"), + TDnsPolicyValue("Senegal"), + TDnsPolicyValue("Somalia"), + TDnsPolicyValue("Suriname"), + TDnsPolicyValue("South Sudan"), + TDnsPolicyValue("São Tomé and Príncipe"), + TDnsPolicyValue("El Salvador"), + TDnsPolicyValue("Sint Maarten"), + TDnsPolicyValue("Syria"), + TDnsPolicyValue("Swaziland"), + TDnsPolicyValue("Turks and Caicos Islands"), + TDnsPolicyValue("Chad"), + TDnsPolicyValue("French Southern Territories"), + TDnsPolicyValue("Togo"), + TDnsPolicyValue("Thailand"), + TDnsPolicyValue("Tajikistan"), + TDnsPolicyValue("Tokelau"), + TDnsPolicyValue("East Timor"), + TDnsPolicyValue("Turkmenistan"), + TDnsPolicyValue("Tunisia"), + TDnsPolicyValue("Tonga"), + TDnsPolicyValue("Turkey"), + TDnsPolicyValue("Trinidad and Tobago"), + TDnsPolicyValue("Tuvalu"), + TDnsPolicyValue("Taiwan"), + TDnsPolicyValue("Tanzania"), + TDnsPolicyValue("Ukraine"), + TDnsPolicyValue("Uganda"), + TDnsPolicyValue("U.S. Minor Outlying Islands"), + TDnsPolicyValue("United States"), + TDnsPolicyValue("Alaska"), + TDnsPolicyValue("Alabama"), + TDnsPolicyValue("Arkansas"), + TDnsPolicyValue("Arizona"), + TDnsPolicyValue("California"), + TDnsPolicyValue("Colorado"), + TDnsPolicyValue("Connecticut"), + TDnsPolicyValue("District of Columbia"), + TDnsPolicyValue("Delaware"), + TDnsPolicyValue("Florida"), + TDnsPolicyValue("Georgia"), + TDnsPolicyValue("Hawaii"), + TDnsPolicyValue("Iowa"), + TDnsPolicyValue("Idaho"), + TDnsPolicyValue("Illinois"), + TDnsPolicyValue("Indiana"), + TDnsPolicyValue("Kansas"), + TDnsPolicyValue("Kentucky"), + TDnsPolicyValue("Louisiana"), + TDnsPolicyValue("Massachusetts"), + TDnsPolicyValue("Maryland"), + TDnsPolicyValue("Maine"), + TDnsPolicyValue("Michigan"), + TDnsPolicyValue("Minnesota"), + TDnsPolicyValue("Missouri"), + TDnsPolicyValue("Mississippi"), + TDnsPolicyValue("Montana"), + TDnsPolicyValue("North Carolina"), + TDnsPolicyValue("North Dakota"), + TDnsPolicyValue("Nebraska"), + TDnsPolicyValue("New Hampshire"), + TDnsPolicyValue("New Jersey"), + TDnsPolicyValue("New Mexico"), + TDnsPolicyValue("Nevada"), + TDnsPolicyValue("New York"), + TDnsPolicyValue("Ohio"), + TDnsPolicyValue("Oklahoma"), + TDnsPolicyValue("Oregon"), + TDnsPolicyValue("Pennsylvania"), + TDnsPolicyValue("Rhode Island"), + TDnsPolicyValue("South Carolina"), + TDnsPolicyValue("South Dakota"), + TDnsPolicyValue("Tennessee"), + TDnsPolicyValue("Texas"), + TDnsPolicyValue("Utah"), + TDnsPolicyValue("Virginia"), + TDnsPolicyValue("Vermont"), + TDnsPolicyValue("Washington"), + TDnsPolicyValue("Wisconsin"), + TDnsPolicyValue("West Virginia"), + TDnsPolicyValue("Wyoming"), + TDnsPolicyValue("Uruguay"), + TDnsPolicyValue("Uzbekistan"), + TDnsPolicyValue("Vatican City"), + TDnsPolicyValue("Saint Vincent and the Grenadines"), + TDnsPolicyValue("Venezuela"), + TDnsPolicyValue("British Virgin Islands"), + TDnsPolicyValue("U.S. Virgin Islands"), + TDnsPolicyValue("Vietnam"), + TDnsPolicyValue("Vanuatu"), + TDnsPolicyValue("Wallis and Futuna"), + TDnsPolicyValue("Samoa"), + TDnsPolicyValue("Kosovo"), + TDnsPolicyValue("Yemen"), + TDnsPolicyValue("Mayotte"), + TDnsPolicyValue("South Africa"), + TDnsPolicyValue("Zambia"), + TDnsPolicyValue("Zimbabwe"), + TDnsPolicyValue("Africa"), + TDnsPolicyValue("Antarctica"), + TDnsPolicyValue("Asia"), + TDnsPolicyValue("Europe"), + TDnsPolicyValue("North America"), + TDnsPolicyValue("Oceania"), + TDnsPolicyValue("South America"), +} + +var AwsRegions = []TDnsPolicyValue{ + TDnsPolicyValue("us-east-2"), + TDnsPolicyValue("us-east-1"), + TDnsPolicyValue("us-west-1"), + TDnsPolicyValue("us-west-2"), + TDnsPolicyValue("af-south-1"), + TDnsPolicyValue("ap-east-1"), + TDnsPolicyValue("ap-south-1"), + TDnsPolicyValue("ap-northeast-3"), + TDnsPolicyValue("ap-northeast-2"), + TDnsPolicyValue("ap-southeast-1"), + TDnsPolicyValue("ap-southeast-2"), + TDnsPolicyValue("ap-northeast-1"), + TDnsPolicyValue("ca-central-1"), + TDnsPolicyValue("cn-north-1"), + TDnsPolicyValue("cn-northwest-1"), + TDnsPolicyValue("eu-central-1"), + TDnsPolicyValue("eu-west-1"), + TDnsPolicyValue("eu-west-2"), + TDnsPolicyValue("eu-south-1"), + TDnsPolicyValue("eu-west-3"), + TDnsPolicyValue("eu-north-1"), + TDnsPolicyValue("me-south-1"), + TDnsPolicyValue("sa-east-1"), + TDnsPolicyValue("us-gov-east-1"), + TDnsPolicyValue("us-gov-west-1"), +} + +var AwsFailovers = []TDnsPolicyValue{ + TDnsPolicyValue("PRIMARY"), + TDnsPolicyValue("SECONDARY"), +} +var AwsMultiValueAnswers = []TDnsPolicyValue{ + TDnsPolicyValue("true"), + TDnsPolicyValue("false"), +} + +type TTlRange struct { + RangeType TDnsTTLRangeType + TTLMinValue int64 + TTLMaxValue int64 + AllowedTTLs []int64 // sorted +} + +func (ttlR TTlRange) GetSuppportedTTL(ttl int64) int64 { + if ttlR.RangeType == ContinuousTTlRange { + if ttl < ttlR.TTLMinValue { + return ttlR.TTLMinValue + } + if ttl > ttlR.TTLMaxValue { + return ttlR.TTLMaxValue + } + return ttl + } + if ttlR.RangeType == DiscreteTTlRange { + if ttl < ttlR.AllowedTTLs[0] { + return ttlR.AllowedTTLs[0] + } + for i := 0; i < len(ttlR.AllowedTTLs)-1; i++ { + if ttl > ttlR.AllowedTTLs[i] && ttl < ttlR.AllowedTTLs[i+1] { + if ttl-ttlR.AllowedTTLs[i] < ttlR.AllowedTTLs[i+1]-ttl { + return ttlR.AllowedTTLs[i] + } + return ttlR.AllowedTTLs[i+1] + } + } + return ttlR.AllowedTTLs[len(ttlR.AllowedTTLs)-1] + } + return ttl +} + +var TtlRangeAliyunEnterpriseUltimate = TTlRange{RangeType: ContinuousTTlRange, TTLMinValue: 1, TTLMaxValue: 86400} +var TtlRangeAliyunEnterpriseStandard = TTlRange{RangeType: ContinuousTTlRange, TTLMinValue: 60, TTLMaxValue: 86400} +var TtlRangeAliyunPersonal = TTlRange{RangeType: ContinuousTTlRange, TTLMinValue: 600, TTLMaxValue: 86400} +var TtlRangeAliyunFree = TTlRange{RangeType: ContinuousTTlRange, TTLMinValue: 600, TTLMaxValue: 86400} + +var TtlRangeAliyunPvtz = TTlRange{RangeType: DiscreteTTlRange, AllowedTTLs: []int64{5, 10, 15, 20, 30, 60, 120, 300, 600, 1800, 3600, 43200, 86400}} + +var TtlRangeQcloudEnterpriseUltimate = TTlRange{RangeType: ContinuousTTlRange, TTLMinValue: 1, TTLMaxValue: 604800} +var TtlRangeQcloudEnterpriseStandard = TTlRange{RangeType: ContinuousTTlRange, TTLMinValue: 30, TTLMaxValue: 604800} +var TtlRangeQcloudEnterpriseBasic = TTlRange{RangeType: ContinuousTTlRange, TTLMinValue: 60, TTLMaxValue: 604800} +var TtlRangeQcloudPersonalProfessional = TTlRange{RangeType: ContinuousTTlRange, TTLMinValue: 120, TTLMaxValue: 604800} +var TtlRangeQcloudFree = TTlRange{RangeType: ContinuousTTlRange, TTLMinValue: 600, TTLMaxValue: 604800} + type SPrivateZoneVpc struct { Id string RegionId string @@ -139,6 +550,7 @@ type DnsRecordSet struct { DnsValue string Status string Ttl int64 + MxPriority int64 PolicyType TDnsPolicyType PolicyValue TDnsPolicyValue PolicyOptions *jsonutils.JSONDict @@ -188,6 +600,10 @@ func (r DnsRecordSet) GetEnabled() bool { return r.Enabled } +func (r DnsRecordSet) GetMxPriority() int64 { + return r.MxPriority +} + func (record DnsRecordSet) Equals(r DnsRecordSet) bool { if record.DnsName != r.DnsName { return false @@ -210,6 +626,9 @@ func (record DnsRecordSet) Equals(r DnsRecordSet) bool { if record.Enabled != r.Enabled { return false } + if record.DnsType == DnsTypeMX && record.MxPriority != r.MxPriority { + return false + } return IsPolicyOptionEquals(record.PolicyOptions, r.PolicyOptions) } @@ -242,6 +661,7 @@ func CompareDnsRecordSet(iRecords []ICloudDnsRecordSet, local []DnsRecordSet, de Status: iRecords[i].GetStatus(), Enabled: iRecords[i].GetEnabled(), Ttl: iRecords[i].GetTTL(), + MxPriority: iRecords[i].GetMxPriority(), PolicyType: iRecords[i].GetPolicyType(), PolicyValue: iRecords[i].GetPolicyValue(), PolicyOptions: iRecords[i].GetPolicyOptions(), diff --git a/pkg/cloudprovider/dnszone_test.go b/pkg/cloudprovider/dnszone_test.go index 610d654ff6..e1677a41ac 100644 --- a/pkg/cloudprovider/dnszone_test.go +++ b/pkg/cloudprovider/dnszone_test.go @@ -161,3 +161,19 @@ func TestDiff(t *testing.T) { } } } + +func TestDiscreteTTlRange(t *testing.T) { + cases := []struct { + TtlInput int64 + TtlOutPut int64 + }{ + {TtlInput: 0, TtlOutPut: 5}, + {TtlInput: 86399, TtlOutPut: 86400}, + {TtlInput: 86401, TtlOutPut: 86400}, + } + for _, c := range cases { + if c.TtlOutPut != TtlRangeAliyunPvtz.GetSuppportedTTL(c.TtlInput) { + t.Fatalf("input %d GetSuppportedTTL should be %d", c.TtlInput, c.TtlOutPut) + } + } +} diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index e7dd26db86..f72c8a52da 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -1030,6 +1030,8 @@ type ICloudDnsZone interface { SyncDnsRecordSets(common, add, del, update []DnsRecordSet) error Delete() error + + GetDnsProductType() TDnsProductType } type ICloudDnsRecordSet interface { @@ -1041,6 +1043,7 @@ type ICloudDnsRecordSet interface { GetDnsType() TDnsType GetDnsValue() string GetTTL() int64 + GetMxPriority() int64 GetPolicyType() TDnsPolicyType GetPolicyValue() TDnsPolicyValue diff --git a/pkg/compute/models/dns_recordsets.go b/pkg/compute/models/dns_recordsets.go index 97c2a7a886..e3e150f1ff 100644 --- a/pkg/compute/models/dns_recordsets.go +++ b/pkg/compute/models/dns_recordsets.go @@ -18,6 +18,7 @@ import ( "context" "database/sql" "fmt" + "strings" "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" @@ -62,9 +63,10 @@ type SDnsRecordSet struct { db.SEnabledStatusStandaloneResourceBase SDnsZoneResourceBase - DnsType string `width:"36" charset:"ascii" nullable:"false" list:"user" update:"domain" create:"domain_required"` - DnsValue string `width:"256" charset:"ascii" nullable:"false" list:"user" update:"domain" create:"domain_required"` - TTL int64 `nullable:"false" list:"user" update:"domain" create:"domain_required" json:"ttl"` + DnsType string `width:"36" charset:"ascii" nullable:"false" list:"user" update:"domain" create:"domain_required"` + DnsValue string `width:"256" charset:"ascii" nullable:"false" list:"user" update:"domain" create:"domain_required"` + TTL int64 `nullable:"false" list:"user" update:"domain" create:"domain_required" json:"ttl"` + MxPriority int64 `nullable:"false" list:"user" update:"domain" create:"domain_optional"` } func (manager *SDnsRecordSetManager) EnableGenerateName() bool { @@ -78,6 +80,7 @@ func (manager *SDnsRecordSetManager) ValidateCreateData(ctx context.Context, use if err != nil { return input, err } + input.Name = strings.ToLower(input.Name) if len(input.DnsZoneId) == 0 { return input, httperrors.NewMissingParameterError("dns_zone_id") } @@ -88,6 +91,13 @@ func (manager *SDnsRecordSetManager) ValidateCreateData(ctx context.Context, use } return input, httperrors.NewGeneralError(err) } + if input.DnsType == "MX" { + if input.MxPriority < 1 || input.MxPriority > 50 { + return input, httperrors.NewOutOfRangeError("mx_priority range limited to [1,50]") + } + } else { + input.MxPriority = 0 + } dnsZone := _dnsZone.(*SDnsZone) for _, policy := range input.TrafficPolicies { if len(policy.Provider) == 0 { @@ -399,6 +409,7 @@ func (self *SDnsRecordSet) syncWithCloudDnsRecord(ctx context.Context, userCred self.Enabled = tristate.NewFromBool(ext.Enabled) self.Status = ext.Status self.TTL = ext.Ttl + self.MxPriority = ext.MxPriority self.DnsType = string(ext.DnsType) self.DnsValue = ext.DnsValue return nil diff --git a/pkg/compute/models/dns_zonecaches.go b/pkg/compute/models/dns_zonecaches.go index dbcb051271..4de05e23c5 100644 --- a/pkg/compute/models/dns_zonecaches.go +++ b/pkg/compute/models/dns_zonecaches.go @@ -65,6 +65,7 @@ type SDnsZoneCache struct { // 归属云账号ID CloudaccountId string `width:"36" charset:"ascii" nullable:"false" list:"user"` + ProductType string `width:"36" charset:"ascii" nullable:"false" list:"user"` } func (manager *SDnsZoneCacheManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.DnsZoneCacheCreateInput) (api.DnsZoneCacheCreateInput, error) { @@ -429,16 +430,18 @@ func (self *SDnsZoneCache) GetRecordSets() ([]cloudprovider.DnsRecordSet, error) if err != nil { return nil, errors.Wrapf(err, "GetRecordSetsByDnsType") } + ttlrange := factory.GetTTLRange(cloudprovider.TDnsZoneType(dnsZone.ZoneType), cloudprovider.TDnsProductType(self.ProductType)) ret := []cloudprovider.DnsRecordSet{} for i := range records { record := cloudprovider.DnsRecordSet{ - Id: records[i].Id, - DnsName: records[i].Name, - DnsValue: records[i].DnsValue, - DnsType: cloudprovider.TDnsType(records[i].DnsType), - Enabled: records[i].Enabled.Bool(), - Status: records[i].Status, - Ttl: records[i].TTL, + Id: records[i].Id, + DnsName: records[i].Name, + DnsValue: records[i].DnsValue, + DnsType: cloudprovider.TDnsType(records[i].DnsType), + Enabled: records[i].Enabled.Bool(), + Status: records[i].Status, + Ttl: ttlrange.GetSuppportedTTL(records[i].TTL), + MxPriority: records[i].MxPriority, } record.PolicyType, record.PolicyValue, record.PolicyOptions, err = records[i].GetDefaultDnsTrafficPolicy(account.Provider) @@ -490,7 +493,7 @@ func (self *SDnsZoneCache) SyncRecordSets(ctx context.Context, userCred mcclient return errors.Wrapf(err, "DnsRecordSetManager.FetchById(%s)", update[i].Id) } record := _record.(*SDnsRecordSet) - update[i].Ttl = record.TTL + update[i].MxPriority = record.MxPriority update[i].Enabled = record.GetEnabled() } diff --git a/pkg/compute/models/dns_zones.go b/pkg/compute/models/dns_zones.go index 48fef62ae7..566da5aeaf 100644 --- a/pkg/compute/models/dns_zones.go +++ b/pkg/compute/models/dns_zones.go @@ -387,7 +387,7 @@ func (manager *SDnsZoneManager) newFromCloudDnsZone(ctx context.Context, userCre SyncCloudDomain(userCred, dnsZone, account.GetOwnerId()) dnsZone.SyncShareState(ctx, userCred, account.getAccountShareInfo()) - _, err = dnsZone.newCache(ctx, userCred, account.Id, ext.GetGlobalId()) + _, err = dnsZone.newCache(ctx, userCred, account.Id, ext) if err != nil { return nil, false, errors.Wrapf(err, "newCache") } @@ -433,16 +433,19 @@ func (self *SDnsZone) RegisterCache(ctx context.Context, userCred mcclient.Token return cache, nil } - return self.newCache(ctx, userCred, accountId, "") + return self.newCache(ctx, userCred, accountId, nil) } -func (self *SDnsZone) newCache(ctx context.Context, userCred mcclient.TokenCredential, accountId, externalId string) (*SDnsZoneCache, error) { +func (self *SDnsZone) newCache(ctx context.Context, userCred mcclient.TokenCredential, accountId string, ext cloudprovider.ICloudDnsZone) (*SDnsZoneCache, error) { cache := &SDnsZoneCache{} cache.SetModelManager(DnsZoneCacheManager, cache) cache.Name = self.Name - cache.ExternalId = externalId cache.CloudaccountId = accountId cache.DnsZoneId = self.Id + if ext != nil { + cache.ExternalId = ext.GetGlobalId() + cache.ProductType = string(ext.GetDnsProductType()) + } err := DnsZoneCacheManager.TableSpec().Insert(ctx, cache) if err != nil { return nil, errors.Wrapf(err, "dnsZoneCache.Insert") @@ -513,13 +516,14 @@ func (self *SDnsZone) SyncDnsRecordSets(ctx context.Context, userCred mcclient.T local := []cloudprovider.DnsRecordSet{} for i := range dbRecords { record := cloudprovider.DnsRecordSet{ - Id: dbRecords[i].Id, - DnsName: dbRecords[i].Name, - Enabled: dbRecords[i].Enabled.Bool(), - Status: dbRecords[i].Status, - DnsType: cloudprovider.TDnsType(dbRecords[i].DnsType), - DnsValue: dbRecords[i].DnsValue, - Ttl: dbRecords[i].TTL, + Id: dbRecords[i].Id, + DnsName: dbRecords[i].Name, + Enabled: dbRecords[i].Enabled.Bool(), + Status: dbRecords[i].Status, + DnsType: cloudprovider.TDnsType(dbRecords[i].DnsType), + DnsValue: dbRecords[i].DnsValue, + Ttl: dbRecords[i].TTL, + MxPriority: dbRecords[i].MxPriority, } record.PolicyType, record.PolicyValue, record.PolicyOptions, err = dbRecords[i].GetDefaultDnsTrafficPolicy(provider) if err != nil { @@ -582,6 +586,7 @@ func (self *SDnsZone) newFromCloudDnsRecordSet(ctx context.Context, userCred mcc record.Status = ext.Status record.Enabled = tristate.NewFromBool(ext.Enabled) record.TTL = ext.Ttl + record.MxPriority = ext.MxPriority record.DnsType = string(ext.DnsType) record.DnsValue = ext.DnsValue diff --git a/pkg/mcclient/options/dnszone_recordsets.go b/pkg/mcclient/options/dnszone_recordsets.go index 46eb804e3c..2a1824ac3d 100644 --- a/pkg/mcclient/options/dnszone_recordsets.go +++ b/pkg/mcclient/options/dnszone_recordsets.go @@ -31,10 +31,11 @@ func (opts *DnsRecordSetListOptions) Params() (jsonutils.JSONObject, error) { type DnsRecordSetCreateOptions struct { EnabledStatusCreateOptions DNS_ZONE_ID string `help:"Dns Zone Id"` - DNS_TYPE string `choices:"A|AAAA|CAA|CNAME|MX|NS|SRV|SOA|TXT|PRT|DS|DNSKEY|IPSECKEY|NAPTR|SPF|SSHFP|TLSA|REDIRECT_URL|FORWARD_URL"` + DNS_TYPE string `choices:"A|AAAA|CAA|CNAME|MX|NS|SRV|SOA|TXT|PTR|DS|DNSKEY|IPSECKEY|NAPTR|SPF|SSHFP|TLSA|REDIRECT_URL|FORWARD_URL"` DNS_VALUE string `help:"Dns Value"` TTL int64 `help:"Dns ttl"` - Provider string `help:"Dns triffic policy provider" choices:"Aws|Qcloud"` + MxPriority int64 `help:"dns mx type mxpriority"` + Provider string `help:"Dns triffic policy provider" choices:"Aws|Qcloud|Aliyun"` PolicyType string `choices:"Simple|ByCarrier|ByGeoLocation|BySearchEngine|IpRange|Weighted|Failover|MultiValueAnswer|Latency"` PolicyValue string `help:"Dns Traffic policy value"` PolicyOptions string diff --git a/pkg/multicloud/aliyun/aliyun.go b/pkg/multicloud/aliyun/aliyun.go index 7d038c8de4..9d1fb252e5 100644 --- a/pkg/multicloud/aliyun/aliyun.go +++ b/pkg/multicloud/aliyun/aliyun.go @@ -50,10 +50,12 @@ const ( ALIYUN_BSS_API_VERSION = "2017-12-14" - ALIYUN_RAM_API_VERSION = "2015-05-01" - ALIYUN_API_VERION_RDS = "2014-08-15" - ALIYUN_RM_API_VERSION = "2020-03-31" - ALIYUN_STS_API_VERSION = "2015-04-01" + ALIYUN_RAM_API_VERSION = "2015-05-01" + ALIYUN_API_VERION_RDS = "2014-08-15" + ALIYUN_RM_API_VERSION = "2020-03-31" + ALIYUN_STS_API_VERSION = "2015-04-01" + ALIYUN_PVTZ_API_VERSION = "2018-01-01" + ALIYUN_ALIDNS_API_VERSION = "2015-01-09" ) type AliyunClientConfig struct { @@ -235,6 +237,22 @@ func (self *SAliyunClient) trialRequest(apiName string, params map[string]string return jsonRequest(cli, "actiontrail.cn-hangzhou.aliyuncs.com", ALIYUN_API_VERSION_TRIAL, apiName, params, self.debug) } +func (self *SAliyunClient) pvtzRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + cli, err := self.getDefaultClient() + if err != nil { + return nil, err + } + return jsonRequest(cli, "pvtz.aliyuncs.com", ALIYUN_PVTZ_API_VERSION, apiName, params, self.debug) +} + +func (self *SAliyunClient) alidnsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + cli, err := self.getDefaultClient() + if err != nil { + return nil, err + } + return jsonRequest(cli, "alidns.aliyuncs.com", ALIYUN_ALIDNS_API_VERSION, apiName, params, self.debug) +} + func (self *SAliyunClient) fetchRegions() error { body, err := self.ecsRequest("DescribeRegions", map[string]string{"AcceptLanguage": "zh-CN"}) if err != nil { @@ -464,6 +482,7 @@ func (region *SAliyunClient) GetCapabilities() []string { cloudprovider.CLOUD_CAPABILITY_CACHE, cloudprovider.CLOUD_CAPABILITY_EVENT, cloudprovider.CLOUD_CAPABILITY_CLOUDID, + cloudprovider.CLOUD_CAPABILITY_DNSZONE, } return caps } diff --git a/pkg/multicloud/aliyun/dns_domain.go b/pkg/multicloud/aliyun/dns_domain.go new file mode 100644 index 0000000000..f3fdddc585 --- /dev/null +++ b/pkg/multicloud/aliyun/dns_domain.go @@ -0,0 +1,413 @@ +// 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 aliyun + +import ( + "strconv" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type SDomain struct { + multicloud.SResourceBase + client *SAliyunClient + ttlMinValue int64 + PunyCode string `json:"PunyCode"` + VersionCode string `json:"VersionCode"` + InstanceID string `json:"InstanceId"` + AliDomain bool `json:"AliDomain"` + DomainName string `json:"DomainName"` + DomainID string `json:"DomainId"` + DNSServers DNSServers `json:"DnsServers"` + GroupID string `json:"GroupId"` +} + +type SDomains struct { + PageNumber int `json:"PageNumber"` + TotalCount int `json:"TotalCount"` + PageSize int `json:"PageSize"` + // RequestID string `json:"RequestId"` + Domains sDomains `json:"Domains"` +} + +type DNSServers struct { + DNSServer []string `json:"DnsServer"` +} + +type sDomains struct { + Domain []SDomain `json:"Domain"` +} + +// https://help.aliyun.com/document_detail/29758.html?spm=a2c4g.11186623.6.653.2ad93b59euq4oF +type SDNSProduct struct { + client *SAliyunClient + InstanceID string `json:"InstanceId"` + VersionCode string `json:"VersionCode"` + VersionName string `json:"VersionName"` + StartTime int64 `json:"StartTime"` + EndTime int64 `json:"EndTime"` + Domain string `json:"Domain"` + BindCount int64 `json:"BindCount"` + BindUsedCount int64 `json:"BindUsedCount"` + TTLMinValue int64 `json:"TTLMinValue"` + SubDomainLevel int64 `json:"SubDomainLevel"` + DNSSLBCount int64 `json:"DnsSLBCount"` + URLForwardCount int64 `json:"URLForwardCount"` + DDosDefendFlow int64 `json:"DDosDefendFlow"` + DDosDefendQuery int64 `json:"DDosDefendQuery"` + OverseaDDosDefendFlow int64 `json:"OverseaDDosDefendFlow"` + SearchEngineLines string `json:"SearchEngineLines"` + ISPLines string `json:"ISPLines"` + ISPRegionLines string `json:"ISPRegionLines"` + OverseaLine string `json:"OverseaLine"` +} + +type sDNSProducts struct { + DNSProduct []SDNSProduct `json:"DnsProduct"` +} + +type SDNSProducts struct { + // RequestID string `json:"RequestId"` + TotalCount int `json:"TotalCount"` + PageNumber int `json:"PageNumber"` + PageSize int `json:"PageSize"` + DNSProducts sDNSProducts `json:"DnsProducts"` +} + +// https://help.aliyun.com/document_detail/29758.html?spm=a2c4g.11186623.6.653.4b6d6970WphX2t +func (client *SAliyunClient) DescribeDnsProductInstances(pageNumber int, pageSize int) (SDNSProducts, error) { + sproducts := SDNSProducts{} + params := map[string]string{} + params["Action"] = "DescribeDnsProductInstances" + params["PageNumber"] = strconv.Itoa(pageNumber) + params["PageSize"] = strconv.Itoa(pageSize) + resp, err := client.alidnsRequest("DescribeDnsProductInstances", params) + if err != nil { + return sproducts, errors.Wrap(err, "DescribeDnsProductInstances") + } + err = resp.Unmarshal(&sproducts, "DnsProducts") + if err != nil { + return sproducts, errors.Wrap(err, "resp.Unmarshal") + } + return sproducts, nil +} + +func (client *SAliyunClient) GetAllDnsProductInstances() ([]SDNSProduct, error) { + pageNumber := 0 + sproducts := []SDNSProduct{} + for { + pageNumber++ + products, err := client.DescribeDnsProductInstances(pageNumber, 20) + if err != nil { + return nil, errors.Wrapf(err, "client.DescribeDnsProductInstances(%d, 20)", pageNumber) + } + sproducts = append(sproducts, products.DNSProducts.DNSProduct...) + if len(sproducts) >= products.TotalCount { + break + } + } + for i := 0; i < len(sproducts); i++ { + sproducts[i].client = client + } + return sproducts, nil +} + +// https://help.aliyun.com/document_detail/29751.html?spm=a2c4g.11186623.6.638.55563230d00kzJ +func (client *SAliyunClient) DescribeDomains(pageNumber int, pageSize int) (SDomains, error) { + sdomains := SDomains{} + params := map[string]string{} + params["Action"] = "DescribeDomains" + params["PageNumber"] = strconv.Itoa(pageNumber) + params["PageSize"] = strconv.Itoa(pageSize) + resp, err := client.alidnsRequest("DescribeDomains", params) + if err != nil { + return sdomains, errors.Wrap(err, "DescribeDomains") + } + err = resp.Unmarshal(&sdomains) + if err != nil { + return sdomains, errors.Wrap(err, "resp.Unmarshal") + } + return sdomains, nil +} + +func (client *SAliyunClient) GetAllDomains() ([]SDomain, error) { + pageNumber := 0 + sdomains := []SDomain{} + for { + pageNumber += 1 + domains, err := client.DescribeDomains(pageNumber, 20) + if err != nil { + return nil, errors.Wrapf(err, "client.DescribeDomains(%d, 20)", pageNumber) + } + sdomains = append(sdomains, domains.Domains.Domain...) + if len(sdomains) >= domains.TotalCount { + break + } + } + for i := 0; i < len(sdomains); i++ { + sdomains[i].client = client + } + return sdomains, nil +} + +func (client *SAliyunClient) GetPublicICloudDnsZones() ([]cloudprovider.ICloudDnsZone, error) { + izones := []cloudprovider.ICloudDnsZone{} + sdomains, err := client.GetAllDomains() + if err != nil { + return nil, errors.Wrap(err, "client.GetAllDomains()") + } + for i := 0; i < len(sdomains); i++ { + izones = append(izones, &sdomains[i]) + } + return izones, nil +} + +func (client *SAliyunClient) DescribeDomainInfo(domainName string) (*SDomain, error) { + sdomain := &SDomain{client: client} + params := map[string]string{} + params["Action"] = "DescribeDomainInfo" + params["DomainName"] = domainName + resp, err := client.alidnsRequest("DescribeDomainInfo", params) + if err != nil { + return sdomain, errors.Wrap(err, "DescribeDomainInfo") + } + err = resp.Unmarshal(sdomain) + if err != nil { + return sdomain, errors.Wrap(err, "resp.Unmarshal") + } + return sdomain, nil +} + +func (client *SAliyunClient) GetPublicICloudDnsZoneById(id string) (cloudprovider.ICloudDnsZone, error) { + izones, err := client.GetPublicICloudDnsZones() + if err != nil { + return nil, errors.Wrapf(err, "client.DescribeDomainInfo(%s)", id) + } + for i := 0; i < len(izones); i++ { + if izones[i].GetGlobalId() == id { + return izones[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (client *SAliyunClient) AddDomain(domainName string) (*SDomain, error) { + sdomain := &SDomain{client: client} + params := map[string]string{} + params["Action"] = "AddDomain" + params["DomainName"] = domainName + resp, err := client.alidnsRequest("AddDomain", params) + if err != nil { + return sdomain, errors.Wrap(err, "AddDomain") + } + err = resp.Unmarshal(sdomain) + if err != nil { + return sdomain, errors.Wrapf(err, "%s:resp.Unmarshal()", resp) + } + return sdomain, nil +} + +func (client *SAliyunClient) CreatePublicICloudDnsZone(opts *cloudprovider.SDnsZoneCreateOptions) (cloudprovider.ICloudDnsZone, error) { + sdomain, err := client.AddDomain(opts.Name) + if err != nil { + return nil, errors.Wrapf(err, "client.AddDomain(%s)", opts.Name) + } + return sdomain, nil +} + +func (client *SAliyunClient) DeleteDomain(domainName string) error { + params := map[string]string{} + params["Action"] = "DeleteDomain" + params["DomainName"] = domainName + _, err := client.alidnsRequest("DeleteDomain", params) + if err != nil { + return errors.Wrap(err, "DeleteDomain") + } + return nil +} + +func (self *SDomain) GetId() string { + return self.DomainID +} + +func (self *SDomain) GetName() string { + return self.DomainName +} + +func (self *SDomain) GetGlobalId() string { + return self.GetId() +} + +func (self *SDomain) GetStatus() string { + return api.DNS_ZONE_STATUS_AVAILABLE +} + +func (self *SDomain) Refresh() error { + sdomain, err := self.client.DescribeDomainInfo(self.DomainName) + if err != nil { + return errors.Wrapf(err, "self.client.DescribeDomainInfo(%s)", self.DomainName) + } + return jsonutils.Update(self, sdomain) +} + +func (self *SDomain) GetZoneType() cloudprovider.TDnsZoneType { + return cloudprovider.PublicZone +} +func (self *SDomain) GetOptions() *jsonutils.JSONDict { + return nil +} + +func (self *SDomain) GetICloudVpcIds() ([]string, error) { + return nil, nil +} +func (self *SDomain) AddVpc(vpc *cloudprovider.SPrivateZoneVpc) error { + return cloudprovider.ErrNotSupported +} +func (self *SDomain) RemoveVpc(vpc *cloudprovider.SPrivateZoneVpc) error { + return cloudprovider.ErrNotSupported +} + +func (self *SDomain) GetIDnsRecordSets() ([]cloudprovider.ICloudDnsRecordSet, error) { + irecords := []cloudprovider.ICloudDnsRecordSet{} + records, err := self.client.GetAllDomainRecords(self.DomainName) + if err != nil { + return nil, errors.Wrapf(err, "self.client.GetAllDomainRecords(%s)", self.DomainName) + } + for i := 0; i < len(records); i++ { + irecords = append(irecords, &records[i]) + } + return irecords, nil +} + +func (self *SDomain) SyncDnsRecordSets(common, add, del, update []cloudprovider.DnsRecordSet) error { + for i := 0; i < len(del); i++ { + err := self.client.DeleteDomainRecord(del[i].ExternalId) + if err != nil { + return errors.Wrapf(err, "self.client.DeleteDomainRecord(%s)", del[i].ExternalId) + } + } + + for i := 0; i < len(add); i++ { + recordId, err := self.client.AddDomainRecord(self.DomainName, add[i]) + if err != nil { + return errors.Wrapf(err, "self.client.AddDomainRecord(%s,%s)", self.DomainName, jsonutils.Marshal(add[i]).String()) + } + if !add[i].Enabled { + // Enable: 启用解析 Disable: 暂停解析 + err = self.client.SetDomainRecordStatus(recordId, "Disable") + if err != nil { + return errors.Wrapf(err, "self.client.SetDomainRecordStatus(%s,%t)", recordId, add[i].Enabled) + } + } + } + + for i := 0; i < len(update); i++ { + // Enable: 启用解析 Disable: 暂停解析 + status := "Enable" + if !update[i].Enabled { + status = "Disable" + } + err := self.client.SetDomainRecordStatus(update[i].ExternalId, status) + if err != nil { + return errors.Wrapf(err, "self.client.SetDomainRecordStatus(%s,%t)", update[i].ExternalId, update[i].Enabled) + } + domainRecord, err := self.client.DescribeDomainRecordInfo(update[i].ExternalId) + if err != nil { + return errors.Wrapf(err, "self.client.DescribeDomainRecordInfo(%s)", update[i].ExternalId) + } + if !domainRecord.match(update[i]) { + err = self.client.UpdateDomainRecord(update[i]) + if err != nil { + return errors.Wrapf(err, "self.client.UpdateDomainRecord(%s)", jsonutils.Marshal(update[i]).String()) + } + } + } + return nil +} + +func (self *SDomain) Delete() error { + return self.client.DeleteDomain(self.DomainName) +} + +func TDnsProductType(productName string) cloudprovider.TDnsProductType { + switch productName { + case "企业旗舰版": + return cloudprovider.DnsProductEnterpriseUltimate + case "企业标准版": + return cloudprovider.DnsProductEnterpriseStandard + case "个人版": + return cloudprovider.DnsProductPersonalProfessional + default: + return cloudprovider.DnsProductFree + } +} + +func (self *SDomain) GetDnsProductType() cloudprovider.TDnsProductType { + sproducts, err := self.client.GetAllDnsProductInstances() + if err != nil { + log.Errorf("self.client.GetAllDnsProductInstances():%s", err) + return cloudprovider.DnsProductFree + } + // https://help.aliyun.com/document_detail/29806.html?spm=a2c4g.11186623.4.1.67728197c8SCN9 + // 免费版,最低600 + self.ttlMinValue = 600 + for i := 0; i < len(sproducts); i++ { + if sproducts[i].Domain == self.DomainName { + return TDnsProductType(sproducts[i].VersionName) + } + } + return cloudprovider.DnsProductFree +} + +func (self *SDomain) fetchTTLMinValue() (int64, error) { + if self.ttlMinValue != 0 { + return self.ttlMinValue, nil + } + sproducts, err := self.client.GetAllDnsProductInstances() + if err != nil { + return 0, errors.Wrap(err, "self.client.GetAllDnsProductInstances()") + } + // https://help.aliyun.com/document_detail/29806.html?spm=a2c4g.11186623.4.1.67728197c8SCN9 + // 免费版,最低600 + self.ttlMinValue = 600 + for i := 0; i < len(sproducts); i++ { + if sproducts[i].Domain == self.DomainName { + self.ttlMinValue = sproducts[i].TTLMinValue + return self.ttlMinValue, nil + } + } + return self.ttlMinValue, nil +} + +func (self *SDomain) GetProperlyTTL(ttl int64) int64 { + ttlMin, err := self.fetchTTLMinValue() + if err != nil { + log.Errorf("self.fetchTTLMinValue():%s", err) + ttlMin = 600 + } + if ttl <= ttlMin { + return ttlMin + } + if ttl > 86400 { + return 86400 + } + return ttl +} diff --git a/pkg/multicloud/aliyun/dns_domain_record.go b/pkg/multicloud/aliyun/dns_domain_record.go new file mode 100644 index 0000000000..5b53d6b06d --- /dev/null +++ b/pkg/multicloud/aliyun/dns_domain_record.go @@ -0,0 +1,308 @@ +// 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 aliyun + +import ( + "strconv" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SDomainRecords struct { + //RequestID string `json:"RequestId"` + TotalCount int `json:"TotalCount"` + PageNumber int `json:"PageNumber"` + PageSize int `json:"PageSize"` + DomainRecords sDomainRecords `json:"DomainRecords"` +} + +// https://help.aliyun.com/document_detail/29777.html?spm=a2c4g.11186623.6.666.aa4832307YdopF +type SDomainRecord struct { + DomainId string `json:"DomainId"` + GroupId string `json:"GroupId"` + GroupName string `json:"GroupName"` + PunyCode string `json:"PunyCode"` + RR string `json:"RR"` + Status string `json:"Status"` + Value string `json:"Value"` + RecordID string `json:"RecordId"` + Type string `json:"Type"` + RequestID string `json:"RequestId"` + DomainName string `json:"DomainName"` + Locked bool `json:"Locked"` + Line string `json:"Line"` + TTL int64 `json:"TTL"` + Priority int64 `json:"Priority"` +} + +type sDomainRecords struct { + Record []SDomainRecord `json:"Record"` +} + +func (client *SAliyunClient) DescribeDomainRecords(domainName string, pageNumber int, pageSize int) (SDomainRecords, error) { + srecords := SDomainRecords{} + params := map[string]string{} + params["Action"] = "DescribeDomainRecords" + params["DomainName"] = domainName + params["PageNumber"] = strconv.Itoa(pageNumber) + params["PageSize"] = strconv.Itoa(pageSize) + resp, err := client.alidnsRequest("DescribeDomainRecords", params) + if err != nil { + return srecords, errors.Wrap(err, "DescribeDomainRecords") + } + err = resp.Unmarshal(&srecords) + if err != nil { + return srecords, errors.Wrap(err, "resp.Unmarshal") + } + return srecords, nil +} + +func (client *SAliyunClient) GetAllDomainRecords(domainName string) ([]SDomainRecord, error) { + srecords := []SDomainRecord{} + pageNumber := 0 + for { + pageNumber++ + records, err := client.DescribeDomainRecords(domainName, pageNumber, 2) + if err != nil { + return nil, errors.Wrapf(err, "client.DescribeDomainRecords(%d, 20)", len(srecords)) + } + srecords = append(srecords, records.DomainRecords.Record...) + if len(srecords) >= records.TotalCount { + break + } + } + return srecords, nil +} + +func (client *SAliyunClient) DescribeDomainRecordInfo(recordId string) (*SDomainRecord, error) { + srecord := SDomainRecord{} + params := map[string]string{} + params["Action"] = "DescribeDomainRecordInfo" + params["RecordId"] = recordId + + resp, err := client.alidnsRequest("DescribeDomainRecordInfo", params) + if err != nil { + return nil, errors.Wrap(err, "DescribeDomainRecordInfo") + } + err = resp.Unmarshal(&srecord) + if err != nil { + return nil, errors.Wrap(err, "resp.Unmarshal") + } + return &srecord, nil +} + +func GetRecordLineLineType(policyinfo cloudprovider.TDnsPolicyValue) string { + switch policyinfo { + case cloudprovider.DnsPolicyValueOversea: + return "oversea" + case cloudprovider.DnsPolicyValueTelecom: + return "telecom" + case cloudprovider.DnsPolicyValueUnicom: + return "unicom" + case cloudprovider.DnsPolicyValueChinaMobile: + return "mobile" + case cloudprovider.DnsPolicyValueCernet: + return "edu" + case cloudprovider.DnsPolicyValueDrPeng: + return "drpeng" + case cloudprovider.DnsPolicyValueBtvn: + return "btvn" + + case cloudprovider.DnsPolicyValueBaidu: + return "baidu" + case cloudprovider.DnsPolicyValueGoogle: + return "google" + case cloudprovider.DnsPolicyValueYoudao: + return "youdao" + case cloudprovider.DnsPolicyValueBing: + return "biying" + default: + return "default" + } +} + +func (client *SAliyunClient) AddDomainRecord(domainName string, opts cloudprovider.DnsRecordSet) (string, error) { + line := GetRecordLineLineType(opts.PolicyValue) + params := map[string]string{} + params["Action"] = "AddDomainRecord" + params["RR"] = opts.DnsName + params["Type"] = string(opts.DnsType) + params["Value"] = opts.DnsValue + params["DomainName"] = domainName + params["TTL"] = strconv.FormatInt(opts.Ttl, 10) + params["Line"] = line + if opts.DnsType == cloudprovider.DnsTypeMX { + params["Priority"] = strconv.FormatInt(opts.MxPriority, 10) + } + ret, err := client.alidnsRequest("AddDomainRecord", params) + if err != nil { + return "", errors.Wrap(err, "AddDomainRecord") + } + recordId := "" + return recordId, ret.Unmarshal(&recordId, "RecordId") +} + +// line +func (client *SAliyunClient) UpdateDomainRecord(opts cloudprovider.DnsRecordSet) error { + line := GetRecordLineLineType(opts.PolicyValue) + params := map[string]string{} + params["Action"] = "UpdateDomainRecord" + params["RR"] = opts.DnsName + params["RecordId"] = opts.ExternalId + params["Type"] = string(opts.DnsType) + params["Value"] = opts.DnsValue + params["TTL"] = strconv.FormatInt(opts.Ttl, 10) + params["Line"] = line + if opts.DnsType == cloudprovider.DnsTypeMX { + params["Priority"] = strconv.FormatInt(opts.MxPriority, 10) + } + _, err := client.alidnsRequest("UpdateDomainRecord", params) + if err != nil { + return errors.Wrap(err, "UpdateDomainRecord") + } + return nil +} + +// Enable: 启用解析 Disable: 暂停解析 +func (client *SAliyunClient) SetDomainRecordStatus(recordId, status string) error { + params := map[string]string{} + params["Action"] = "SetDomainRecordStatus" + params["RecordId"] = recordId + params["Status"] = status + _, err := client.alidnsRequest("SetDomainRecordStatus", params) + if err != nil { + return errors.Wrap(err, "SetDomainRecordStatus") + } + return nil +} + +func (client *SAliyunClient) DeleteDomainRecord(recordId string) error { + params := map[string]string{} + params["Action"] = "DeleteDomainRecord" + params["RecordId"] = recordId + _, err := client.alidnsRequest("DeleteDomainRecord", params) + if err != nil { + return errors.Wrap(err, "DeleteDomainRecord") + } + return nil +} + +func (self *SDomainRecord) GetGlobalId() string { + return self.RecordID +} + +func (self *SDomainRecord) GetDnsName() string { + return self.RR +} + +func (self *SDomainRecord) GetStatus() string { + return api.DNS_ZONE_STATUS_AVAILABLE +} + +func (self *SDomainRecord) GetEnabled() bool { + return self.Status == "ENABLE" +} + +func (self *SDomainRecord) GetDnsType() cloudprovider.TDnsType { + return cloudprovider.TDnsType(self.Type) +} + +func (self *SDomainRecord) GetDnsValue() string { + return self.Value +} + +func (self *SDomainRecord) GetTTL() int64 { + return self.TTL +} + +func (self *SDomainRecord) GetMxPriority() int64 { + if self.GetDnsType() == cloudprovider.DnsTypeMX { + return self.Priority + } + return 0 +} + +func (self *SDomainRecord) GetPolicyType() cloudprovider.TDnsPolicyType { + switch self.Line { + case "telecom", "unicom", "mobile", "edu", "drpeng", "btvn": + return cloudprovider.DnsPolicyTypeByCarrier + case "google", "baidu", "biying", "youdao": + return cloudprovider.DnsPolicyTypeBySearchEngine + case "oversea": + return cloudprovider.DnsPolicyTypeByGeoLocation + default: + return cloudprovider.DnsPolicyTypeSimple + } +} + +func (self *SDomainRecord) GetPolicyValue() cloudprovider.TDnsPolicyValue { + switch self.Line { + case "telecom": + return cloudprovider.DnsPolicyValueTelecom + case "unicom": + return cloudprovider.DnsPolicyValueUnicom + case "mobile": + return cloudprovider.DnsPolicyValueChinaMobile + case "oversea": + return cloudprovider.DnsPolicyValueOversea + case "edu": + return cloudprovider.DnsPolicyValueCernet + + case "drpeng": + return cloudprovider.DnsPolicyValueDrPeng + case "btvn": + return cloudprovider.DnsPolicyValueBtvn + case "google": + return cloudprovider.DnsPolicyValueGoogle + case "baidu": + return cloudprovider.DnsPolicyValueBaidu + case "biying": + return cloudprovider.DnsPolicyValueBing + case "youdao": + return cloudprovider.DnsPolicyValueYoudao + + } + return "" +} + +func (self *SDomainRecord) GetPolicyOptions() *jsonutils.JSONDict { + return nil +} + +func (self *SDomainRecord) match(update cloudprovider.DnsRecordSet) bool { + if update.DnsName != self.GetDnsName() { + return false + } + if update.DnsType != self.GetDnsType() { + return false + } + if update.DnsValue != self.GetDnsValue() { + return false + } + if update.PolicyType != self.GetPolicyType() { + return false + } + if update.PolicyValue != self.GetPolicyValue() { + return false + } + if update.Ttl != self.GetTTL() { + return false + } + return true +} diff --git a/pkg/multicloud/aliyun/private_zone.go b/pkg/multicloud/aliyun/private_zone.go new file mode 100644 index 0000000000..da736e6f6d --- /dev/null +++ b/pkg/multicloud/aliyun/private_zone.go @@ -0,0 +1,404 @@ +// 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 aliyun + +import ( + "strconv" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +var allowedTtls = []int64{5, 10, 15, 20, 30, 60, 120, 300, 600, 1800, 3600, 43200, 86400} + +type SPvtzVpc struct { + VpcID string `json:"VpcId"` + RegionName string `json:"RegionName"` + VpcName string `json:"VpcName"` + RegionID string `json:"RegionId"` +} + +type SPvtzBindVpcs struct { + Vpc []SPvtzVpc `json:"Vpc"` +} + +type SPrivateZone struct { + multicloud.SResourceBase + client *SAliyunClient + + // RequestID string `json:"RequestId"` + ZoneID string `json:"ZoneId"` + SlaveDNS string `json:"SlaveDns"` + ResourceGroupID string `json:"ResourceGroupId"` + ProxyPattern string `json:"ProxyPattern"` + CreateTime string `json:"CreateTime"` + Remark string `json:"Remark"` + ZoneName string `json:"ZoneName"` + UpdateTime string `json:"UpdateTime"` + UpdateTimestamp string `json:"UpdateTimestamp"` + RecordCount int `json:"RecordCount"` + CreateTimestamp int64 `json:"CreateTimestamp"` + BindVpcs SPvtzBindVpcs `json:"BindVpcs"` + IsPtr bool `json:"IsPtr"` +} + +// list return +type sPrivateZones struct { + Zone []SPrivateZone `json:"Zone"` +} + +type SPrivateZones struct { + RequestID string `json:"RequestId"` + PageSize int `json:"PageSize"` + PageNumber int `json:"PageNumber"` + TotalPages int `json:"TotalPages"` + TotalItems int `json:"TotalItems"` + Zones sPrivateZones `json:"Zones"` +} + +// https://help.aliyun.com/document_detail/66243.html?spm=a2c4g.11186623.6.580.761357982tMV0Q +func (client *SAliyunClient) DescribeZones(pageNumber int, pageSize int) (SPrivateZones, error) { + sZones := SPrivateZones{} + params := map[string]string{} + params["Action"] = "DescribeZones" + params["PageNumber"] = strconv.Itoa(pageNumber) + params["PageSize"] = strconv.Itoa(pageSize) + resp, err := client.pvtzRequest("DescribeZones", params) + if err != nil { + return sZones, errors.Wrap(err, "DescribeZones") + } + err = resp.Unmarshal(&sZones) + if err != nil { + return sZones, errors.Wrap(err, "resp.Unmarshal") + } + return sZones, nil +} + +// 没有vpc等详细信息 +func (client *SAliyunClient) GetAllZones() ([]SPrivateZone, error) { + pageNumber := 0 + szones := []SPrivateZone{} + for { + pageNumber++ + zones, err := client.DescribeZones(pageNumber, 20) + if err != nil { + return nil, errors.Wrapf(err, "client.DescribeZones(%d, 20)", pageNumber) + } + szones = append(szones, zones.Zones.Zone...) + if len(szones) >= zones.TotalItems { + break + } + } + return szones, nil +} + +func (client *SAliyunClient) GetAllZonesInfo() ([]SPrivateZone, error) { + spvtzs := []SPrivateZone{} + szones, err := client.GetAllZones() + if err != nil { + return nil, errors.Wrap(err, "client.GetAllZones()") + } + for i := 0; i < len(szones); i++ { + spvtz, err := client.DescribeZoneInfo(szones[i].ZoneID) + if err != nil { + return nil, errors.Wrapf(err, "client.DescribeZoneInfo(%s)", szones[i].ZoneID) + } + spvtzs = append(spvtzs, *spvtz) + } + return spvtzs, nil +} + +func (client *SAliyunClient) GetPrivateICloudDnsZones() ([]cloudprovider.ICloudDnsZone, error) { + izones := []cloudprovider.ICloudDnsZone{} + szones, err := client.GetAllZonesInfo() + if err != nil { + return nil, errors.Wrap(err, "client.GetAllZonesInfo()") + } + for i := 0; i < len(szones); i++ { + izones = append(izones, &szones[i]) + } + return izones, nil +} + +func (client *SAliyunClient) DescribeZoneInfo(zoneId string) (*SPrivateZone, error) { + params := map[string]string{} + params["Action"] = "DescribeZoneInfo" + params["ZoneId"] = zoneId + resp, err := client.pvtzRequest("DescribeZoneInfo", params) + if err != nil { + return nil, errors.Wrap(err, "DescribeZoneInfo") + } + sZone := &SPrivateZone{client: client} + err = resp.Unmarshal(sZone) + if err != nil { + return nil, errors.Wrap(err, "resp.Unmarshal") + } + return sZone, nil +} + +func (client *SAliyunClient) GetPrivateICloudDnsZoneById(id string) (cloudprovider.ICloudDnsZone, error) { + pvtzs, err := client.GetAllZones() + if err != nil { + return nil, errors.Wrap(err, "client.GetAllZones()") + } + index := -1 + for i := 0; i < len(pvtzs); i++ { + if pvtzs[i].ZoneID == id { + index = i + break + } + } + if index < 0 || index >= len(pvtzs) { + return nil, cloudprovider.ErrNotFound + } + izone, err := client.DescribeZoneInfo(id) + if err != nil { + return nil, errors.Wrapf(err, "client.DescribeZoneInfo(%s)", id) + } + return izone, nil +} + +func (client *SAliyunClient) DeleteZone(zoneId string) error { + params := map[string]string{} + params["Action"] = "DeleteZone" + params["ZoneId"] = zoneId + _, err := client.pvtzRequest("DeleteZone", params) + if err != nil { + return errors.Wrap(err, "DeleteZone") + } + return nil +} + +func (client *SAliyunClient) AddZone(zoneName string) (string, error) { + params := map[string]string{} + params["Action"] = "AddZone" + params["ZoneName"] = zoneName + ret, err := client.pvtzRequest("AddZone", params) + if err != nil { + return "", errors.Wrap(err, "AddZone") + } + zoneId := "" + return zoneId, ret.Unmarshal(&zoneId, "ZoneId") +} + +func (client *SAliyunClient) CreateZone(opts *cloudprovider.SDnsZoneCreateOptions) (*SPrivateZone, error) { + zoneId, err := client.AddZone(opts.Name) + if err != nil { + return nil, errors.Wrapf(err, "client.AddZone(%s)", opts.Name) + } + err = client.BindZoneVpcs(zoneId, opts.Vpcs) + if err != nil { + return nil, errors.Wrapf(err, " client.BindZoneVpcs(%s,%s)", zoneId, jsonutils.Marshal(opts.Vpcs).String()) + } + return client.DescribeZoneInfo(zoneId) +} + +func (client *SAliyunClient) CreatePrivateICloudDnsZone(opts *cloudprovider.SDnsZoneCreateOptions) (cloudprovider.ICloudDnsZone, error) { + izone, err := client.CreateZone(opts) + if err != nil { + return nil, errors.Wrapf(err, "client.CreateZone(%s)", jsonutils.Marshal(opts).String()) + } + return izone, nil +} + +func (client *SAliyunClient) BindZoneVpc(ZoneId string, vpc *cloudprovider.SPrivateZoneVpc) error { + params := map[string]string{} + params["Action"] = "BindZoneVpc" + params["ZoneId"] = ZoneId + params["Vpcs.1.RegionId"] = vpc.RegionId + params["Vpcs.1.VpcId"] = vpc.Id + _, err := client.pvtzRequest("BindZoneVpc", params) + if err != nil { + return errors.Wrap(err, "BindZoneVpc") + } + return nil +} + +func (client *SAliyunClient) BindZoneVpcs(zoneId string, vpc []cloudprovider.SPrivateZoneVpc) error { + params := map[string]string{} + params["Action"] = "BindZoneVpc" + params["ZoneId"] = zoneId + index := "" + for i := 0; i < len(vpc); i++ { + index = strconv.Itoa(i + 1) + params["Vpcs."+index+".RegionId"] = vpc[i].RegionId + params["Vpcs."+index+".VpcId"] = vpc[i].Id + } + _, err := client.pvtzRequest("BindZoneVpc", params) + if err != nil { + return errors.Wrap(err, "BindZoneVpc") + } + return nil +} + +func (client *SAliyunClient) UnBindZoneVpcs(zoneId string) error { + params := map[string]string{} + params["Action"] = "BindZoneVpc" + params["ZoneId"] = zoneId + _, err := client.pvtzRequest("BindZoneVpc", params) + if err != nil { + return errors.Wrap(err, "BindZoneVpc") + } + return nil +} + +func (self *SPrivateZone) GetId() string { + return self.ZoneID +} + +func (self *SPrivateZone) GetName() string { + return self.ZoneName +} + +func (self *SPrivateZone) GetGlobalId() string { + return self.GetId() +} + +func (self *SPrivateZone) GetStatus() string { + return api.DNS_ZONE_STATUS_AVAILABLE +} + +func (self *SPrivateZone) Refresh() error { + szone, err := self.client.DescribeZoneInfo(self.ZoneID) + if err != nil { + return errors.Wrapf(err, "self.client.DescribeZoneInfo(%s)", self.ZoneID) + } + return jsonutils.Update(self, szone) +} + +func (self *SPrivateZone) GetZoneType() cloudprovider.TDnsZoneType { + return cloudprovider.PrivateZone +} + +func (self *SPrivateZone) GetOptions() *jsonutils.JSONDict { + return nil +} + +func (self *SPrivateZone) GetICloudVpcIds() ([]string, error) { + var ret []string + for i := 0; i < len(self.BindVpcs.Vpc); i++ { + ret = append(ret, self.BindVpcs.Vpc[i].VpcID) + } + return ret, nil +} + +func (self *SPrivateZone) AddVpc(vpc *cloudprovider.SPrivateZoneVpc) error { + vpcs := []cloudprovider.SPrivateZoneVpc{} + for i := 0; i < len(self.BindVpcs.Vpc); i++ { + vpc := cloudprovider.SPrivateZoneVpc{} + vpc.Id = self.BindVpcs.Vpc[i].VpcID + vpc.RegionId = self.BindVpcs.Vpc[i].RegionID + vpcs = append(vpcs, vpc) + } + vpcs = append(vpcs, *vpc) + return self.client.BindZoneVpcs(self.ZoneID, vpcs) +} + +func (self *SPrivateZone) RemoveVpc(vpc *cloudprovider.SPrivateZoneVpc) error { + vpcs := []cloudprovider.SPrivateZoneVpc{} + for i := 0; i < len(self.BindVpcs.Vpc); i++ { + newVpc := cloudprovider.SPrivateZoneVpc{} + if self.BindVpcs.Vpc[i].VpcID == vpc.Id && self.BindVpcs.Vpc[i].RegionID == vpc.RegionId { + continue + } + newVpc.Id = self.BindVpcs.Vpc[i].VpcID + newVpc.RegionId = self.BindVpcs.Vpc[i].RegionID + vpcs = append(vpcs, newVpc) + } + return self.client.BindZoneVpcs(self.ZoneID, vpcs) +} + +func (self *SPrivateZone) GetIDnsRecordSets() ([]cloudprovider.ICloudDnsRecordSet, error) { + zonerecords, err := self.client.GetAllZoneRecords(self.ZoneID) + if err != nil { + return nil, errors.Wrapf(err, "self.client.GetAllZoneRecords(%s)", self.ZoneID) + } + result := []cloudprovider.ICloudDnsRecordSet{} + for i := 0; i < len(zonerecords); i++ { + zonerecords[i].szone = self + result = append(result, &zonerecords[i]) + } + return result, nil +} + +func (self *SPrivateZone) SyncDnsRecordSets(common, add, del, update []cloudprovider.DnsRecordSet) error { + for i := 0; i < len(del); i++ { + err := self.client.DeleteZoneRecord(del[i].ExternalId) + if err != nil { + return errors.Wrapf(err, "self.client.DeleteZoneRecord(%s)", del[i].ExternalId) + } + } + + for i := 0; i < len(add); i++ { + recordId, err := self.client.AddZoneRecord(self.ZoneID, add[i]) + if err != nil { + return errors.Wrapf(err, "self.client.AddZoneRecord(%s,%s)", self.ZoneID, jsonutils.Marshal(add[i]).String()) + } + if !add[i].Enabled { + err = self.client.SetZoneRecordStatus(recordId, "DISABLE") + if err != nil { + return errors.Wrapf(err, "self.client.SetZoneRecordStatus(%s,%t)", recordId, add[i].Enabled) + } + } + } + + for i := 0; i < len(update); i++ { + // ENABLE: 启用解析 DISABLE: 暂停解析 + status := "ENABLE" + if !update[i].Enabled { + status = "DISABLE" + } + err := self.client.SetZoneRecordStatus(update[i].ExternalId, status) + if err != nil { + return errors.Wrapf(err, "self.client.SetZoneRecordStatus(%s,%t)", update[i].ExternalId, update[i].Enabled) + } + err = self.client.UpdateZoneRecord(update[i]) + if err != nil { + return errors.Wrapf(err, "self.client.UpdateZoneRecord(%s)", jsonutils.Marshal(update[i]).String()) + } + } + + return nil +} + +func (self *SPrivateZone) Delete() error { + if len(self.BindVpcs.Vpc) > 0 { + err := self.client.UnBindZoneVpcs(self.ZoneID) + if err != nil { + return errors.Wrapf(err, "self.client.UnBindZoneVpcs(%s)", self.ZoneID) + } + } + return self.client.DeleteZone(self.ZoneID) +} + +func (self *SPrivateZone) GetDnsProductType() cloudprovider.TDnsProductType { + return "" +} + +func (self *SPrivateZone) GetProperlyTTL(ttl int64) int64 { + if ttl < allowedTtls[0] { + return allowedTtls[0] + } + for i := 0; i < len(allowedTtls)-1; i++ { + if ttl > allowedTtls[i] && ttl < allowedTtls[i+1] { + return allowedTtls[i] + } + } + return allowedTtls[len(allowedTtls)-1] +} diff --git a/pkg/multicloud/aliyun/private_zone_record.go b/pkg/multicloud/aliyun/private_zone_record.go new file mode 100644 index 0000000000..0f736370e2 --- /dev/null +++ b/pkg/multicloud/aliyun/private_zone_record.go @@ -0,0 +1,198 @@ +// 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 aliyun + +import ( + "strconv" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SPvtzRecords struct { + PageNumber int `json:"PageNumber"` + Records sPvtzRecords `json:"Records"` + PageSize int `json:"PageSize"` + // RequestID string `json:"RequestId"` + TotalItems int `json:"TotalItems"` + TotalPages int `json:"TotalPages"` +} + +type SPvtzRecord struct { + szone *SPrivateZone + Status string `json:"Status"` + Value string `json:"Value"` + Rr string `json:"Rr"` + RecordID int64 `json:"RecordId"` + TTL int64 `json:"Ttl"` + Type string `json:"Type"` + Priority int64 `json:"Priority"` +} + +type sPvtzRecords struct { + Record []SPvtzRecord `json:"Record"` +} + +// https://help.aliyun.com/document_detail/66252.html?spm=a2c4g.11186623.6.595.3f3f243fU1AjOV +func (client *SAliyunClient) DescribeZoneRecords(ZoneId string, pageNumber int, pageSize int) (SPvtzRecords, error) { + sRecords := SPvtzRecords{} + params := map[string]string{} + params["Action"] = "DescribeZoneRecords" + params["ZoneId"] = ZoneId + params["PageNumber"] = strconv.Itoa(pageNumber) + params["PageSize"] = strconv.Itoa(pageSize) + resp, err := client.pvtzRequest("DescribeZoneRecords", params) + if err != nil { + return sRecords, errors.Wrap(err, "DescribeZoneRecords") + } + err = resp.Unmarshal(&sRecords) + if err != nil { + return sRecords, errors.Wrap(err, "resp.Unmarshal") + } + return sRecords, nil +} + +func (client *SAliyunClient) GetAllZoneRecords(ZoneId string) ([]SPvtzRecord, error) { + count := 0 + pageNumber := 0 + srecords := []SPvtzRecord{} + + for { + pageNumber++ + records, err := client.DescribeZoneRecords(ZoneId, pageNumber, 20) + if err != nil { + return nil, errors.Wrapf(err, "client.DescribeZones(%d, 20)", count) + } + count += len(records.Records.Record) + + srecords = append(srecords, records.Records.Record...) + if count >= records.TotalItems { + break + } + } + return srecords, nil +} + +func (client *SAliyunClient) AddZoneRecord(ZoneId string, opts cloudprovider.DnsRecordSet) (string, error) { + params := map[string]string{} + params["Action"] = "AddZoneRecord" + params["Rr"] = opts.DnsName + params["Type"] = string(opts.DnsType) + params["Value"] = opts.DnsValue + params["ZoneId"] = ZoneId + params["Ttl"] = strconv.FormatInt(opts.Ttl, 10) + if opts.DnsType == cloudprovider.DnsTypeMX { + params["Priority"] = strconv.FormatInt(opts.MxPriority, 10) + } + ret, err := client.pvtzRequest("AddZoneRecord", params) + if err != nil { + return "", errors.Wrap(err, "AddZoneRecord") + } + recordId := "" + return recordId, ret.Unmarshal(&recordId, "RecordId") +} + +// https://help.aliyun.com/document_detail/66251.html?spm=a2c4g.11186623.6.596.15d55563zpsSWE +// status ENABLE: 启用解析 DISABLE: 暂停解析 +func (client *SAliyunClient) SetZoneRecordStatus(recordId string, status string) error { + params := map[string]string{} + params["Action"] = "SetZoneRecordStatus" + params["RecordId"] = recordId + params["Status"] = status + _, err := client.pvtzRequest("SetZoneRecordStatus", params) + if err != nil { + return errors.Wrap(err, "SetZoneRecordStatus") + } + return nil +} + +func (client *SAliyunClient) UpdateZoneRecord(opts cloudprovider.DnsRecordSet) error { + params := map[string]string{} + params["Action"] = "UpdateZoneRecord" + params["RecordId"] = opts.ExternalId + params["Rr"] = opts.DnsName + params["Type"] = string(opts.DnsType) + params["Value"] = opts.DnsValue + params["Ttl"] = strconv.FormatInt(opts.Ttl, 10) + if opts.DnsType == cloudprovider.DnsTypeMX { + params["Priority"] = strconv.FormatInt(opts.MxPriority, 10) + } + _, err := client.pvtzRequest("UpdateZoneRecord", params) + if err != nil { + return errors.Wrap(err, "UpdateZoneRecord") + } + return nil +} + +func (client *SAliyunClient) DeleteZoneRecord(RecordId string) error { + params := map[string]string{} + params["Action"] = "DeleteZoneRecord" + params["RecordId"] = RecordId + _, err := client.pvtzRequest("DeleteZoneRecord", params) + if err != nil { + return errors.Wrap(err, "DeleteZoneRecord") + } + return nil +} + +func (self *SPvtzRecord) GetGlobalId() string { + return strconv.FormatInt(self.RecordID, 10) +} + +func (self *SPvtzRecord) GetDnsName() string { + return self.Rr +} + +func (self *SPvtzRecord) GetStatus() string { + return api.DNS_ZONE_STATUS_AVAILABLE +} + +func (self *SPvtzRecord) GetEnabled() bool { + return self.Status == "ENABLE" +} + +func (self *SPvtzRecord) GetDnsType() cloudprovider.TDnsType { + return cloudprovider.TDnsType(self.Type) +} + +func (self *SPvtzRecord) GetDnsValue() string { + return self.Value +} + +func (self *SPvtzRecord) GetTTL() int64 { + return self.TTL +} + +func (self *SPvtzRecord) GetMxPriority() int64 { + if self.GetDnsType() == cloudprovider.DnsTypeMX { + return self.Priority + } + return 0 +} + +func (self *SPvtzRecord) GetPolicyType() cloudprovider.TDnsPolicyType { + return cloudprovider.DnsPolicyTypeSimple +} + +func (self *SPvtzRecord) GetPolicyValue() cloudprovider.TDnsPolicyValue { + return "" +} + +func (self *SPvtzRecord) GetPolicyOptions() *jsonutils.JSONDict { + return nil +} diff --git a/pkg/multicloud/aliyun/provider/provider.go b/pkg/multicloud/aliyun/provider/provider.go index 15ed20de91..4449d450bb 100644 --- a/pkg/multicloud/aliyun/provider/provider.go +++ b/pkg/multicloud/aliyun/provider/provider.go @@ -103,7 +103,7 @@ func (self *SAliyunProviderFactory) GetSupportedDnsPolicyTypes() map[cloudprovid } } -func (self *SAliyunProviderFactory) GetSupportedDnsValues() map[cloudprovider.TDnsPolicyType][]cloudprovider.TDnsPolicyValue { +func (self *SAliyunProviderFactory) GetSupportedDnsPolicyValues() map[cloudprovider.TDnsPolicyType][]cloudprovider.TDnsPolicyValue { return map[cloudprovider.TDnsPolicyType][]cloudprovider.TDnsPolicyValue{ cloudprovider.DnsPolicyTypeByCarrier: []cloudprovider.TDnsPolicyValue{ cloudprovider.DnsPolicyValueUnicom, @@ -122,6 +122,29 @@ func (self *SAliyunProviderFactory) GetSupportedDnsValues() map[cloudprovider.TD } } +func (self *SAliyunProviderFactory) GetTTLRange(zoneType cloudprovider.TDnsZoneType, productType cloudprovider.TDnsProductType) cloudprovider.TTlRange { + if zoneType == cloudprovider.PublicZone { + if len(productType) > 0 { + switch productType { + case cloudprovider.DnsProductEnterpriseUltimate: + return cloudprovider.TtlRangeAliyunEnterpriseUltimate + case cloudprovider.DnsProductEnterpriseStandard: + return cloudprovider.TtlRangeAliyunEnterpriseStandard + case cloudprovider.DnsProductPersonalProfessional: + return cloudprovider.TtlRangeAliyunPersonal + default: + return cloudprovider.TtlRangeAliyunFree + } + } + return cloudprovider.TtlRangeAliyunFree + } + + if zoneType == cloudprovider.PrivateZone { + return cloudprovider.TtlRangeAliyunPvtz + } + return cloudprovider.TTlRange{} +} + func (self *SAliyunProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, input cloudprovider.SCloudaccountCredential) (cloudprovider.SCloudaccount, error) { output := cloudprovider.SCloudaccount{} if len(input.AccessKeyId) == 0 { @@ -307,3 +330,47 @@ func (self *SAliyunProvider) GetSamlEntityId() string { func (self *SAliyunProvider) GetSamlSpInitiatedLoginUrl(idpName string) string { return "" } + +func (self *SAliyunProvider) GetICloudDnsZones() ([]cloudprovider.ICloudDnsZone, error) { + izones := []cloudprovider.ICloudDnsZone{} + privateZone, err := self.client.GetPrivateICloudDnsZones() + if err != nil { + return nil, errors.Wrap(err, "self.client.GetPrivateICloudDnsZones()") + } + publicZone, err := self.client.GetPublicICloudDnsZones() + if err != nil { + return nil, errors.Wrap(err, "self.client.GetPrivateICloudDnsZones()") + } + izones = append(izones, privateZone...) + izones = append(izones, publicZone...) + return izones, nil +} +func (self *SAliyunProvider) GetICloudDnsZoneById(id string) (cloudprovider.ICloudDnsZone, error) { + privateIzone, err := self.client.GetPrivateICloudDnsZoneById(id) + if err == nil { + return privateIzone, nil + } else { + if err != cloudprovider.ErrNotFound { + return nil, err + } + } + + publicIzone, err := self.client.GetPublicICloudDnsZoneById(id) + if err == nil { + return publicIzone, nil + } else { + if err != cloudprovider.ErrNotFound { + return nil, err + } + } + + return nil, cloudprovider.ErrNotFound +} + +func (self *SAliyunProvider) CreateICloudDnsZone(opts *cloudprovider.SDnsZoneCreateOptions) (cloudprovider.ICloudDnsZone, error) { + if opts.ZoneType == cloudprovider.PrivateZone { + return self.client.CreatePrivateICloudDnsZone(opts) + } else { + return self.client.CreatePublicICloudDnsZone(opts) + } +} diff --git a/pkg/multicloud/aliyun/shell/dns_domain.go b/pkg/multicloud/aliyun/shell/dns_domain.go new file mode 100644 index 0000000000..cb9dba0c03 --- /dev/null +++ b/pkg/multicloud/aliyun/shell/dns_domain.go @@ -0,0 +1,171 @@ +// 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 +// PageNumberations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud/aliyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type DnsProductListOptions struct { + PageNumber int `help:"page size"` + PageSize int `help:"page PageSize"` + } + shellutils.R(&DnsProductListOptions{}, "dnsproduct-list", "List dnsproduct", func(cli *aliyun.SRegion, args *DnsProductListOptions) error { + //products, e := cli.GetClient().DescribeDnsProductInstances(args.PageNumber, args.PageSize) + products, e := cli.GetClient().GetAllDnsProductInstances() + if e != nil { + return e + } + //printList(products.DNSProducts.DNSProduct, products.TotalCount, args.PageSize, args.PageNumber, []string{}) + printList(products, len(products), args.PageNumber, args.PageSize, []string{}) + return nil + }) + + type DomianListOptions struct { + PageNumber int `help:"page size"` + PageSize int `help:"page PageSize"` + } + shellutils.R(&DomianListOptions{}, "domain-list", "List Domain", func(cli *aliyun.SRegion, args *DomianListOptions) error { + sdomains, e := cli.GetClient().DescribeDomains(args.PageNumber, args.PageSize) + if e != nil { + return e + } + printList(sdomains.Domains.Domain, sdomains.TotalCount, args.PageNumber, args.PageSize, []string{}) + return nil + }) + + type SDomainCreateOptions struct { + DOMAINNAME string `help:"Domain name"` + } + shellutils.R(&SDomainCreateOptions{}, "domain-create", "Create domain", func(cli *aliyun.SRegion, args *SDomainCreateOptions) error { + hostzones, err := cli.GetClient().AddDomain(args.DOMAINNAME) + if err != nil { + return err + } + printObject(hostzones) + return nil + }) + + type SDomainDeleteOptions struct { + DOMAINNAME string + } + shellutils.R(&SDomainDeleteOptions{}, "domain-delete", "delete domain", func(cli *aliyun.SRegion, args *SDomainDeleteOptions) error { + err := cli.GetClient().DeleteDomain(args.DOMAINNAME) + if err != nil { + return err + } + return nil + }) + + type SDomainShowOptions struct { + DOMAINNAME string + } + shellutils.R(&SDomainShowOptions{}, "domain-show", "Show domain", func(cli *aliyun.SRegion, args *SDomainShowOptions) error { + szone, e := cli.GetClient().DescribeDomainInfo(args.DOMAINNAME) + if e != nil { + return e + } + printObject(szone) + return nil + }) + + type SDomainRecordListOptions struct { + DOMAINNAME string + PageNumber int `help:"page size"` + PageSize int `help:"page PageSize"` + } + shellutils.R(&SDomainRecordListOptions{}, "domainrecord-list", "List domainrecord", func(cli *aliyun.SRegion, args *SDomainRecordListOptions) error { + srecords, e := cli.GetClient().DescribeDomainRecords(args.DOMAINNAME, args.PageNumber, args.PageSize) + if e != nil { + return e + } + printList(srecords.DomainRecords.Record, srecords.TotalCount, args.PageNumber, args.PageSize, []string{}) + return nil + }) + + type DomainRecordCreateOptions struct { + DOMAINNAME string + NAME string + VALUE string `help:"dns record value"` + TTL int64 `help:"ttl"` + TYPE string `help:"dns type"` + PolicyType string `help:"PolicyType"` + PolicyValue string + } + shellutils.R(&DomainRecordCreateOptions{}, "domainrecord-create", "create domainrecord", func(cli *aliyun.SRegion, args *DomainRecordCreateOptions) error { + opts := cloudprovider.DnsRecordSet{} + opts.DnsName = args.NAME + opts.DnsType = cloudprovider.TDnsType(args.TYPE) + opts.DnsValue = args.VALUE + opts.Ttl = args.TTL + opts.PolicyType = cloudprovider.TDnsPolicyType(args.PolicyType) + + _, err := cli.GetClient().AddDomainRecord(args.DOMAINNAME, opts) + if err != nil { + return err + } + return nil + }) + + type DomainRecordupdateOptions struct { + DOMAINRECORDID string `help:"DOMAINRECORDID"` + NAME string `help:"Domain name"` + VALUE string `help:"dns record value"` + TTL int64 `help:"ttl"` + TYPE string `help:"dns type"` + PolicyType string `help:"PolicyType"` + } + shellutils.R(&DomainRecordupdateOptions{}, "domainrecord-update", "update domainrecord", func(cli *aliyun.SRegion, args *DomainRecordupdateOptions) error { + opts := cloudprovider.DnsRecordSet{} + opts.DnsName = args.NAME + opts.DnsType = cloudprovider.TDnsType(args.TYPE) + opts.DnsValue = args.VALUE + opts.Ttl = args.TTL + opts.ExternalId = args.DOMAINRECORDID + opts.PolicyType = cloudprovider.TDnsPolicyType(args.PolicyType) + err := cli.GetClient().UpdateDomainRecord(opts) + if err != nil { + return err + } + return nil + }) + + type DomainRecordDeleteOptions struct { + DOMAINRECORDID string `help:"DOMAINRECORDID"` + } + shellutils.R(&DomainRecordDeleteOptions{}, "domainrecord-delete", "delete domainrecord", func(cli *aliyun.SRegion, args *DomainRecordDeleteOptions) error { + err := cli.GetClient().DeleteDomainRecord(args.DOMAINRECORDID) + if err != nil { + return err + } + return nil + }) + + type DomainRecordSetStatusOptions struct { + DOMAINRECORDID string `help:"PRIVATEZONEID"` + STATUS string `choices:"Enable|Disable"` + } + shellutils.R(&DomainRecordSetStatusOptions{}, "domainrecord-setstatus", "set domainrecord status", func(cli *aliyun.SRegion, args *DomainRecordSetStatusOptions) error { + + err := cli.GetClient().SetDomainRecordStatus(args.DOMAINRECORDID, args.STATUS) + if err != nil { + return err + } + return nil + }) +} diff --git a/pkg/multicloud/aliyun/shell/private_zone.go b/pkg/multicloud/aliyun/shell/private_zone.go new file mode 100644 index 0000000000..5cbc1293a8 --- /dev/null +++ b/pkg/multicloud/aliyun/shell/private_zone.go @@ -0,0 +1,193 @@ +// 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 +// PageSizeations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud/aliyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type PrivateZoneListOptions struct { + PageSize int `help:"page size"` + PageNumber int `help:"page PageNumber"` + } + shellutils.R(&PrivateZoneListOptions{}, "privatezone-list", "List privatezone", func(cli *aliyun.SRegion, args *PrivateZoneListOptions) error { + szones, e := cli.GetClient().DescribeZones(args.PageNumber, args.PageSize) + if e != nil { + return e + } + printList(szones.Zones.Zone, szones.TotalItems, args.PageNumber, args.PageSize, []string{}) + return nil + }) + + type PrivateZoneCreateOptions struct { + NAME string `help:"Domain name"` + Type string `choices:"PublicZone|PrivateZone"` + Vpc string `help:"vpc id"` + Region string `help:"region id"` + } + shellutils.R(&PrivateZoneCreateOptions{}, "privatezone-create", "Create privatezone", func(cli *aliyun.SRegion, args *PrivateZoneCreateOptions) error { + opts := cloudprovider.SDnsZoneCreateOptions{} + opts.Name = args.NAME + opts.ZoneType = cloudprovider.TDnsZoneType(args.Type) + if len(args.Vpc) > 0 && len(args.Region) > 0 { + vpc := cloudprovider.SPrivateZoneVpc{} + vpc.Id = args.Vpc + vpc.RegionId = args.Region + opts.Vpcs = []cloudprovider.SPrivateZoneVpc{vpc} + } + hostzones, err := cli.GetClient().CreateZone(&opts) + if err != nil { + return err + } + printObject(hostzones) + return nil + }) + + type PrivateZoneDeleteOptions struct { + PRIVATEZONEID string + } + shellutils.R(&PrivateZoneDeleteOptions{}, "privatezone-delete", "delete privatezone", func(cli *aliyun.SRegion, args *PrivateZoneDeleteOptions) error { + err := cli.GetClient().DeleteZone(args.PRIVATEZONEID) + if err != nil { + return err + } + return nil + }) + + type PrivateZoneShowOptions struct { + ID string `help:"ID or name of privatezone"` + } + shellutils.R(&PrivateZoneShowOptions{}, "privatezone-show", "Show privatezone", func(cli *aliyun.SRegion, args *PrivateZoneShowOptions) error { + szone, e := cli.GetClient().DescribeZoneInfo(args.ID) + if e != nil { + return e + } + printObject(szone) + return nil + }) + + type PrivateZoneAddVpcOptions struct { + PRIVATEZONEID string + VPC string + REGION string + } + shellutils.R(&PrivateZoneAddVpcOptions{}, "privatezone-add-vpc", "associate vpc with privatezone", func(cli *aliyun.SRegion, args *PrivateZoneAddVpcOptions) error { + vpc := cloudprovider.SPrivateZoneVpc{} + vpc.Id = args.VPC + vpc.RegionId = args.REGION + err := cli.GetClient().BindZoneVpc(args.PRIVATEZONEID, &vpc) + if err != nil { + return err + } + return nil + }) + + type PrivateZoneDeleteVpcsOptions struct { + PRIVATEZONEID string + } + shellutils.R(&PrivateZoneDeleteVpcsOptions{}, "privatezone-delete-vpc", "delete vpc with privatezone", func(cli *aliyun.SRegion, args *PrivateZoneDeleteVpcsOptions) error { + err := cli.GetClient().UnBindZoneVpcs(args.PRIVATEZONEID) + if err != nil { + return err + } + return nil + }) + + type PrivateZoneRecordListOptions struct { + ID string `help:"ID or name of privatezone"` + PageSize int `help:"page size"` + PageNumber int `help:"page PageNumber"` + } + shellutils.R(&PrivateZoneRecordListOptions{}, "privatezonerecord-list", "List privatezonerecord", func(cli *aliyun.SRegion, args *PrivateZoneRecordListOptions) error { + srecords, e := cli.GetClient().DescribeZoneRecords(args.ID, args.PageNumber, args.PageSize) + if e != nil { + return e + } + printList(srecords.Records.Record, srecords.TotalItems, args.PageNumber, args.PageSize, []string{}) + return nil + }) + + type PvtzRecordCreateOptions struct { + PRIVATEZONEID string `help:"PRIVATEZONEID"` + NAME string `help:"Domain name"` + VALUE string `help:"dns record value"` + TTL int64 `help:"ttl"` + TYPE string `help:"dns type"` + PolicyType string `help:"PolicyType"` + Identify string `help:"Identify"` + } + shellutils.R(&PvtzRecordCreateOptions{}, "privatezonerecord-create", "create privatezonerecord", func(cli *aliyun.SRegion, args *PvtzRecordCreateOptions) error { + opts := cloudprovider.DnsRecordSet{} + opts.DnsName = args.NAME + opts.DnsType = cloudprovider.TDnsType(args.TYPE) + opts.DnsValue = args.VALUE + opts.Ttl = args.TTL + opts.ExternalId = args.Identify + _, err := cli.GetClient().AddZoneRecord(args.PRIVATEZONEID, opts) + if err != nil { + return err + } + return nil + }) + + type PvtzRecordupdateOptions struct { + PRIVATEZONEID string `help:"PRIVATEZONEID"` + NAME string `help:"Domain name"` + VALUE string `help:"dns record value"` + TTL int64 `help:"ttl"` + TYPE string `help:"dns type"` + Identify string `help:"Identify"` + } + shellutils.R(&PvtzRecordupdateOptions{}, "privatezonerecord-update", "update privatezonerecord", func(cli *aliyun.SRegion, args *PvtzRecordupdateOptions) error { + opts := cloudprovider.DnsRecordSet{} + opts.DnsName = args.NAME + opts.DnsType = cloudprovider.TDnsType(args.TYPE) + opts.DnsValue = args.VALUE + opts.Ttl = args.TTL + opts.ExternalId = args.Identify + err := cli.GetClient().UpdateZoneRecord(opts) + if err != nil { + return err + } + return nil + }) + + type PvtzRecordDeleteOptions struct { + PRIVATEZONERECORDID string `help:"PRIVATEZONEID"` + } + shellutils.R(&PvtzRecordDeleteOptions{}, "privatezonerecord-delete", "delete privatezonerecord", func(cli *aliyun.SRegion, args *PvtzRecordDeleteOptions) error { + err := cli.GetClient().DeleteZoneRecord(args.PRIVATEZONERECORDID) + if err != nil { + return err + } + return nil + }) + + type PvtzRecordSetStatusOptions struct { + PRIVATEZONERECORDID string `help:"PRIVATEZONEID"` + STATUS string `choices:"ENABLE|DISABLE"` + } + shellutils.R(&PvtzRecordSetStatusOptions{}, "privatezonerecord-setstatus", "set privatezonerecord status", func(cli *aliyun.SRegion, args *PvtzRecordSetStatusOptions) error { + + err := cli.GetClient().SetZoneRecordStatus(args.PRIVATEZONERECORDID, args.STATUS) + if err != nil { + return err + } + return nil + }) +} diff --git a/pkg/multicloud/aws/dnshostedzone.go b/pkg/multicloud/aws/dnshostedzone.go index b749850496..3c2000567e 100644 --- a/pkg/multicloud/aws/dnshostedzone.go +++ b/pkg/multicloud/aws/dnshostedzone.go @@ -17,6 +17,7 @@ package aws import ( "time" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/route53" "yunion.io/x/jsonutils" @@ -72,6 +73,33 @@ func (self *SHostedZone) Refresh() error { return jsonutils.Update(self, hostedZone) } +func (client *SAwsClient) ListGeoLocations() ([]*route53.GeoLocationDetails, error) { + s, err := client.getAwsRoute53Session() + if err != nil { + return nil, errors.Wrap(err, "region.getAwsRoute53Session()") + } + route53Client := route53.New(s) + locations := []*route53.GeoLocationDetails{} + params := route53.ListGeoLocationsInput{} + max := "100" + for { + params.MaxItems = &max + ret, err := route53Client.ListGeoLocations(¶ms) + if err != nil { + return nil, errors.Wrapf(err, "route53Client.ListGeoLocations(%s)", jsonutils.Marshal(params).String()) + } + locations = append(locations, ret.GeoLocationDetailsList...) + if ret.IsTruncated != nil && !*ret.IsTruncated { + break + } + + params.StartContinentCode = ret.NextContinentCode + params.StartCountryCode = ret.NextCountryCode + params.StartSubdivisionCode = ret.NextSubdivisionCode + } + return locations, nil +} + func (client *SAwsClient) CreateHostedZone(opts *cloudprovider.SDnsZoneCreateOptions) (*SHostedZone, error) { s, err := client.getAwsRoute53Session() if err != nil { @@ -225,6 +253,11 @@ func (client *SAwsClient) GetHostedZoneById(ID string) (*SHostedZone, error) { params.Id = &ID ret, err := route53Client.GetHostedZone(¶ms) if err != nil { + if aerr, ok := err.(awserr.Error); ok { + if aerr.Code() == route53.ErrCodeNoSuchHostedZone { + return nil, errors.Wrap(cloudprovider.ErrNotFound, err.Error()) + } + } return nil, errors.Wrap(err, "route53Client.GetHostedZone()") } @@ -341,18 +374,18 @@ func (self *SHostedZone) GetIDnsRecordSets() ([]cloudprovider.ICloudDnsRecordSet } func (self *SHostedZone) SyncDnsRecordSets(common, add, del, update []cloudprovider.DnsRecordSet) error { - for i := 0; i < len(add); i++ { - err := self.AddDnsRecordSet(&add[i]) - if err != nil { - return errors.Wrap(err, "self.AddDnsRecordSet()") - } - } for i := 0; i < len(del); i++ { err := self.RemoveDnsRecordSet(&del[i]) if err != nil { return errors.Wrap(err, "self.RemoveDnsRecordSet()") } } + for i := 0; i < len(add); i++ { + err := self.AddDnsRecordSet(&add[i]) + if err != nil { + return errors.Wrap(err, "self.AddDnsRecordSet()") + } + } for i := 0; i < len(update); i++ { err := self.UpdateDnsRecordSet(&update[i]) if err != nil { @@ -388,3 +421,7 @@ func (self *SHostedZone) RemoveDnsRecordSet(opts *cloudprovider.DnsRecordSet) er } return self.client.RemoveDnsRecordSet(self.ID, opts) } + +func (self *SHostedZone) GetDnsProductType() cloudprovider.TDnsProductType { + return "" +} diff --git a/pkg/multicloud/aws/dnsrecordset.go b/pkg/multicloud/aws/dnsrecordset.go index f0fa4847bc..2d62e00dc1 100644 --- a/pkg/multicloud/aws/dnsrecordset.go +++ b/pkg/multicloud/aws/dnsrecordset.go @@ -22,7 +22,9 @@ import ( "github.com/aws/aws-sdk-go/service/route53" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/stringutils" "yunion.io/x/onecloud/pkg/cloudprovider" ) @@ -145,7 +147,7 @@ func (client *SAwsClient) ChangeResourceRecordSets(action string, hostedZoneId s return nil } -func Getroute53ResourceRecordSet(opts *cloudprovider.DnsRecordSet) (*route53.ResourceRecordSet, error) { +func Getroute53ResourceRecordSet(client *SAwsClient, opts *cloudprovider.DnsRecordSet) (*route53.ResourceRecordSet, error) { resourceRecordSet := route53.ResourceRecordSet{} resourceRecordSet.SetName(opts.DnsName) resourceRecordSet.SetTTL(opts.Ttl) @@ -154,9 +156,16 @@ func Getroute53ResourceRecordSet(opts *cloudprovider.DnsRecordSet) (*route53.Res resourceRecordSet.SetSetIdentifier(opts.ExternalId) } records := []*route53.ResourceRecord{} - values := strings.Split(opts.DnsValue, " ") + values := strings.Split(opts.DnsValue, "\n") for i := 0; i < len(values); i++ { - records = append(records, &route53.ResourceRecord{Value: &values[i]}) + value := values[i] + if opts.DnsType == cloudprovider.DnsTypeTXT || opts.DnsType == cloudprovider.DnsTypeSPF { + value = "\"" + value + "\"" + } + if opts.DnsType == cloudprovider.DnsTypeMX { + value = strconv.FormatInt(opts.MxPriority, 10) + " " + value + } + records = append(records, &route53.ResourceRecord{Value: &value}) } resourceRecordSet.SetResourceRecords(records) @@ -164,7 +173,11 @@ func Getroute53ResourceRecordSet(opts *cloudprovider.DnsRecordSet) (*route53.Res if opts.PolicyType == cloudprovider.DnsPolicyTypeSimple || opts.PolicyValue == cloudprovider.DnsPolicyValueEmpty { return &resourceRecordSet, nil } - + // SetIdentifier 设置policy需要 ,也可以通过externalId设置 + if resourceRecordSet.SetIdentifier == nil { + resourceRecordSet.SetSetIdentifier(stringutils.UUID4()) + } + // addition option(health check) if opts.PolicyOptions != nil { health := struct { HealthCheckId string @@ -175,27 +188,51 @@ func Getroute53ResourceRecordSet(opts *cloudprovider.DnsRecordSet) (*route53.Res } } + // failover choice:PRIMARY|SECONDARY if opts.PolicyType == cloudprovider.DnsPolicyTypeFailover { resourceRecordSet.SetFailover(string(opts.PolicyValue)) } + // geolocation if opts.PolicyType == cloudprovider.DnsPolicyTypeByGeoLocation { - /* - sGeo := SGeoLocationCode{} - err := opts.PolicyParams.Unmarshal(&sGeo, "location") - if err != nil { - return nil, errors.Wrapf(err, "%s Unmarshal(location)", fmt.Sprintln(opts.PolicyParams)) + Geo := route53.GeoLocation{} + locations, err := client.ListGeoLocations() + if err != nil { + return nil, errors.Wrap(err, "client.ListGeoLocations()") + } + matchedIndex := -1 + for i := 0; i < len(locations); i++ { + if locations[i].SubdivisionName != nil { + if string(opts.PolicyValue) == *locations[i].SubdivisionName { + matchedIndex = i + break + } } - Geo := route53.GeoLocation{} - Geo.ContinentCode = &sGeo.ContinentCode - Geo.CountryCode = &sGeo.CountryCode - Geo.SubdivisionCode = &sGeo.SubdivisionCode - resourceRecordSet.SetGeoLocation(&Geo) - */ + if locations[i].CountryName != nil { + if string(opts.PolicyValue) == *locations[i].CountryName { + matchedIndex = i + break + } + } + if locations[i].ContinentCode != nil { + if string(opts.PolicyValue) == *locations[i].ContinentCode { + matchedIndex = i + break + } + } + } + if matchedIndex < 0 || matchedIndex >= len(locations) { + return nil, errors.Wrap(cloudprovider.ErrNotSupported, "Can't find Support for this location") + } + Geo.ContinentCode = locations[matchedIndex].ContinentCode + Geo.CountryCode = locations[matchedIndex].CountryCode + Geo.SubdivisionCode = locations[matchedIndex].SubdivisionCode + resourceRecordSet.SetGeoLocation(&Geo) } - + // latency ,region based if opts.PolicyType == cloudprovider.DnsPolicyTypeLatency { resourceRecordSet.SetRegion(string(opts.PolicyValue)) } + // MultiValueAnswer ,bool if opts.PolicyType == cloudprovider.DnsPolicyTypeMultiValueAnswer { var multiValueAnswer bool = true if string(opts.PolicyValue) == "false" { @@ -203,16 +240,17 @@ func Getroute53ResourceRecordSet(opts *cloudprovider.DnsRecordSet) (*route53.Res } resourceRecordSet.SetMultiValueAnswer(multiValueAnswer) } + // Weighted.,int64 value if opts.PolicyType == cloudprovider.DnsPolicyTypeWeighted { - weight, _ := strconv.Atoi(opts.DnsValue) + weight, _ := strconv.Atoi(string(opts.PolicyValue)) resourceRecordSet.SetWeight(int64(weight)) } - return &resourceRecordSet, nil + return &resourceRecordSet, nil } func (client *SAwsClient) AddDnsRecordSet(hostedZoneId string, opts *cloudprovider.DnsRecordSet) error { - resourceRecordSet, err := Getroute53ResourceRecordSet(opts) + resourceRecordSet, err := Getroute53ResourceRecordSet(client, opts) if err != nil { return errors.Wrapf(err, "Getroute53ResourceRecordSet(%s)", fmt.Sprintln(opts)) } @@ -224,7 +262,7 @@ func (client *SAwsClient) AddDnsRecordSet(hostedZoneId string, opts *cloudprovid } func (client *SAwsClient) UpdateDnsRecordSet(hostedZoneId string, opts *cloudprovider.DnsRecordSet) error { - resourceRecordSet, err := Getroute53ResourceRecordSet(opts) + resourceRecordSet, err := Getroute53ResourceRecordSet(client, opts) if err != nil { return errors.Wrapf(err, "Getroute53ResourceRecordSet(%s)", fmt.Sprintln(opts)) } @@ -286,15 +324,40 @@ func (self *SdnsRecordSet) GetDnsType() cloudprovider.TDnsType { func (self *SdnsRecordSet) GetDnsValue() string { var records []string for i := 0; i < len(self.ResourceRecords); i++ { - records = append(records, self.ResourceRecords[i].Value) + value := self.ResourceRecords[i].Value + if self.Type == "TXT" || self.Type == "SPF" { + value = value[1 : len(value)-1] + } + if self.Type == "MX" { + strs := strings.Split(value, " ") + if len(strs) >= 2 { + value = strs[1] + } + } + records = append(records, value) } - return strings.Join(records, " ") + return strings.Join(records, "\n") } func (self *SdnsRecordSet) GetTTL() int64 { return self.TTL } +func (self *SdnsRecordSet) GetMxPriority() int64 { + if self.GetDnsType() != cloudprovider.DnsTypeMX { + return 0 + } + strs := strings.Split(self.GetDnsValue(), " ") + if len(strs) > 0 { + mx, err := strconv.ParseInt(strs[0], 10, 64) + if err == nil { + return mx + } + } + log.Errorf("can't parse mxpriority:%s", self.GetDnsValue()) + return 0 +} + // trafficpolicy 信息 func (self *SdnsRecordSet) GetPolicyType() cloudprovider.TDnsPolicyType { /* @@ -332,12 +395,39 @@ func (self *SdnsRecordSet) GetPolicyOptions() *jsonutils.JSONDict { return options } +func CodeMatch(s string, d *string) bool { + if d == nil { + return len(s) == 0 + } + return s == *d +} + func (self *SdnsRecordSet) GetPolicyValue() cloudprovider.TDnsPolicyValue { if len(self.Failover) > 0 { return cloudprovider.TDnsPolicyValue(self.Failover) } if self.GeoLocation != nil { - return cloudprovider.TDnsPolicyValue(jsonutils.Marshal(self.GeoLocation).String()) + locations, err := self.hostedZone.client.ListGeoLocations() + log.Errorf("List aws route53 locations failed!") + if err != nil { + return "" + } + for i := 0; i < len(locations); i++ { + if CodeMatch(self.GeoLocation.SubdivisionCode, locations[i].SubdivisionCode) && + CodeMatch(self.GeoLocation.CountryCode, locations[i].CountryCode) && + CodeMatch(self.GeoLocation.ContinentCode, locations[i].ContinentCode) { + if locations[i].SubdivisionCode != nil { + return cloudprovider.TDnsPolicyValue(*locations[i].SubdivisionName) + } + if locations[i].CountryCode != nil { + return cloudprovider.TDnsPolicyValue(*locations[i].CountryName) + } + if locations[i].ContinentCode != nil { + return cloudprovider.TDnsPolicyValue(*locations[i].ContinentName) + } + } + } + return "" } if len(self.Region) > 0 { return cloudprovider.TDnsPolicyValue(self.Region) diff --git a/pkg/multicloud/aws/provider/provider.go b/pkg/multicloud/aws/provider/provider.go index 0f8bfdc07f..8859a53eb3 100644 --- a/pkg/multicloud/aws/provider/provider.go +++ b/pkg/multicloud/aws/provider/provider.go @@ -115,8 +115,13 @@ func (self *SAwsProviderFactory) GetSupportedDnsPolicyTypes() map[cloudprovider. } } -func (self *SAwsProviderFactory) GetSupportedDnsPolicyTypeValues() map[cloudprovider.TDnsPolicyType][]cloudprovider.TDnsPolicyValue { - return map[cloudprovider.TDnsPolicyType][]cloudprovider.TDnsPolicyValue{} +func (self *SAwsProviderFactory) GetSupportedDnsPolicyValues() map[cloudprovider.TDnsPolicyType][]cloudprovider.TDnsPolicyValue { + return map[cloudprovider.TDnsPolicyType][]cloudprovider.TDnsPolicyValue{ + cloudprovider.DnsPolicyTypeByGeoLocation: cloudprovider.AwsGeoLocations, + cloudprovider.DnsPolicyTypeLatency: cloudprovider.AwsRegions, + cloudprovider.DnsPolicyTypeFailover: cloudprovider.AwsFailovers, + cloudprovider.DnsPolicyTypeMultiValueAnswer: cloudprovider.AwsMultiValueAnswers, + } } func (self *SAwsProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, input cloudprovider.SCloudaccountCredential) (cloudprovider.SCloudaccount, error) { diff --git a/pkg/multicloud/aws/shell/hostedzone.go b/pkg/multicloud/aws/shell/hostedzone.go index 9a2d80e55a..516c3d1be0 100644 --- a/pkg/multicloud/aws/shell/hostedzone.go +++ b/pkg/multicloud/aws/shell/hostedzone.go @@ -21,6 +21,16 @@ import ( ) func init() { + type Route53LocationListOptions struct{} + shellutils.R(&Route53LocationListOptions{}, "route53location-list", "List route53location", func(cli *aws.SRegion, args *Route53LocationListOptions) error { + locations, err := cli.GetClient().ListGeoLocations() + if err != nil { + return err + } + printList(locations, len(locations), 0, 20, []string{}) + return nil + }) + type HostedZoneListOptions struct{} shellutils.R(&HostedZoneListOptions{}, "hostedzone-list", "List hostedzone", func(cli *aws.SRegion, args *HostedZoneListOptions) error { hostzones, err := cli.GetClient().GetHostedZones() @@ -54,7 +64,18 @@ func init() { printObject(hostzones) return nil }) + type HostedZoneGetOptions struct { + HOSTEDZONEID string + } + shellutils.R(&HostedZoneGetOptions{}, "hostedzone-show", "get hostedzone by id", func(cli *aws.SRegion, args *HostedZoneGetOptions) error { + hostedzone, err := cli.GetClient().GetHostedZoneById(args.HOSTEDZONEID) + if err != nil { + return err + } + printObject(hostedzone) + return nil + }) type HostedZoneAddVpcOptions struct { HOSTEDZONEID string VPC string diff --git a/pkg/multicloud/qcloud/dnspod_domain.go b/pkg/multicloud/qcloud/dnspod_domain.go index f37d990ffb..87f3309177 100644 --- a/pkg/multicloud/qcloud/dnspod_domain.go +++ b/pkg/multicloud/qcloud/dnspod_domain.go @@ -248,10 +248,19 @@ func (self *SDomian) AddDnsRecordSet(opts *cloudprovider.DnsRecordSet) error { values := strings.Split(opts.DnsValue, "*") for i := 0; i < len(values); i++ { opts.DnsValue = values[i] - err := self.client.CreateDnsRecord(opts, self.Name) + recordId, err := self.client.CreateDnsRecord(opts, self.Name) if err != nil { return errors.Wrapf(err, "self.client.CreateDnsRecord(%s, %s)", fmt.Sprintln(opts), self.Name) } + opts.ExternalId = recordId + if !opts.Enabled { + status := "disable" + err = self.client.ModifyRecordStatus(status, opts.ExternalId, self.Name) + if err != nil { + return errors.Wrapf(err, "self.client.ModifyRecordStatus(%s,%s,%s)", status, opts.ExternalId, self.Name) + } + } + } return nil } @@ -264,6 +273,14 @@ func (self *SDomian) UpdateDnsRecordSet(opts *cloudprovider.DnsRecordSet) error if err != nil { return errors.Wrapf(err, "self.client.CreateDnsRecord(%s, %s)", fmt.Sprintln(opts), self.Name) } + status := "enable" + if !opts.Enabled { + status = "disable" + } + err = self.client.ModifyRecordStatus(status, opts.ExternalId, self.Name) + if err != nil { + return errors.Wrapf(err, "self.client.ModifyRecordStatus(%s,%s,%s)", status, opts.ExternalId, self.Name) + } } return nil } @@ -281,18 +298,20 @@ func (self *SDomian) RemoveDnsRecordSet(opts *cloudprovider.DnsRecordSet) error } func (self *SDomian) SyncDnsRecordSets(common, add, del, update []cloudprovider.DnsRecordSet) error { - for i := 0; i < len(add); i++ { - err := self.AddDnsRecordSet(&add[i]) - if err != nil { - return errors.Wrapf(err, "self.AddDnsRecordSet(%s)", fmt.Sprintln(add[i])) - } - } for i := 0; i < len(del); i++ { err := self.RemoveDnsRecordSet(&del[i]) if err != nil { return errors.Wrapf(err, "self.RemoveDnsRecordSet(%s)", fmt.Sprintln(del[i])) } } + + for i := 0; i < len(add); i++ { + err := self.AddDnsRecordSet(&add[i]) + if err != nil { + return errors.Wrapf(err, "self.AddDnsRecordSet(%s)", fmt.Sprintln(add[i])) + } + } + for i := 0; i < len(update); i++ { err := self.UpdateDnsRecordSet(&update[i]) if err != nil { @@ -301,3 +320,20 @@ func (self *SDomian) SyncDnsRecordSets(common, add, del, update []cloudprovider. } return nil } + +func (self *SDomian) GetDnsProductType() cloudprovider.TDnsProductType { + switch self.GradeTitle { + case "企业旗舰版": + return cloudprovider.DnsProductEnterpriseUltimate + case "企业标准版": + return cloudprovider.DnsProductEnterpriseStandard + case "企业基础版": + return cloudprovider.DnsProductEnterpriseBasic + case "个人专业版": + return cloudprovider.DnsProductPersonalProfessional + case "免费版": + return cloudprovider.DnsProductFree + default: + return cloudprovider.DnsProductFree + } +} diff --git a/pkg/multicloud/qcloud/dnspod_record.go b/pkg/multicloud/qcloud/dnspod_record.go index c0767140a5..ed455838a4 100644 --- a/pkg/multicloud/qcloud/dnspod_record.go +++ b/pkg/multicloud/qcloud/dnspod_record.go @@ -26,6 +26,13 @@ import ( "yunion.io/x/onecloud/pkg/cloudprovider" ) +type SRecordCreateRet struct { + ID string `json:"id"` + Name string `json:"name"` + Status string `json:"status"` + Weight int `json:"weight"` +} + type SRecordCountInfo struct { RecordTotal string `json:"record_total"` RecordsNum string `json:"records_num"` @@ -46,7 +53,7 @@ type SDnsRecord struct { LineID string `json:"line_id"` Type string `json:"type"` Remark string `json:"remark"` - Mx int `json:"mx"` + Mx int64 `json:"mx"` Hold string `json:"hold"` } @@ -131,7 +138,7 @@ func GetRecordLineLineType(policyinfo cloudprovider.TDnsPolicyValue) string { } // https://cloud.tencent.com/document/api/302/8516 -func (client *SQcloudClient) CreateDnsRecord(opts *cloudprovider.DnsRecordSet, domainName string) error { +func (client *SQcloudClient) CreateDnsRecord(opts *cloudprovider.DnsRecordSet, domainName string) (string, error) { params := map[string]string{} recordline := GetRecordLineLineType(opts.PolicyValue) if opts.Ttl < 600 { @@ -149,11 +156,19 @@ func (client *SQcloudClient) CreateDnsRecord(opts *cloudprovider.DnsRecordSet, d params["ttl"] = strconv.FormatInt(opts.Ttl, 10) params["value"] = opts.DnsValue params["recordLine"] = recordline - _, err := client.cnsRequest("RecordCreate", params) - if err != nil { - return errors.Wrapf(err, "client.cnsRequest(RecordCreate, %s)", fmt.Sprintln(params)) + if opts.DnsType == cloudprovider.DnsTypeMX { + params["mx"] = strconv.FormatInt(opts.MxPriority, 10) } - return nil + resp, err := client.cnsRequest("RecordCreate", params) + if err != nil { + return "", errors.Wrapf(err, "client.cnsRequest(RecordCreate, %s)", fmt.Sprintln(params)) + } + SRecordCreateRet := SRecordCreateRet{} + err = resp.Unmarshal(&SRecordCreateRet, "record") + if err != nil { + return "", errors.Wrapf(err, "%s.Unmarshal(records)", fmt.Sprintln(resp)) + } + return SRecordCreateRet.ID, nil } // https://cloud.tencent.com/document/product/302/8511 @@ -177,6 +192,9 @@ func (client *SQcloudClient) ModifyDnsRecord(opts *cloudprovider.DnsRecordSet, d params["ttl"] = strconv.FormatInt(opts.Ttl, 10) params["value"] = opts.DnsValue params["recordLine"] = recordline + if opts.DnsType == cloudprovider.DnsTypeMX { + params["mx"] = strconv.FormatInt(opts.MxPriority, 10) + } _, err := client.cnsRequest("RecordModify", params) if err != nil { return errors.Wrapf(err, "client.cnsRequest(RecordModify, %s)", fmt.Sprintln(params)) @@ -184,6 +202,19 @@ func (client *SQcloudClient) ModifyDnsRecord(opts *cloudprovider.DnsRecordSet, d return nil } +// https://cloud.tencent.com/document/product/302/8519 +func (client *SQcloudClient) ModifyRecordStatus(status, recordId, domain string) error { + params := map[string]string{} + params["domain"] = domain + params["recordId"] = recordId + params["status"] = status // “disable” 和 “enable” + _, err := client.cnsRequest("RecordStatus", params) + if err != nil { + return errors.Wrapf(err, "client.cnsRequest(RecordModify, %s)", fmt.Sprintln(params)) + } + return nil +} + // https://cloud.tencent.com/document/api/302/8514 func (client *SQcloudClient) DeleteDnsRecord(recordId int, domainName string) error { params := map[string]string{} @@ -220,6 +251,9 @@ func (self *SDnsRecord) GetDnsType() cloudprovider.TDnsType { } func (self *SDnsRecord) GetDnsValue() string { + if self.GetDnsType() == cloudprovider.DnsTypeMX || self.GetDnsType() == cloudprovider.DnsTypeCNAME || self.GetDnsType() == cloudprovider.DnsTypeSRV { + return self.Value[:len(self.Value)-1] + } return self.Value } @@ -227,6 +261,13 @@ func (self *SDnsRecord) GetTTL() int64 { return int64(self.TTL) } +func (self *SDnsRecord) GetMxPriority() int64 { + if self.GetDnsType() == cloudprovider.DnsTypeMX { + return self.Mx + } + return 0 +} + func (self *SDnsRecord) GetPolicyType() cloudprovider.TDnsPolicyType { switch self.Line { case "境内", "境外": diff --git a/pkg/multicloud/qcloud/provider/provider.go b/pkg/multicloud/qcloud/provider/provider.go index 22afc4b22b..56aa0910c9 100644 --- a/pkg/multicloud/qcloud/provider/provider.go +++ b/pkg/multicloud/qcloud/provider/provider.go @@ -78,8 +78,6 @@ func (self *SQcloudProviderFactory) GetSupportedDnsTypes() map[cloudprovider.TDn cloudprovider.DnsTypeSRV, cloudprovider.DnsTypeTXT, cloudprovider.DnsTypePTR, - cloudprovider.DnsTypeFORWARD_URL, - cloudprovider.DnsTypeREDIRECT_URL, }, } } @@ -120,6 +118,27 @@ func (self *SQcloudProviderFactory) GetSupportedDnsPolicyValues() map[cloudprovi } } +// https://buy.cloud.tencent.com/cns?from=gobuy&domain=example4.com +func (self *SQcloudProviderFactory) GetTTLRange(zoneType cloudprovider.TDnsZoneType, productType cloudprovider.TDnsProductType) cloudprovider.TTlRange { + if len(productType) > 0 { + switch productType { + case cloudprovider.DnsProductEnterpriseUltimate: + return cloudprovider.TtlRangeQcloudEnterpriseUltimate + case cloudprovider.DnsProductEnterpriseStandard: + return cloudprovider.TtlRangeQcloudEnterpriseStandard + case cloudprovider.DnsProductEnterpriseBasic: + return cloudprovider.TtlRangeQcloudEnterpriseBasic + case cloudprovider.DnsProductPersonalProfessional: + return cloudprovider.TtlRangeQcloudPersonalProfessional + case cloudprovider.DnsProductFree: + return cloudprovider.TtlRangeQcloudFree + default: + return cloudprovider.TtlRangeQcloudFree + } + } + return cloudprovider.TtlRangeQcloudFree +} + func (self *SQcloudProviderFactory) ValidateChangeBandwidth(instanceId string, bandwidth int64) error { if len(instanceId) == 0 { return fmt.Errorf("Only changes to the binding machine's EIP bandwidth are supported") diff --git a/pkg/multicloud/qcloud/shell/dnspod.go b/pkg/multicloud/qcloud/shell/dnspod.go index 7cfda3c7ab..974dc066e2 100644 --- a/pkg/multicloud/qcloud/shell/dnspod.go +++ b/pkg/multicloud/qcloud/shell/dnspod.go @@ -88,7 +88,7 @@ func init() { change.DnsValue = args.VALUE change.Ttl = args.TTL change.DnsType = cloudprovider.TDnsType(args.TYPE) - e := cli.GetClient().CreateDnsRecord(&change, args.DOMAIN) + _, e := cli.GetClient().CreateDnsRecord(&change, args.DOMAIN) if e != nil { return e } @@ -117,6 +117,19 @@ func init() { return nil }) + type DnsRecordUpdateStatusOptions struct { + DOMAIN string + RECORDID int + STATUS string `choices:"disable|enable"` + } + shellutils.R(&DnsRecordUpdateStatusOptions{}, "dnsrecord-updatestatus", "update dndrecord", func(cli *qcloud.SRegion, args *DnsRecordUpdateStatusOptions) error { + e := cli.GetClient().ModifyRecordStatus(args.STATUS, strconv.Itoa(args.RECORDID), args.DOMAIN) + if e != nil { + return e + } + return nil + }) + type DnsRecordRemoveOptions struct { DOMAIN string RECORDID int