ddns 4.0.1__py2.py3-none-any.whl → 4.1.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ddns might be problematic. Click here for more details.
- ddns/__builtins__.pyi +6 -0
- ddns/__init__.py +2 -9
- ddns/__main__.py +108 -144
- ddns/cache.py +183 -0
- ddns/ip.py +145 -0
- ddns/provider/__init__.py +79 -0
- ddns/provider/_base.py +526 -0
- ddns/provider/_signature.py +113 -0
- ddns/provider/alidns.py +145 -176
- ddns/provider/aliesa.py +130 -0
- ddns/provider/callback.py +66 -104
- ddns/provider/cloudflare.py +87 -151
- ddns/provider/debug.py +19 -0
- ddns/provider/dnscom.py +91 -168
- ddns/provider/dnspod.py +102 -174
- ddns/provider/dnspod_com.py +11 -8
- ddns/provider/edgeone.py +83 -0
- ddns/provider/he.py +41 -78
- ddns/provider/huaweidns.py +134 -256
- ddns/provider/namesilo.py +159 -0
- ddns/provider/noip.py +101 -0
- ddns/provider/tencentcloud.py +194 -0
- ddns/util/comment.py +88 -0
- ddns/util/fileio.py +113 -0
- ddns/util/http.py +322 -0
- ddns/util/try_run.py +37 -0
- ddns-4.1.0.dist-info/METADATA +327 -0
- ddns-4.1.0.dist-info/RECORD +33 -0
- ddns/util/cache.py +0 -139
- ddns/util/config.py +0 -317
- ddns/util/ip.py +0 -96
- ddns-4.0.1.dist-info/METADATA +0 -326
- ddns-4.0.1.dist-info/RECORD +0 -21
- {ddns-4.0.1.dist-info → ddns-4.1.0.dist-info}/WHEEL +0 -0
- {ddns-4.0.1.dist-info → ddns-4.1.0.dist-info}/entry_points.txt +0 -0
- {ddns-4.0.1.dist-info → ddns-4.1.0.dist-info}/licenses/LICENSE +0 -0
- {ddns-4.0.1.dist-info → ddns-4.1.0.dist-info}/top_level.txt +0 -0
ddns/provider/he.py
CHANGED
|
@@ -1,83 +1,46 @@
|
|
|
1
1
|
# coding=utf-8
|
|
2
2
|
"""
|
|
3
3
|
Hurricane Electric (he.net) API
|
|
4
|
-
|
|
5
|
-
https://dns.he.net/docs.html
|
|
6
|
-
@author: NN708
|
|
4
|
+
@author: NN708, NewFuture
|
|
7
5
|
"""
|
|
8
6
|
|
|
9
|
-
from
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
conn = HTTPSConnection(API.SITE)
|
|
50
|
-
|
|
51
|
-
conn.request(API.METHOD, '/' + API.ACTION, urlencode(params), {
|
|
52
|
-
"Content-type": "application/x-www-form-urlencoded"
|
|
53
|
-
})
|
|
54
|
-
response = conn.getresponse()
|
|
55
|
-
res = response.read().decode('utf8')
|
|
56
|
-
conn.close()
|
|
57
|
-
|
|
58
|
-
if response.status < 200 or response.status >= 300:
|
|
59
|
-
warning('%s : error[%d]:%s', API.ACTION, response.status, res)
|
|
60
|
-
raise Exception(res)
|
|
61
|
-
else:
|
|
62
|
-
debug('%s : result:%s', API.ACTION, res)
|
|
63
|
-
if not res:
|
|
64
|
-
raise Exception("empty response")
|
|
65
|
-
elif res[:5] == "nochg" or res[:4] == "good": # No change or success
|
|
66
|
-
return res
|
|
67
|
-
else:
|
|
68
|
-
raise Exception(res)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def update_record(domain, value, record_type="A"):
|
|
72
|
-
"""
|
|
73
|
-
更新记录
|
|
74
|
-
"""
|
|
75
|
-
info(">>>>>%s(%s)", domain, record_type)
|
|
76
|
-
res = request(hostname=domain, myip=value)
|
|
77
|
-
if res[:4] == "good":
|
|
78
|
-
result = "Record updated. New IP is: " + res[5:-1]
|
|
79
|
-
elif res[:5] == "nochg":
|
|
80
|
-
result = "IP not changed. IP is: " + res[6:-1]
|
|
81
|
-
else:
|
|
82
|
-
result = "Record update failed."
|
|
83
|
-
return result
|
|
7
|
+
from ._base import SimpleProvider, TYPE_FORM
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HeProvider(SimpleProvider):
|
|
11
|
+
endpoint = "https://dyn.dns.he.net"
|
|
12
|
+
content_type = TYPE_FORM
|
|
13
|
+
accept = None # he.net does not require a specific Accept header
|
|
14
|
+
decode_response = False # he.net response is plain text, not JSON
|
|
15
|
+
|
|
16
|
+
def _validate(self):
|
|
17
|
+
self.logger.warning(
|
|
18
|
+
"HE.net 缺少充分的真实环境测试,请及时在 GitHub Issues 中反馈: %s",
|
|
19
|
+
"https://github.com/NewFuture/DDNS/issues",
|
|
20
|
+
)
|
|
21
|
+
if self.id:
|
|
22
|
+
raise ValueError("Hurricane Electric (he.net) does not use `id`, use `token(password)` only.")
|
|
23
|
+
if not self.token:
|
|
24
|
+
raise ValueError("Hurricane Electric (he.net) requires `token(password)`.")
|
|
25
|
+
|
|
26
|
+
def set_record(self, domain, value, record_type="A", ttl=None, line=None, **extra):
|
|
27
|
+
"""
|
|
28
|
+
使用 POST API 更新或创建 DNS 记录。Update or create DNS record with POST API.
|
|
29
|
+
https://dns.he.net/docs.html
|
|
30
|
+
"""
|
|
31
|
+
self.logger.info("%s => %s(%s)", domain, value, record_type)
|
|
32
|
+
params = {
|
|
33
|
+
"hostname": domain, # he.net requires full domain name
|
|
34
|
+
"myip": value, # IP address to update
|
|
35
|
+
"password": self.token, # Use token as password
|
|
36
|
+
}
|
|
37
|
+
try:
|
|
38
|
+
res = self._http("POST", "/nic/update", body=params)
|
|
39
|
+
if res and res[:5] == "nochg" or res[:4] == "good": # No change or success
|
|
40
|
+
self.logger.info("HE API response: %s", res)
|
|
41
|
+
return True
|
|
42
|
+
else:
|
|
43
|
+
self.logger.error("HE API error: %s", res)
|
|
44
|
+
except Exception as e:
|
|
45
|
+
self.logger.error("Error updating record for %s: %s", domain, e)
|
|
46
|
+
return False
|
ddns/provider/huaweidns.py
CHANGED
|
@@ -2,264 +2,142 @@
|
|
|
2
2
|
"""
|
|
3
3
|
HuaweiDNS API
|
|
4
4
|
华为DNS解析操作库
|
|
5
|
-
|
|
6
|
-
@author: cybmp3
|
|
5
|
+
@author: NewFuture
|
|
7
6
|
"""
|
|
8
7
|
|
|
8
|
+
from time import gmtime, strftime
|
|
9
|
+
|
|
10
|
+
from ._base import TYPE_JSON, BaseProvider, encode_params, join_domain
|
|
11
|
+
from ._signature import hmac_sha256_authorization, sha256_hash
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HuaweiDNSProvider(BaseProvider):
|
|
15
|
+
endpoint = "https://dns.myhuaweicloud.com"
|
|
16
|
+
content_type = TYPE_JSON
|
|
17
|
+
algorithm = "SDK-HMAC-SHA256"
|
|
18
|
+
|
|
19
|
+
def _validate(self):
|
|
20
|
+
self.logger.warning(
|
|
21
|
+
"华为云 DNS 缺少充分的真实环境测试,请及时在 GitHub Issues 中反馈: %s",
|
|
22
|
+
"https://github.com/NewFuture/DDNS/issues",
|
|
23
|
+
)
|
|
24
|
+
super(HuaweiDNSProvider, self)._validate()
|
|
25
|
+
|
|
26
|
+
def _request(self, method, path, **params):
|
|
27
|
+
"""
|
|
28
|
+
https://support.huaweicloud.com/api-dns/zh-cn_topic_0037134406.html
|
|
29
|
+
https://support.huaweicloud.com/devg-apisign/api-sign-algorithm-002.html
|
|
30
|
+
https://support.huaweicloud.com/devg-apisign/api-sign-algorithm-003.html
|
|
31
|
+
https://support.huaweicloud.com/devg-apisign/api-sign-algorithm-004.html
|
|
32
|
+
"""
|
|
33
|
+
# type: (str, str, **Any) -> dict
|
|
34
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
35
|
+
if method.upper() == "GET" or method.upper() == "DELETE":
|
|
36
|
+
query = encode_params(params)
|
|
37
|
+
body = ""
|
|
38
|
+
else:
|
|
39
|
+
query = ""
|
|
40
|
+
body = self._encode_body(params)
|
|
41
|
+
|
|
42
|
+
now = strftime("%Y%m%dT%H%M%SZ", gmtime())
|
|
43
|
+
headers = {
|
|
44
|
+
"content-type": self.content_type,
|
|
45
|
+
"host": self.endpoint.split("://", 1)[1].strip("/"),
|
|
46
|
+
"X-Sdk-Date": now,
|
|
47
|
+
}
|
|
9
48
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class Config:
|
|
34
|
-
ID = "id" # AK
|
|
35
|
-
TOKEN = "TOKEN" # AS
|
|
36
|
-
PROXY = None # 代理设置
|
|
37
|
-
TTL = None
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class API:
|
|
41
|
-
# API 配置
|
|
42
|
-
SCHEME = 'https'
|
|
43
|
-
SITE = 'dns.myhuaweicloud.com' # API endpoint
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def HexEncodeSHA256Hash(data):
|
|
47
|
-
sha = sha256()
|
|
48
|
-
sha.update(data)
|
|
49
|
-
return sha.hexdigest()
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def StringToSign(canonical_request, t):
|
|
53
|
-
b = HexEncodeSHA256Hash(canonical_request)
|
|
54
|
-
return "%s\n%s\n%s" % (Algorithm, datetime.strftime(t, BasicDateFormat), b)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def CanonicalHeaders(headers, signed_headers):
|
|
58
|
-
a = []
|
|
59
|
-
__headers = {}
|
|
60
|
-
for key in headers:
|
|
61
|
-
key_encoded = key.lower()
|
|
62
|
-
value = headers[key]
|
|
63
|
-
value_encoded = value.strip()
|
|
64
|
-
__headers[key_encoded] = value_encoded
|
|
65
|
-
for key in signed_headers:
|
|
66
|
-
a.append(key + ":" + __headers[key])
|
|
67
|
-
return '\n'.join(a) + "\n"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def request(method, path, param=None, body=None, **params):
|
|
71
|
-
# path 是不带host但是 前面需要带 / , body json 字符串或者自己从dict转换下
|
|
72
|
-
# 也可以自己改成 判断下是不是post 是post params就是body
|
|
73
|
-
if param:
|
|
74
|
-
params.update(param)
|
|
75
|
-
|
|
76
|
-
query = urlencode(sorted(params.items()))
|
|
77
|
-
headers = {"content-type": "application/json"} # 初始化header
|
|
78
|
-
headers["X-Sdk-Date"] = datetime.strftime(
|
|
79
|
-
datetime.utcnow(), BasicDateFormat)
|
|
80
|
-
headers["host"] = API.SITE
|
|
81
|
-
# 如何后来有需要把header头 key转换为小写 value 删除前导空格和尾随空格
|
|
82
|
-
sign_headers = []
|
|
83
|
-
for key in headers:
|
|
84
|
-
sign_headers.append(key.lower())
|
|
85
|
-
# 先排序
|
|
86
|
-
sign_headers.sort()
|
|
87
|
-
|
|
88
|
-
if body is None:
|
|
89
|
-
body = ""
|
|
90
|
-
|
|
91
|
-
hex_encode = HexEncodeSHA256Hash(body.encode('utf-8'))
|
|
92
|
-
# 生成文档中的CanonicalRequest
|
|
93
|
-
canonical_headers = CanonicalHeaders(headers, sign_headers)
|
|
94
|
-
|
|
95
|
-
# 签名中的path 必须 / 结尾
|
|
96
|
-
if path[-1] != '/':
|
|
97
|
-
sign_path = path + "/"
|
|
98
|
-
else:
|
|
99
|
-
sign_path = path
|
|
100
|
-
|
|
101
|
-
canonical_request = "%s\n%s\n%s\n%s\n%s\n%s" % (method.upper(), sign_path, query,
|
|
102
|
-
canonical_headers, ";".join(sign_headers), hex_encode)
|
|
103
|
-
|
|
104
|
-
hashed_canonical_request = HexEncodeSHA256Hash(
|
|
105
|
-
canonical_request.encode('utf-8'))
|
|
106
|
-
|
|
107
|
-
# StringToSign
|
|
108
|
-
str_to_sign = "%s\n%s\n%s" % (
|
|
109
|
-
Algorithm, headers['X-Sdk-Date'], hashed_canonical_request)
|
|
110
|
-
|
|
111
|
-
secret = Config.TOKEN
|
|
112
|
-
# 计算签名 HexEncode(HMAC(Access Secret Key, string to sign))
|
|
113
|
-
signature = hmac(secret.encode(
|
|
114
|
-
'utf-8'), str_to_sign.encode('utf-8'), digestmod=sha256).digest()
|
|
115
|
-
signature = hexlify(signature).decode()
|
|
116
|
-
# 添加签名信息到请求头
|
|
117
|
-
auth_header = "%s Access=%s, SignedHeaders=%s, Signature=%s" % (
|
|
118
|
-
Algorithm, Config.ID, ";".join(sign_headers), signature)
|
|
119
|
-
headers['Authorization'] = auth_header
|
|
120
|
-
# 创建Http请求
|
|
121
|
-
|
|
122
|
-
if Config.PROXY:
|
|
123
|
-
conn = HTTPSConnection(Config.PROXY)
|
|
124
|
-
conn.set_tunnel(API.SITE, 443)
|
|
125
|
-
else:
|
|
126
|
-
conn = HTTPSConnection(API.SITE)
|
|
127
|
-
conn.request(method, API.SCHEME + "://" + API.SITE +
|
|
128
|
-
path + '?' + query, body, headers)
|
|
129
|
-
info(API.SCHEME + "://" + API.SITE + path + '?' + query, body)
|
|
130
|
-
resp = conn.getresponse()
|
|
131
|
-
data = resp.read().decode('utf8')
|
|
132
|
-
resp.close()
|
|
133
|
-
if resp.status < 200 or resp.status >= 300:
|
|
134
|
-
|
|
135
|
-
warning('%s : error[%d]: %s', path, resp.status, data)
|
|
136
|
-
raise Exception(data)
|
|
137
|
-
else:
|
|
138
|
-
data = jsondecode(data)
|
|
139
|
-
debug('%s : result:%s', path, data)
|
|
49
|
+
# 使用通用签名函数
|
|
50
|
+
body_hash = sha256_hash(body)
|
|
51
|
+
# 华为云需要在签名时使用带尾斜杠的路径
|
|
52
|
+
sign_path = path if path.endswith("/") else path + "/"
|
|
53
|
+
authorization_format = "%s Access=%s, SignedHeaders={SignedHeaders}, Signature={Signature}" % (
|
|
54
|
+
self.algorithm,
|
|
55
|
+
self.id,
|
|
56
|
+
)
|
|
57
|
+
authorization = hmac_sha256_authorization(
|
|
58
|
+
secret_key=self.token,
|
|
59
|
+
method=method,
|
|
60
|
+
path=sign_path,
|
|
61
|
+
query=query,
|
|
62
|
+
headers=headers,
|
|
63
|
+
body_hash=body_hash,
|
|
64
|
+
signing_string_format=self.algorithm + "\n" + now + "\n{HashedCanonicalRequest}",
|
|
65
|
+
authorization_format=authorization_format,
|
|
66
|
+
)
|
|
67
|
+
headers["Authorization"] = authorization
|
|
68
|
+
|
|
69
|
+
# 使用原始路径发送实际请求
|
|
70
|
+
path = "{}?{}".format(path, query) if query else path
|
|
71
|
+
data = self._http(method, path, headers=headers, body=body)
|
|
140
72
|
return data
|
|
141
73
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if records: # update
|
|
213
|
-
for (rid, record) in records.items():
|
|
214
|
-
if record['records'] != value:
|
|
215
|
-
"""
|
|
216
|
-
PUT https://{endpoint}/v2/zones/{zone_id}/recordsets/{recordset_id}
|
|
217
|
-
|
|
218
|
-
{
|
|
219
|
-
"name" : "www.example.com.",
|
|
220
|
-
"description" : "This is an example record set.",
|
|
221
|
-
"type" : "A",
|
|
222
|
-
"ttl" : 3600,
|
|
223
|
-
"records" : [ "192.168.10.1", "192.168.10.2" ]
|
|
224
|
-
}
|
|
225
|
-
"""
|
|
226
|
-
body = {
|
|
227
|
-
"name": domain,
|
|
228
|
-
"description": "Managed by DDNS.",
|
|
229
|
-
"type": record_type,
|
|
230
|
-
"records": [
|
|
231
|
-
value
|
|
232
|
-
]
|
|
233
|
-
}
|
|
234
|
-
# 如果TTL不为空,则添加到字典中
|
|
235
|
-
if Config.TTL is not None:
|
|
236
|
-
body['ttl'] = Config.TTL
|
|
237
|
-
res = request('PUT', '/v2/zones/' + zoneid + '/recordsets/' + record['id'],
|
|
238
|
-
body=str(jsonencode(body)))
|
|
239
|
-
if res:
|
|
240
|
-
get_records.records[cache_key][rid]['records'] = value
|
|
241
|
-
result[rid] = res.get("name")
|
|
242
|
-
else:
|
|
243
|
-
result[rid] = "Update fail!\n" + str(res)
|
|
244
|
-
else:
|
|
245
|
-
result[rid] = domain
|
|
246
|
-
else: # create
|
|
247
|
-
body = {
|
|
248
|
-
"name": domain,
|
|
249
|
-
"description": "Managed by DDNS.",
|
|
250
|
-
"type": record_type,
|
|
251
|
-
"records": [
|
|
252
|
-
value
|
|
253
|
-
]
|
|
254
|
-
}
|
|
255
|
-
# 如果TTL不为空,则添加到字典中
|
|
256
|
-
if Config.TTL is not None:
|
|
257
|
-
body['ttl'] = Config.TTL
|
|
258
|
-
res = request('POST', '/v2/zones/' + zoneid + '/recordsets',
|
|
259
|
-
body=str(jsonencode(body)))
|
|
260
|
-
if res:
|
|
261
|
-
get_records.records[cache_key][res['id']] = res
|
|
262
|
-
result = res
|
|
263
|
-
else:
|
|
264
|
-
result = domain + " created fail!"
|
|
265
|
-
return result
|
|
74
|
+
def _query_zone_id(self, domain):
|
|
75
|
+
"""https://support.huaweicloud.com/api-dns/dns_api_62003.html"""
|
|
76
|
+
domain = domain + "." if not domain.endswith(".") else domain
|
|
77
|
+
data = self._request("GET", "/v2/zones", search_mode="equal", limit=500, name=domain)
|
|
78
|
+
zones = data.get("zones", [])
|
|
79
|
+
zone = next((z for z in zones if domain == z.get("name")), None)
|
|
80
|
+
zoneid = zone and zone["id"]
|
|
81
|
+
return zoneid
|
|
82
|
+
|
|
83
|
+
def _query_record(self, zone_id, subdomain, main_domain, record_type, line, extra):
|
|
84
|
+
"""
|
|
85
|
+
v2.1 https://support.huaweicloud.com/api-dns/dns_api_64004.html
|
|
86
|
+
v2 https://support.huaweicloud.com/api-dns/ListRecordSetsByZone.html
|
|
87
|
+
"""
|
|
88
|
+
domain = join_domain(subdomain, main_domain) + "."
|
|
89
|
+
data = self._request(
|
|
90
|
+
"GET",
|
|
91
|
+
"/v2.1/zones/" + zone_id + "/recordsets",
|
|
92
|
+
limit=500,
|
|
93
|
+
name=domain,
|
|
94
|
+
type=record_type,
|
|
95
|
+
line_id=line,
|
|
96
|
+
search_mode="equal",
|
|
97
|
+
)
|
|
98
|
+
records = data.get("recordsets", [])
|
|
99
|
+
record = next((r for r in records if r.get("name") == domain and r.get("type") == record_type), None)
|
|
100
|
+
return record
|
|
101
|
+
|
|
102
|
+
def _create_record(self, zone_id, subdomain, main_domain, value, record_type, ttl, line, extra):
|
|
103
|
+
"""
|
|
104
|
+
v2.1 https://support.huaweicloud.com/api-dns/dns_api_64001.html
|
|
105
|
+
v2 https://support.huaweicloud.com/api-dns/CreateRecordSet.html
|
|
106
|
+
"""
|
|
107
|
+
domain = join_domain(subdomain, main_domain) + "."
|
|
108
|
+
extra["description"] = extra.get("description", self.remark)
|
|
109
|
+
res = self._request(
|
|
110
|
+
"POST",
|
|
111
|
+
"/v2.1/zones/" + zone_id + "/recordsets",
|
|
112
|
+
name=domain,
|
|
113
|
+
type=record_type,
|
|
114
|
+
records=[value],
|
|
115
|
+
ttl=ttl,
|
|
116
|
+
line=line,
|
|
117
|
+
**extra
|
|
118
|
+
) # fmt: skip
|
|
119
|
+
if res and res.get("id"):
|
|
120
|
+
self.logger.info("Record created: %s", res)
|
|
121
|
+
return True
|
|
122
|
+
self.logger.warning("Failed to create record: %s", res)
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
def _update_record(self, zone_id, old_record, value, record_type, ttl, line, extra):
|
|
126
|
+
"""https://support.huaweicloud.com/api-dns/UpdateRecordSets.html"""
|
|
127
|
+
extra["description"] = extra.get("description", self.remark)
|
|
128
|
+
# Note: The v2.1 update API does not support the line parameter in the request body
|
|
129
|
+
# The line parameter is returned in the response but cannot be modified
|
|
130
|
+
res = self._request(
|
|
131
|
+
"PUT",
|
|
132
|
+
"/v2.1/zones/" + zone_id + "/recordsets/" + old_record["id"],
|
|
133
|
+
name=old_record["name"],
|
|
134
|
+
type=record_type,
|
|
135
|
+
records=[value],
|
|
136
|
+
ttl=ttl if ttl is not None else old_record.get("ttl"),
|
|
137
|
+
**extra
|
|
138
|
+
) # fmt: skip
|
|
139
|
+
if res and res.get("id"):
|
|
140
|
+
self.logger.info("Record updated: %s", res)
|
|
141
|
+
return True
|
|
142
|
+
self.logger.warning("Failed to update record: %s", res)
|
|
143
|
+
return False
|