Authored by chunhua.zhang

Merge branch 'qcloudapi' into 'master'

Qcloudapi



See merge request !23
---
- hosts: localhost
vars_files:
- extra_vars/all.yml
vars:
qcloud_key: "{{ lookup('hashi_vault', 'secret=yoho/ops/qcloud token='+'{{vault.token}}'+' url='+'{{vault.server}}') }}"
tasks:
- name: 修改域名映射的IP或CNAME
domain_modify:
secretId: "{{ qcloud_key.SecretId }}"
secretKey: "{{ qcloud_key.SecretKey }}"
domain_name: "www.yohotest.xyz"
#value: "java-public-lb-862332839.cn-north-1.elb.amazonaws.com.cn"
value: '1.1.1.1'
... ...
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# ansible module for domain modify
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'hua.qu@yoho.cn'
}
DOCUMENTATION = '''
---
module: domain_modify
short_description: 修改域名解析
'''
EXAMPLES = '''
- hosts: 127.0.0.1
tasks:
- name: "修改域名对应的IP"
domain_modify:
secretId: "{{ qcloud_key.SecretId }}"
secretKey: "{{ qcloud_key.SecretKey }}"
domain_name: "switch.test.yohops.com"
value: "1.2.3.4"
- name: "修改域名对应的CNAME"
domain_modify:
secretId: "{{ qcloud_key.SecretId }}"
secretKey: "{{ qcloud_key.SecretKey }}"
domain_name: "switch.test.yohops.com"
value: "cname.dnspod.com."
'''
from ansible.module_utils.basic import *
from ansible.module_utils.qcloud_api import QcloudApi
import json
import sys
reload(sys)
sys.setdefaultencoding('utf8')
'''
通过调用接口RecordList获取待修改的域名解析记录列表
'''
def dns_inqure(SecretId,SecretKey,domain_name,value):
ret = {}
# 通过传入的域名参数简单判断域名格式是否正确
domain_regex = re.compile(r'(?:[A-Z0-9_](?:[A-Z0-9-_]{0,247}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))\Z',re.IGNORECASE)
if not domain_regex.match(domain_name):
ret["code"] = 2
ret["err"] = "域名格式不对"
return ret
# 通过传入的域名来获取domain和subDomain值
domain = '.'.join(domain_name.split('.')[-2:])
subDomain = '.'.join(domain_name.split('.')[:-2])
# Comment: 目前仅支持IP和CNAME的修改
pattern = re.compile(r'(\d+\.\d+\.\d+\.\d+)')
recordType = "A" if pattern.match(value) else "CNAME"
# 通过调用域名解析接口RecordList来获取待修改的域名解析记录值(调用修改域名解析接口时必须要传入的参数值)
try:
domaininfo_query = QcloudApi(secretId=SecretId,secretKey=SecretKey)
domaininfo = domaininfo_query.do_query(params={'Action': 'RecordList','domain': domain })
if domaininfo["code"] == 0:
domaininfo = domaininfo["data"]["records"]
# 循环获取到的domain下的所有域名解析值,提取待修改的域名对应的所有的参数值,子域名, 当解析值匹配时ret["code"] = 0
for lines in domaininfo:
if subDomain == lines["name"]:
ret["recordId"] = lines["id"]
ret["subDomain"] = subDomain
ret["domain"] = domain
ret["value_old"] = lines["value"]
ret["recordType_old"] = lines["type"]
ret["recordType_new"] = recordType
ret["recordLine"] = lines["line"]
ret["code"] = 0
break
else:
ret["code"] = 1
else:
ret["code"] = domaininfo["code"]
ret["err"] = domaininfo["message"]
except Exception,e:
ret["code"] = 500
ret["err"] = e.message
# 给子域名不存在情况下加错误信息
if ret["code"] == 1:
ret["err"] = "子域名subDomain: %s 不存在"%subDomain
#elif ret["code"] == 5100:
# ret["err"] = "domain: %s 不存在"%domain
return ret
'''
通过调用接口RecordModify修改域名解析记录
'''
def dns_modify(SecretId,SecretKey,domain_name,value,subDomain,domain,recordType,recordId,recordLine):
ret = {}
# 调用域名解析接口RecordModify修改域名解析记录
try:
domain_modify_api = QcloudApi(secretId=SecretId,secretKey=SecretKey)
dns_info = domain_modify_api.do_query(params={'Action':'RecordModify','domain':domain,'recordId':recordId,'subDomain':subDomain,'recordType':recordType,'recordLine':recordLine,'value':value})
ret["code"] = dns_info["code"]
ret["err"] = dns_info['message']
except Exception,e:
ret["code"] = 500
ret["err"] = e.message
return ret
def run_module():
# 定义参数
module_args = dict(
secretId=dict(type='str', required=True),
secretKey=dict(type='str', required=True),
domain_name=dict(type='str', required=True),
value=dict(type='str', required=True),
)
# seed the result dict in the object
# we primarily care about changed and state
# change is if this module effectively modified the target
# state will include any data that you want your module to pass back
# for consumption, for example, in a subsequent task
result = dict(
changed=False,
original_message='',
message=''
)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
secretId = module.params['secretId']
secretKey = module.params['secretKey']
domain_name = module.params['domain_name']
value = module.params['value']
ret = dns_inqure(secretId,secretKey,domain_name,value)
if ret['code'] != 0:
module.fail_json(msg='查询失败: code: %s err: %s' % (ret["code"],ret['err']), **result)
result['message'] = {"需修改解析的DNS":domain_name ,"修改之前的解析地址":ret["value_old"],"修改之前的记录类型":ret["recordType_old"],"修改之后的解析地址":value ,"修改之后的记录类型":ret["recordType_new"]}
if ret["value_old"] == value:
module.fail_json(msg='Modify Failed: 修改的解析与原解析一致', **result)
# check模式下只执行查询接口 执行语句:ansible-playbook playbooks/domain.modify.yml --check -vvv
if module.check_mode:
#result['response'] = ret
module.exit_json(**result)
# 非check模式下执行修改接口 执行语句:ansible-playbook playbooks/domain.modify.yml
else:
modify_result = dns_modify(secretId,secretKey,domain_name, value,ret["subDomain"],ret["domain"],ret["recordType_new"],ret["recordId"],ret["recordLine"])
if modify_result['code'] != 0:
module.fail_json(msg='Modify Failed: code: %i, err: %s' % (modify_result['code'], modify_result['err']), **result)
result['changed'] = True
#result['response'] = modify_result
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()
... ...
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from ansible.module_utils.basic import *
import requests
import json
import random
import hmac
import base64
import hashlib
import time
import sys
reload(sys)
sys.setdefaultencoding('utf8')
class QcloudApi():
"""腾讯云API接口"""
URLS_DNS = 'cns.api.qcloud.com/v2/index.php'
'public request params'
Region = 'ap-beijing'
Version = 'YOHO-QCLOUD-V1.0'
'see doc: https://cloud.tencent.com/document/product/302/8517'
'init. must provide SecretId and SecretKey'
def __init__(self,secretId,secretKey):
self.secretId = secretId
self.secretKey = secretKey
def do_query(self, params, req_url=URLS_DNS, api_version=2):
'''
Do Query Qcloud api
:param params: 查询参数,dict类型,必须包含 Action
return: dict类型,腾讯云返回的消息体
'''
if 'Action' not in params:
#print("must provide [Action] params !")
raise ValueError(params)
if api_version == 2:
public_params = {'Timestamp': int(time.time()), 'Nonce': QcloudApi.generate_nonce(),'SecretId': self.secretId}
params = dict(params, **public_params)
sign = self.signature(params, req_url)
params['Signature'] = sign
response_dict = requests.post("https://" + req_url, data=params).json()
elif api_version == 3:
public_params = {'Region': QcloudApi.Region, 'Timestamp': int(time.time()), 'Nonce': int(QcloudApi.generate_nonce()), 'Version': '2017-03-12', 'SecretId': self.secretId, 'SignatureMethod':'HmacSHA1'}
# merge params
params = dict(params, **public_params)
# signature
req_url = req_url.rstrip('/')+'/'
sign = self.signature(params, req_url)
params['Signature'] = sign
response_dict = requests.post("https://" + req_url, data=params).json()
return response_dict
@staticmethod
def generate_nonce(length=8):
return ''.join([str(random.randint(0, 9)) for i in range(length)])
def signature(self, request_params, url):
kv_pairs = []
for key in sorted(request_params):
kv_pairs.append('%s=%s' % (key, request_params[key]))
str_req = '&'.join(kv_pairs)
str_full = 'POST%s?%s' % (url, str_req)
hmac_sign = hmac.new(bytes(self.secretKey), bytes(str_full), hashlib.sha1).digest()
return base64.b64encode(hmac_sign).decode()
... ...