华为云用户手册

  • AXE模式解绑接口 1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 # -*- coding: utf-8 -*-import timeimport uuidimport hashlibimport base64import sslimport urllib.requestimport hmacfrom hashlib import sha256# 必填,请参考"开发准备"获取如下数据,替换为实际值realUrl = 'https://rtcpns.cn-north-1.myhuaweicloud.com/rest/caas/extendnumber/v1.0' #APP接入地址+接口访问URIAPP_KEY = "a1********" #APP_KeyAPP_SECRET = "cfc8********" #APP_Secret'''选填,各参数要求请参考"AXE模式解绑接口"subscriptionId和(virtualNum+extendNum)二选一即可,当都传入时,优先选用subscriptionId'''subscriptionId = '****' #指定"AXE模式绑定接口"返回的绑定ID进行解绑virtualNum = '+86170****0001' #AXE中的X号码extendNum = '1234' #AXE中的E号码def buildAKSKHeader(appKey, appSecret): now = time.strftime('%Y-%m-%dT%H:%M:%SZ') #Created nonce = str(uuid.uuid4()).replace('-','') #Nonce digist = hmac.new(appSecret.encode(), (nonce + now).encode(), digestmod=sha256).digest() digestBase64 = base64.b64encode(digist).decode() #PasswordDigest return 'UsernameToken Username="{}",PasswordDigest="{}",Nonce="{}",Created="{}"'.format(appKey, digestBase64, nonce, now);def main(): # 请求URL参数 formData = urllib.parse.urlencode({ 'subscriptionId':subscriptionId, 'virtualNum':virtualNum, 'extendNum':extendNum }) #完整请求地址 fullUrl = realUrl + '?' + formData req = urllib.request.Request(url=fullUrl, method='DELETE') #请求方法为DELETE # 请求Headers参数 req.add_header('Authorization', 'AKSK realm="SDP",profile="UsernameToken",type="Appkey"') req.add_header('X-AKSK', buildAKSKHeader(APP_KEY, APP_SECRET)) req.add_header('Content-Type', 'application/json;charset=UTF-8') # 为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题 ssl._create_default_https_context = ssl._create_unverified_context try: print(formData) #打印请求数据 r = urllib.request.urlopen(req) #发送请求 print(r.read().decode('utf-8')) #打印响应结果 except urllib.error.HTTPError as e: print(e.code) print(e.read().decode('utf-8')) #打印错误信息 except urllib.error.URLError as e: print(e.reason)if __name__ == '__main__': main()
  • AXE模式绑定接口 1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 # -*- coding: utf-8 -*-import timeimport uuidimport hashlibimport base64import jsonimport sslimport urllib.requestimport hmacfrom hashlib import sha256# 必填,请参考"开发准备"获取如下数据,替换为实际值realUrl = 'https://rtcpns.cn-north-1.myhuaweicloud.com/rest/caas/extendnumber/v1.0' #APP接入地址+接口访问URIAPP_KEY = "a1********" #APP_KeyAPP_SECRET = "cfc8********" #APP_SecretvirtualNum = '+86170****0001' #AXE中的X号码bindNum = '+86186****5678' #AXE中的A号码'''选填,各参数要求请参考"AXE模式绑定接口"'''# areaCode = '0755' #需要绑定的X号码对应的城市码# displayNumMode = '0' #非A用户呼叫X号码时,A看到的主显号码# recordFlag = 'false' #是否需要针对该绑定关系产生的所有通话录音# recordHintTone = 'recordHintTone.wav' #设置录音提示音# callbackTone = 'callbackTone.wav' #A呼叫X不存在回呼记录的提示音# callbackNum = '+86170****0021' #A呼叫X不存在回呼记录的转接号码# bindExpiredTime = 24 #绑定关系的有效时间# callbackExpiredTime = 24 #回呼记录有效时间# userData = 'abcdefg' #用户自定义数据def buildAKSKHeader(appKey, appSecret): now = time.strftime('%Y-%m-%dT%H:%M:%SZ') #Created nonce = str(uuid.uuid4()).replace('-','') #Nonce digist = hmac.new(appSecret.encode(), (nonce + now).encode(), digestmod=sha256).digest() digestBase64 = base64.b64encode(digist).decode() #PasswordDigest return 'UsernameToken Username="{}",PasswordDigest="{}",Nonce="{}",Created="{}"'.format(appKey, digestBase64, nonce, now);def main(): # 请求Body,可按需删除选填参数 jsonData = json.dumps({ 'virtualNum':virtualNum, 'bindNum':bindNum,# 'areaCode':areaCode,# 'displayNumMode':displayNumMode,# 'recordFlag':recordFlag,# 'recordHintTone':recordHintTone,# 'callbackTone':callbackTone,# 'callbackNum':callbackNum,# 'bindExpiredTime':bindExpiredTime,# 'callbackExpiredTime':callbackExpiredTime,# 'userData':userData }).encode('ascii') req = urllib.request.Request(url=realUrl, data=jsonData, method='POST') #请求方法为POST # 请求Headers参数 req.add_header('Authorization', 'AKSK realm="SDP",profile="UsernameToken",type="Appkey"') req.add_header('X-AKSK', buildAKSKHeader(APP_KEY, APP_SECRET)) req.add_header('Content-Type', 'application/json;charset=UTF-8') # 为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题 ssl._create_default_https_context = ssl._create_unverified_context try: fo = open('bind_data.txt', 'a', encoding='utf-8') #打开本地文件 fo.write('绑定请求数据:' + jsonData.decode('utf-8') + '\n') #绑定请求参数记录到本地文件,方便定位问题 r = urllib.request.urlopen(req) #发送请求 print(r.read().decode('utf-8')) #打印响应结果 fo.write('绑定结果:' + str(r.read().decode('utf-8')) + '\n') #绑定ID很重要,请记录到本地文件,方便后续修改绑定关系及解绑 except urllib.error.HTTPError as e: print(e.code) print(e.read().decode('utf-8')) #打印错误信息 except urllib.error.URLError as e: print(e.reason) finally: fo.close() #关闭文件if __name__ == '__main__': main()
  • Java代码样例 样例 隐私保护通话 代码样例(JAVA) 环境要求 基于JDK 1.8版本,要求JDK 1.6及以上版本。 引用库 commons-io、commons-lang3、fastjson、log4j、sun.misc.base64decoder 下载链接 点此下载 本文档所述Demo在提供服务的过程中,可能会涉及个人数据的使用,建议您遵从国家的相关法律采取足够的措施,以确保用户的个人数据受到充分的保护。 本文档所述Demo仅用于功能演示,不允许客户直接进行商业使用。 本文档信息仅供参考,不构成任何要约或承诺。 本文档接口携带参数只是用作参考,不可以直接复制使用,填写参数需要替换为实际值,请参考“开发准备”获取所需数据。 父主题: 代码样例
  • 短信通知接口 1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 # -*- coding: utf-8 -*-'''短信通知客户平台收到隐私保护通话平台的短信通知的接口通知'''import json#短信通知样例jsonBody = json.dumps({ 'appKey': '****', 'smsEvent': { 'smsIdentifier': '****', 'notificationMode': 'Block', 'calling': '+86138****0001', 'virtualNumber': '+86138****0000', 'event': 'Text SMS ', 'timeStamp': '2018-09-13T09:46:16.023Z' }}).encode('ascii')print(jsonBody)'''短信通知@see: 详细内容以接口文档为准@param param: jsonBody@return: '''def onSmsEvent(jsonBody): jsonObj = json.loads(jsonBody) #将通知消息解析为jsonObj if ('smsEvent' not in jsonObj): print('param error: no smsEvent.') return #print(jsonObj['appKey']) #商户应用的AppKey smsEvent = jsonObj['smsEvent'] #短信通知信息 ''' Example: 此处已解析notificationMode为例,请按需解析所需参数并自行实现相关处理 'smsIdentifier': 短信唯一标识 'notificationMode': 通知模式 'calling': 真实发送方号码 'called': 真实接收方号码 'virtualNumber': 隐私号码(X号码) 'event': 短信状态事件 'timeStamp': 短信事件发生的系统时间戳,UTC时间 'subscriptionId': 绑定ID 'smsContent': 用户发送的短信内容 ''' if ('notificationMode' in smsEvent): if ('Block' == smsEvent['notificationMode']): #收到隐私保护通话平台的短信通知,若为Block模式,请参考接口文档回消息指示下一步操作 actions = { 'operation': 'vNumberRoute', #操作类型:转发短信/丢弃短信 'message': { 'called': '+86138****7022', #真实接收方号码 'calling': '+86138****7021' #真实发送方号码 } } resp = json.dumps({'actions': [actions]}) #Block模式响应消息 print(resp); elif ('Notify' == smsEvent['notificationMode']): #收到隐私保护通话平台的短信通知,若为Notify模式,请回HTTP状态码为200的空消息 #statusCode = 200; print('This is AXB sms Notify mode.'); else: print('notificationMode param error.');def main(): onSmsEvent(jsonBody); #短信通知处理if __name__ == '__main__': main()
  • 呼叫事件通知接口 1 2 3 4 5 6 7 8 9 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99100101102103104105106107108109110111112113114115116117118119120121 # -*- coding: utf-8 -*-'''呼叫事件通知客户平台收到隐私保护通话平台的呼叫事件通知的接口通知'''import json#呼叫事件通知样例jsonBody = json.dumps({ 'eventType': 'disconnect', 'statusInfo': { 'sessionId': '1200_1029_4294967295_20190123091514@callenabler246.huaweicaas.com', 'timestamp': '2019-01-23 09:16:41', 'caller': '+86138****0021', 'called': '+86138****7021', 'stateCode': 0, 'stateDesc': 'The user releases the call.', 'subscriptionId': '****' }}).encode('ascii')print(jsonBody)'''呼叫事件通知@see: 详细内容以接口文档为准@param param: jsonBody@return: '''def onCallEvent(jsonBody): jsonObj = json.loads(jsonBody) #将通知消息解析为jsonObj eventType = jsonObj['eventType'] #通知事件类型 if ('fee' == eventType): print('EventType error: ' + eventType) return if ('statusInfo' not in jsonObj): print('param error: no statusInfo.') return statusInfo = jsonObj['statusInfo'] #呼叫状态事件信息 print('eventType: ' + eventType) #打印通知事件类型 #callin:呼入事件 if ('callin' == eventType): ''' Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 'sessionId': 通话链路的标识ID 'caller': 主叫号码 'called': 被叫号码 'subscriptionId': 绑定关系ID ''' if ('sessionId' in statusInfo): print('sessionId: ' + statusInfo['sessionId']) return #callout:呼出事件 if ('callout' == eventType): ''' Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 'sessionId': 通话链路的标识ID 'caller': 主叫号码 'called': 被叫号码 'subscriptionId': 绑定关系ID ''' if ('sessionId' in statusInfo): print('sessionId: ' + statusInfo['sessionId']) return #alerting:振铃事件 if ('alerting' == eventType): ''' Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 'sessionId': 通话链路的标识ID 'caller': 主叫号码 'called': 被叫号码 'subscriptionId': 绑定关系ID ''' if ('sessionId' in statusInfo): print('sessionId: ' + statusInfo['sessionId']) return #answer:应答事件 if ('answer' == eventType): ''' Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 'sessionId': 通话链路的标识ID 'caller': 主叫号码 'called': 被叫号码 'subscriptionId': 绑定关系ID ''' if ('sessionId' in statusInfo): print('sessionId: ' + statusInfo['sessionId']) return #disconnect:挂机事件 if ('disconnect' == eventType): ''' Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 'sessionId': 通话链路的标识ID 'caller': 主叫号码 'called': 被叫号码 'stateCode': 通话挂机的原因值 'stateDesc': 通话挂机的原因值的描述 'subscriptionId': 绑定关系ID ''' if ('sessionId' in statusInfo): print('sessionId: ' + statusInfo['sessionId']) returndef main(): onCallEvent(jsonBody) #呼叫事件处理if __name__ == '__main__': main()
  • 获取录音文件下载地址接口 1 2 3 4 5 6 7 8 91011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162 # -*- coding: utf-8 -*-import timeimport uuidimport hashlibimport base64import urllibimport requests #需要先使用pip install requests命令安装依赖import hmacfrom hashlib import sha256# 必填,请参考"开发准备"获取如下数据,替换为实际值realUrl = 'https://rtcpns.cn-north-1.myhuaweicloud.com/rest/provision/voice/record/v1.0' #APP接入地址+接口访问URIAPP_KEY = "a1********" #APP_KeyAPP_SECRET = "cfc8********" #APP_Secret# 必填,通过"话单通知接口"获取recordDomain = '****.com' #录音文件存储的服务器 域名 fileName = '****.wav' #录音文件名def buildAKSKHeader(appKey, appSecret): now = time.strftime('%Y-%m-%dT%H:%M:%SZ') #Created nonce = str(uuid.uuid4()).replace('-','') #Nonce digist = hmac.new(appSecret.encode(), (nonce + now).encode(), digestmod=sha256).digest() digestBase64 = base64.b64encode(digist).decode() #PasswordDigest return 'UsernameToken Username="{}",PasswordDigest="{}",Nonce="{}",Created="{}"'.format(appKey, digestBase64, nonce, now);def main(): # 请求URL参数 formData = urllib.parse.urlencode({ 'recordDomain':recordDomain, 'fileName':fileName }) #完整请求地址 fullUrl = realUrl + '?' + formData # 请求Headers参数 header = { 'Authorization': 'AKSK realm="SDP",profile="UsernameToken",type="Appkey"', 'X-AKSK': buildAKSKHeader(appKey, appSecret), 'Content-Type': 'application/json;charset=UTF-8' } try: fo = open('bind_data.txt', 'a', encoding='utf-8') #打开本地文件 r = requests.get(fullUrl, headers=header, allow_redirects=False, verify=False) #发送请求 if(301 == r.status_code): print(r.status_code) #打印响应结果 print(r.headers['Location']) fo.write('获取录音文件下载地址:' + r.headers['Location'] + '\n') else: print(r.status_code) #打印响应结果 print(r.text) except urllib.error.HTTPError as e: print(e.code) print(e.read().decode('utf-8')) #打印错误信息 except urllib.error.URLError as e: print(e.reason) finally: fo.close() #关闭文件if __name__ == '__main__': main()
  • AXB模式绑定信息查询接口 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 # -*- coding: utf-8 -*-import timeimport uuidimport hashlibimport base64import sslimport urllib.requestimport hmacfrom hashlib import sha256# 必填,请参考"开发准备"获取如下数据,替换为实际值realUrl = 'https://rtcpns.cn-north-1.myhuaweicloud.com/rest/caas/relationnumber/partners/v1.0' #APP接入地址+接口访问URIAPP_KEY = "a1********" #APP_KeyAPP_SECRET = "cfc8********" #APP_Secret'''选填,各参数要求请参考"AXB模式绑定信息查询接口"subscriptionId和relationNum为二选一关系,两者都携带时以subscriptionId为准'''subscriptionId = '****' #指定"AXB模式绑定接口"返回的绑定ID进行查询relationNum = '+86170****0001' #指定X号码(隐私号码)进行查询# pageIndex = 1 #查询的分页索引,从1开始编号# pageSize = 20 #查询的分页大小,即每次查询返回多少条数据def buildAKSKHeader(appKey, appSecret): now = time.strftime('%Y-%m-%dT%H:%M:%SZ') #Created nonce = str(uuid.uuid4()).replace('-','') #Nonce digist = hmac.new(appSecret.encode(), (nonce + now).encode(), digestmod=sha256).digest() digestBase64 = base64.b64encode(digist).decode() #PasswordDigest return 'UsernameToken Username="{}",PasswordDigest="{}",Nonce="{}",Created="{}"'.format(appKey, digestBase64, nonce, now);def main(): # 请求URL参数,可按需删除选填参数 formData = urllib.parse.urlencode({ 'subscriptionId':subscriptionId, 'relationNum':relationNum,# 'pageIndex':pageIndex,# 'pageSize':pageSize }) #完整请求地址 fullUrl = realUrl + '?' + formData req = urllib.request.Request(url=fullUrl, method='GET') #请求方法为GET # 请求Headers参数 req.add_header('Authorization', 'AKSK realm="SDP",profile="UsernameToken",type="Appkey"') req.add_header('X-AKSK', buildAKSKHeader(APP_KEY, APP_SECRET)) req.add_header('Content-Type', 'application/json;charset=UTF-8') # 为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题 ssl._create_default_https_context = ssl._create_unverified_context try: fo = open('bind_data.txt', 'a', encoding='utf-8') #打开本地文件 r = urllib.request.urlopen(req) #发送请求 print(r.read().decode('utf-8')) #打印响应结果 fo.write('绑定查询结果:' + str(r.read().decode('utf-8')) + '\n') #查询结果,记录到本地文件 except urllib.error.HTTPError as e: print(e.code) print(e.read().decode('utf-8')) #打印错误信息 except urllib.error.URLError as e: print(e.reason) finally: fo.close() #关闭文件if __name__ == '__main__': main()
  • AXB模式解绑接口 1 2 3 4 5 6 7 8 91011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 # -*- coding: utf-8 -*-import timeimport uuidimport hashlibimport base64import sslimport urllib.requestimport hmacfrom hashlib import sha256# 必填,请参考"开发准备"获取如下数据,替换为实际值realUrl = 'https://rtcpns.cn-north-1.myhuaweicloud.com/rest/caas/relationnumber/partners/v1.0' #APP接入地址+接口访问URIAPP_KEY = "a1********" #APP_KeyAPP_SECRET = "cfc8********" #APP_Secret'''选填,各参数要求请参考"AXB模式解绑接口"subscriptionId和relationNum为二选一关系,两者都携带时以subscriptionId为准'''subscriptionId = '****' #指定"AXB模式绑定接口"返回的绑定ID进行解绑relationNum = '+86170****0001' #指定X号码(隐私号码)进行解绑def buildAKSKHeader(appKey, appSecret): now = time.strftime('%Y-%m-%dT%H:%M:%SZ') #Created nonce = str(uuid.uuid4()).replace('-','') #Nonce digist = hmac.new(appSecret.encode(), (nonce + now).encode(), digestmod=sha256).digest() digestBase64 = base64.b64encode(digist).decode() #PasswordDigest return 'UsernameToken Username="{}",PasswordDigest="{}",Nonce="{}",Created="{}"'.format(appKey, digestBase64, nonce, now);def main(): # 请求URL参数 formData = urllib.parse.urlencode({ 'subscriptionId':subscriptionId, 'relationNum':relationNum }) #完整请求地址 fullUrl = realUrl + '?' + formData req = urllib.request.Request(url=fullUrl, method='DELETE') #请求方法为DELETE # 请求Headers参数 req.add_header('Authorization', 'AKSK realm="SDP",profile="UsernameToken",type="Appkey"') req.add_header('X-AKSK', buildAKSKHeader(APP_KEY, APP_SECRET)) req.add_header('Content-Type', 'application/json;charset=UTF-8') # 为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题 ssl._create_default_https_context = ssl._create_unverified_context try: print(formData) #打印请求数据 r = urllib.request.urlopen(req) #发送请求 print(r.read().decode('utf-8')) #打印响应结果 except urllib.error.HTTPError as e: print(e.code) print(e.read().decode('utf-8')) #打印错误信息 except urllib.error.URLError as e: print(e.reason)if __name__ == '__main__': main()
  • AXB模式绑定信息修改接口 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 # -*- coding: utf-8 -*-import timeimport uuidimport hashlibimport base64import jsonimport sslimport urllib.requestimport hmacfrom hashlib import sha256# 必填,请参考"开发准备"获取如下数据,替换为实际值realUrl = 'https://rtcpns.cn-north-1.myhuaweicloud.com/rest/caas/relationnumber/partners/v1.0' #APP接入地址+接口访问URIAPP_KEY = "a1********" #APP_KeyAPP_SECRET = "cfc8********" #APP_SecretsubscriptionId = '0167ecc9-bfb6-4eec-b671-a7dab2ba78c' #必填,指定"AXB模式绑定接口"返回的绑定ID进行修改'''选填,各参数要求请参考"AXB模式绑定信息修改接口"'''callerNum = '+86186****5678' #A号码calleeNum = '+86186****5679' #B号码# callDirection = 0 #允许呼叫的方向# duration = 86400 #绑定关系保持时间,到期后会被系统自动解除绑定关系# maxDuration = 60 #设置允许单次通话进行的最长时间,通话时间从接通被叫的时刻开始计算# lastMinVoice = 'lastMinVoice.wav' #设置通话剩余最后一分钟时的提示音# privateSms = 'true' #设置该绑定关系是否支持短信功能# callerHintTone = 'callerHintTone.wav' #设置A拨打X号码时的通话前等待音# calleeHintTone = 'calleeHintTone.wav' #设置B拨打X号码时的通话前等待音# preVoice = {# 'callerHintTone': callerHintTone,# 'calleeHintTone': calleeHintTone# };def buildAKSKHeader(appKey, appSecret): now = time.strftime('%Y-%m-%dT%H:%M:%SZ') #Created nonce = str(uuid.uuid4()).replace('-','') #Nonce digist = hmac.new(appSecret.encode(), (nonce + now).encode(), digestmod=sha256).digest() digestBase64 = base64.b64encode(digist).decode() #PasswordDigest return 'UsernameToken Username="{}",PasswordDigest="{}",Nonce="{}",Created="{}"'.format(appKey, digestBase64, nonce, now);def main(): # 请求Body,可按需删除选填参数 jsonData = json.dumps({ 'subscriptionId':subscriptionId, 'callerNum':callerNum, 'calleeNum':calleeNum,# 'callDirection':callDirection,# 'duration':duration,# 'maxDuration':maxDuration,# 'lastMinVoice':lastMinVoice,# 'privateSms':privateSms,# 'preVoice':preVoice }).encode('ascii') req = urllib.request.Request(url=realUrl, data=jsonData, method='PUT') #请求方法为PUT # 请求Headers参数 req.add_header('Authorization', 'AKSK realm="SDP",profile="UsernameToken",type="Appkey"') req.add_header('X-AKSK', buildAKSKHeader(APP_KEY, APP_SECRET)) req.add_header('Content-Type', 'application/json;charset=UTF-8') # 为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题 ssl._create_default_https_context = ssl._create_unverified_context try: print(jsonData.decode('utf-8')) #打印请求数据 r = urllib.request.urlopen(req) #发送请求 print(r.read().decode('utf-8')) #打印响应结果 except urllib.error.HTTPError as e: print(e.code) print(e.read().decode('utf-8')) #打印错误信息 except urllib.error.URLError as e: print(e.reason)if __name__ == '__main__': main()
  • AXB模式绑定接口 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 # -*- coding: utf-8 -*-import timeimport uuidimport hashlibimport base64import jsonimport sslimport urllib.requestimport hmacfrom hashlib import sha256# 必填,请参考"开发准备"获取如下数据,替换为实际值realUrl = 'https://rtcpns.cn-north-1.myhuaweicloud.com/rest/caas/relationnumber/partners/v1.0' #APP接入地址+接口访问URIAPP_KEY = "a1********" #APP_KeyAPP_SECRET = "cfc8********" #APP_SecretrelationNum = '+86170****0001' #X号码(隐私号码)callerNum = '+86186****5678' #A号码calleeNum = '+86186****5679' #B号码'''选填,各参数要求请参考"AXB模式绑定接口"'''# areaCode = '0755' #需要绑定的X号码对应的城市码# callDirection = 0 #允许呼叫的方向# duration = 86400 #绑定关系保持时间,到期后会被系统自动解除绑定关系# recordFlag = 'false' #是否需要针对该绑定关系产生的所有通话录音# recordHintTone = 'recordHintTone.wav' #设置录音提示音# maxDuration = 60 #设置允许单次通话进行的最长时间,通话时间从接通被叫的时刻开始计算# lastMinVoice = 'lastMinVoice.wav' #设置通话剩余最后一分钟时的提示音# privateSms = 'true' #设置该绑定关系是否支持短信功能# callerHintTone = 'callerHintTone.wav' #设置A拨打X号码时的通话前等待音# calleeHintTone = 'calleeHintTone.wav' #设置B拨打X号码时的通话前等待音# preVoice = {# 'callerHintTone': callerHintTone,# 'calleeHintTone': calleeHintTone# };def buildAKSKHeader(appKey, appSecret): now = time.strftime('%Y-%m-%dT%H:%M:%SZ') #Created nonce = str(uuid.uuid4()).replace('-','') #Nonce digist = hmac.new(appSecret.encode(), (nonce + now).encode(), digestmod=sha256).digest() digestBase64 = base64.b64encode(digist).decode() #PasswordDigest return 'UsernameToken Username="{}",PasswordDigest="{}",Nonce="{}",Created="{}"'.format(appKey, digestBase64, nonce, now);def main(): # 请求Body,可按需删除选填参数 jsonData = json.dumps({ 'relationNum':relationNum,# 'areaCode':areaCode, 'callerNum':callerNum, 'calleeNum':calleeNum,# 'callDirection':callDirection,# 'duration':duration,# 'recordFlag':recordFlag,# 'recordHintTone':recordHintTone,# 'maxDuration':maxDuration,# 'lastMinVoice':lastMinVoice,# 'privateSms':privateSms,# 'preVoice':preVoice }).encode('ascii') req = urllib.request.Request(url=realUrl, data=jsonData, method='POST') #请求方法为POST # 请求Headers参数 req.add_header('Authorization', 'AKSK realm="SDP",profile="UsernameToken",type="Appkey"') req.add_header('X-AKSK', buildAKSKHeader(APP_KEY, APP_SECRET)) req.add_header('Content-Type', 'application/json;charset=UTF-8') # 为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题 ssl._create_default_https_context = ssl._create_unverified_context try: fo = open('bind_data.txt', 'a', encoding='utf-8') #打开本地文件 fo.write('绑定请求数据:' + jsonData.decode('utf-8') + '\n') #绑定请求参数记录到本地文件,方便定位问题 r = urllib.request.urlopen(req) #发送请求 print(r.read().decode('utf-8')) #打印响应结果 fo.write('绑定结果:' + str(r.read().decode('utf-8')) + '\n') #绑定ID很重要,请记录到本地文件,方便后续修改绑定关系及解绑 except urllib.error.HTTPError as e: print(e.code) print(e.read().decode('utf-8')) #打印错误信息 except urllib.error.URLError as e: print(e.reason) finally: fo.close() #关闭文件if __name__ == '__main__': main()
  • 短信通知接口 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 /** * 短信通知 * 客户平台收到隐私保护通话平台的短信通知的接口通知 *///短信通知样例var jsonBody = JSON.stringify({ 'appKey': '****', 'smsEvent': { 'smsIdentifier': '****', 'notificationMode': 'Block', 'calling': '+86138****0001', 'virtualNumber': '+86138****0000', 'event': 'TextSMS', 'timeStamp': '2018-09-13T09:46:16.023Z' }});console.log('jsonBody:', jsonBody);/** * 短信通知 * @brief 详细内容以接口文档为准 * @param jsonBody * @returns */function onSmsEvent(jsonBody) { var jsonObj = JSON.parse(jsonBody); //将通知消息解析为jsonObj if (!jsonObj.hasOwnProperty('smsEvent')) { console.log('param error: no smsEvent.'); return; } //console.log('appKey:', jsonObj.appKey); //商户应用的AppKey var smsEvent = jsonObj.smsEvent; //短信通知信息 /** * Example: 此处以解析notificationMode为例,请按需解析所需参数并自行实现相关处理 * * 'smsIdentifier': 短信唯一标识 * 'notificationMode': 通知模式 * 'calling': 真实发送方号码 * 'called': 真实接收方号码 * 'virtualNumber': 隐私号码(X号码) * 'event': 短信状态事件 * 'timeStamp': 短信事件发生的系统时间戳,UTC时间 * 'subscriptionId': 绑定ID * 'smsContent': 用户发送的短信内容 */ if (smsEvent.hasOwnProperty('notificationMode')) { if ('Block' === smsEvent.notificationMode) { //收到隐私保护通话平台的短信通知,若为Block模式,请参考接口文档回消息指示下一步操作 var actions = { 'operation': 'vNumberRoute', //操作类型:转发短信/丢弃短信 'message': { 'called': '+86138****7022', //真实接收方号码 'calling': '+86138****7021' //真实发送方号码 } }; var resp = JSON.stringify({'actions': [actions]}); //Block模式响应消息 console.log('resp:', resp); } else if ('Notify' === smsEvent.notificationMode) { //收到隐私保护通话平台的短信通知,若为Notify模式,请回HTTP状态码为200的空消息 //var statusCode = 200; console.log('This is AX sms Notify mode.'); } else { console.log('notificationMode param error.'); } }}//短信通知处理onSmsEvent(jsonBody);
  • 呼叫事件通知接口 1 2 3 4 5 6 7 8 9 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 /** * 呼叫事件通知 * 客户平台收到隐私保护通话平台的呼叫事件通知的接口通知 *///呼叫事件通知样例var jsonBody = JSON.stringify({ 'eventType': 'disconnect', 'statusInfo': { 'sessionId': '1200_1827_4294967295_20190124023003@callenabler246.huaweicaas.com', 'timestamp': '2019-01-24 02:30:22', 'caller': '+86138****0022', 'called': '+86138****7021', 'stateCode': 0, 'stateDesc': 'The user releases the call.', 'subscriptionId': '****' }});console.log('jsonBody:', jsonBody);/** * 呼叫事件通知 * @brief 详细内容以接口文档为准 * @param jsonBody * @returns */function onCallEvent(jsonBody) { var jsonObj = JSON.parse(jsonBody); //将通知消息解析为jsonObj var eventType = jsonObj.eventType; //通知事件类型 if ('fee' === eventType) { console.log('EventType error:', eventType); return; } if (!jsonObj.hasOwnProperty('statusInfo')) { console.log('param error: no statusInfo.'); return; } var statusInfo = jsonObj.statusInfo; //呼叫状态事件信息 console.log('eventType:', eventType); //打印通知事件类型 //callin:呼入事件 if ('callin' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; } //callout:呼出事件 if ('callout' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; } //alerting:振铃事件 if ('alerting' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; } //answer:应答事件 if ('answer' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; } //disconnect:挂机事件 if ('disconnect' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'stateCode': 通话挂机的原因值 * 'stateDesc': 通话挂机的原因值的描述 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; }}//呼叫事件处理onCallEvent(jsonBody);
  • 呼叫事件通知接口 1 2 3 4 5 6 7 8 9 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 /** * 呼叫事件通知 * 客户平台收到隐私保护通话平台的呼叫事件通知的接口通知 *///呼叫事件通知样例var jsonBody = JSON.stringify({ 'eventType': 'disconnect', 'statusInfo': { 'sessionId': '1202_1051_4294967295_20190124070250@callenabler246.huaweicaas.com', 'timestamp': '2019-01-24 07:03:28', 'caller': '+86138****0022', 'called': '+86138****7021', 'stateCode': 0, 'stateDesc': 'The user releases the call.', 'subscriptionId': '********' }});console.log('jsonBody:', jsonBody);/** * 呼叫事件通知 * @brief 详细内容以接口文档为准 * @param jsonBody */function onCallEvent(jsonBody) { var jsonObj = JSON.parse(jsonBody); //将通知消息解析为jsonObj var eventType = jsonObj.eventType; //通知事件类型 if ('fee' === eventType) { console.log('EventType error:', eventType); return; } if (!jsonObj.hasOwnProperty('statusInfo')) { console.log('param error: no statusInfo.'); return; } var statusInfo = jsonObj.statusInfo; //呼叫状态事件信息 console.log('eventType:', eventType); //打印通知事件类型 //callin:呼入事件 if ('callin' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; } //collectInfo:放音收号结果事件,仅AXE模式下的A被叫场景携带 if ('collectInfo' === eventType) { /** * Example: 此处以解析digitInfo为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'digitInfo': AXE场景中携带收号结果(即用户输入的数字) */ if (statusInfo.hasOwnProperty('digitInfo')) { console.log('digitInfo:', statusInfo.digitInfo); } return; } //callout:呼出事件 if ('callout' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; } //alerting:振铃事件 if ('alerting' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; } //answer:应答事件 if ('answer' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; } //disconnect:挂机事件 if ('disconnect' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'stateCode': 通话挂机的原因值 * 'stateDesc': 通话挂机的原因值的描述 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; }}//呼叫事件处理onCallEvent(jsonBody);
  • 接口调用错误码处理 调用API接口会产生接口调用响应结果码,响应示例如下: 注:请根据响应码和结果码查看处理方法。 HTTP/1.1 200 OK Content-Type: application/json;charset=utf-8 Content-Length: xx { "resultcode":"0", "resultdesc":"Success", "origNum":"+86138****8888", "privateNum":"+86138****6666","subscriptionId":"******"} 表1 响应结果码 响应码 结果码 英文描述 中文描述 处理方法 200 0 Success. 成功。 - 400 1023006 Authorization not contained in the HTTP header. HTTP消息头未找到Authorization字段。 请检查HTTP消息头中是否携带了Authorization字段。 1023007 realm not contained in Authorization. Authorization字段中未找到realm属性。 请检查Authorization字段中的是否携带了realm属性。 1023008 profile not contained in Authorization. Authorization字段中未找到profile属性。 请检查Authorization字段中的是否携带了profile属性。 1023009 The value of realm in Authorization must be SDP. Authorization中realm属性值应该为“SDP”。 请检查Authorization字段中的realm属性值是否为“SDP”。 1023010 The value of profile in Authorization must be UsernameToken. Authorization中profile属性值应该为“UsernameToken”。 请检查Authorization字段中的profile属性值是否为“UsernameToken”。 1023011 The value of type in Authorization must be app_key. Authorization中type属性值应该为“Appkey”。 请检查Authorization字段中的type属性值是否为“Appkey”。 1023012 type not contained in Authorization. Authorization字段中未找到type属性。 请检查Authorization字段中是否携带了type属性。 1023033 HTTP header not found X-AKSK field HTTP头未找到X-AKSK字段 请检查HTTP消息头中是否携带了X-AKSK字段。 1023034 UserName not contained in X-AKSK. X-AKSK字段中未找到UserName属性。 请检查X-AKSK字段中的是否携带了Username属性。 1023035 Nonce not contained in X-AKAK. X-AKAK字段中未找到Nonce属性。 请检查X-AKAK字段中的是否携带了Nonce属性。 1023036 Created not contained in X-AKSK. X-AKSK字段中未找到Created属性。 请检查X-AKSK字段中的是否携带了Created属性。 1023037 PasswordDigest not contained in X-AKSK. X-AKSK字段中未找到PasswordDigest属性。 请检查X-AKSK字段中的是否携带了PasswordDigest属性。 1023038 UsernameToken not contained in X-AKSK. X-AKSK中没有携带UsernameToken。 请检查X-AKSK字段中的是否携带了UsernameToken属性。 401 1010010 Invalid digest. PasswordDigest校验失败。 请检查PasswordDigest字段填写是否正确。 1010013 Time out limit. 时间超出限制。 请确认X-AKSK鉴权时,生成随机数的时间与发送请求时的本地时间不能相差太大(具体差值请与管理员确认)。 403 1010002 Invalid request. 无效请求。 参考各接口参数说明,检查请求携带的参数格式是否正确,如以下参数格式问题: 绑定接口填写的号码参数需为全局号码格式,如+86138****0001或+8675528****01; 放音文件需上传通过审核才可通过接口调用,点击查看如何上传审核。 参考接口参数说明,检查是否携带了不能同时携带的参数,如AXE模式绑定接口不能同时携带callbackTone和callbackNum,若是,请保留一个; 参数长度或格式是否错误,如AXE模式分机号长度或timeUnit的格式是否正确。 1010003 Invalid app_key. 无效的app_key。 请检查请求携带的app_key填写是否正确。 1010008 The status of the app_key is unavailable. app_key状态异常。 请检查请求携带的app_key所属应用状态是否正常。应用状态可登录控制台后在“应用管理”界面查看。 1010010 The flow control upper limit is reached on the platform. 平台达到系统流控上限。 请稍等一分钟后再试。 1010029 The subscriber status is frozen. 用户账号已冻结。 查看账户是否欠费。 如欠费需充值后才能继续使用。 若未欠费,请联系华为云客服处理。 1010040 The app_key is not allowed to invoke the API. app_key没有调用本API的权限。 出现该错误码表示调用的接口和app_key所属的应用模式不一致。如添加应用时选择的AXB模式的应用,调用接口时只能调用AXB模式的接口,不能调用其他模式的接口。 1016002 The record already exists. 记录已经存在。 出现该错误码表示调用AX模式绑定接口时指定的A号码(origNum)和X号码(privateNum)之间已经存在绑定关系,请更换origNum或privateNum参数的值。 1011001 Account does not exist. 账号不存在。 出现该错误码可能有以下两个原因: 调用AX模式相关接口时指定的X号码(privateNum)可能不是该应用已申请的隐私号码,请确认privateNum参数的填写是否正确; 调用AX模式相关接口时填写的X号码(privateNum)格式不正确,请根据接口文档修改号码格式后再次尝试。 1011002 Insufficient number resources. 号码资源不足。 出现该错误码表示调用AX模式绑定接口时没有可分配的X号码,请申请新的号码资源或修改areaCode的值。点击查看处理方法 1011003 Exceeded the upper limit of resources that can be applied for. 超过允许申请的资源上限。 出现该错误码表示调用AX模式绑定接口时指定的A号码已绑定了五个X号码,请更换origNum参数的值。AX模式中一个A号码只能绑定五个X号码。 1011004 The number is not applied for binding application. 携带的X号码和app_key没有绑定关系。 出现该错误码表示调用AX模式解绑接口或AX模式绑定信息修改接口时携带的app_key和X号码没有绑定关系,请检查携带的X号码是否属于该应用。 1012001 Resource of number is not to be applied. 资源未申请。 出现该错误码表示调用解绑或查询绑定关系接口时携带的app_key和X号码没有绑定关系,请检查携带的X号码是否属于该应用。 1012007 The record does not exist. 记录不存在。 若调用AXB模式绑定接口、AXB模式解绑接口、AXB模式绑定信息修改接口或AXB模式绑定信息查询接口时出现该错误码,表示隐私保护通话平台未查询到绑定关系,请检查携带的relationNum或subscriptionId参数是否属于该应用。 若调用获取录音文件下载地址接口时出现该错误码,表示隐私保护通话平台未查询到录音信息,请确认携带的fileName参数是否填写正确。 1012008 Insufficient number of resources. 号码资源不足。 没有可分配的X号码,请申请新的号码资源或修改areaCode的值。点击查看处理方法 1012009 Maximum number of resources has been exceeded. 指定的X号码的绑定数量已达到上限。 出现该错误码表示调用AXB模式绑定接口指定的X号码已经绑定了5000对关系。请修改relationNum的值,或者解除指定的X号码上的部分绑定关系。AXB模式中一个X号码只能同时绑定5000对关系。 1012010 The relation number has been bound. 绑定关系已存在。 出现该错误码表示调用AXB模式绑定接口时携带的X号码(relationNum)和A号码(callerNum)或B号码(calleeNum)已存在绑定关系,可确认后更换其他X号码进行绑定。 1012012 Application does not open recording function. 应用未开启录音功能。 出现该错误码表示添加应用时未开启录音功能,点击查看如何开启录音功能。 1012102 The number status is abnormal. 号码状态异常。 出现该错误码表示调用接口时指定的X号码因投诉或号码状态异常被隐私保护通话平台加入了黑名单。 请查看订购号码时填写的邮箱是否有业务下线通知邮件,如果没有,请拨打400电话联系华为云客服处理。 1011005 Resources have been allocated. 资源已经分配。 出现该错误码表示调用AX模式绑定接口时指定的X号码(privateNum)已和其他A号码绑定,可更换其他X号码进行绑定。 如果该X号码的绑定关系可以解除,您还可以调用AX模式解绑接口解除该绑定关系后,再使用该X号码进行绑定。 1016001 The record does not exist. 记录不存在。 如果调用AXYB模式解绑接口时出现该错误码,表示调用接口时指定的subscriptionId不正确,未查询到绑定关系,请确认subscriptionId参数是否正确。 如果调用AX模式解绑接口时出现该错误码,表示调用接口时指定的origNum或者subscriptionId参数不正确,未查询到绑定关系,请确认参数是否正确。 如果调用AXE模式解绑接口时出现该错误码,表示调用接口时指定的virtualNum,extendNum或subscriptionId参数不正确,未查询到绑定关系,请确认参数是否正确。 1023005 Virtual number over license limit. 隐私号码超出license限制。 请联系客服处理。 1020166 The app client ip is not in ip white list. 对端app IP不在白名单列表中。 联系客服检查IP白名单是否配置正确。 1020167 No idle extend Number. 没有空闲的分机号。 出现该错误码表示调用AXE模式绑定接口时指定的分机号(extendNum)已被占用,请重新指定分机号。 1013102 The extend number has been bound. 绑定关系已存在。 出现该错误码表示调用AXE模式绑定接口时指定的X号码(virtualNum)和A号码(bindNum)已有绑定关系,无需再次绑定。 500 1010001 Internal system error. 系统错误。 请联系客服处理。
  • 调测指引 隐私保护通话二次开发过程中,开发者需关注的业务调测点如下: 注:隐私保护通话各模式的二次开发业务流程不完全相同,以下流程以AXB模式为例。 Check 1:发起AXB绑定请求前,对请求参数合法性做必要的检查,如: 携带正确的APP接入地址。APP接入地址需登录隐私保护通话控制台,从“应用管理”页获取。 携带的参数格式是否正确,无多余空格等。如:绑定接口携带的A号码需为全局号码格式(如:+86138****0021),其他参数详见API参考文档。 Check 2:获取绑定请求结果时,请解析出响应结果码。若绑定失败,可参考API错误码中的处理建议进行修正。 HTTP/1.1 200 OK Content-Type: application/json;charset=utf-8 Content-Length: xx { "resultcode":"0", "resultdesc":"Success" ,"subscriptionId":"********","relationNum":"+867552****08", "callDirection":0,"duration":0,"maxDuration":0} Check 3:隐私保护通话平台会推送呼叫事件通知给客户服务器。若呼叫失败,请解析出挂机事件通知(disconnect)中的挂机原因值(stateCode)并参考挂机原因值排查失败原因。 注:仅在添加应用时设置了呼叫状态接收地址时,隐私保护通话平台才会推送呼叫事件通知给客户服务器。 POST /status HTTP/1.1{"eventType":"disconnect","statusInfo":{"sessionId":"1200_1029_4294967295_20190123091514@callenabler246.huaweicaas.com","timestamp":"2019-01-23 09:16:41","caller":"+86138****0021","called":"+86138****7021","stateCode":0,"stateDesc":"The user releases the call.","subscriptionId":"********"}} Check 4:通话结束后,隐私保护通话平台会推送话单通知给客户。若呼叫失败,请解析出转接呼叫操作失败的Q850原因值(fwdUnaswRsn)和通话失败的拆线点(ulFailReason),并参考Q850原因值说明和呼叫拆线点说明排查失败原因。 注:仅在添加应用时设置了呼叫话单接收地址时,隐私保护通话平台才会推送话单通知给客户服务器。 POST /fee HTTP/1.1{"eventType":"fee","feeLst":[{"direction":1,"spId":"********","appKey":"************","icid":"ba171f34e6953fcd751edc77127748f4.3757223714.337238282.9","bindNum":"+86138****0022","sessionId":"1200_1029_4294967295_20190123091514@callenabler246.huaweicaas.com","subscriptionId":"********","callerNum":"+86138****0021","calleeNum":"+86138****0022","fwdDisplayNum":"+86138****0022","fwdDstNum":"+86138****7021","callInTime":"2019-01-23 09:15:14","fwdStartTime":"2019-01-23 09:15:15","fwdAlertingTime":"2019-01-23 09:15:21","fwdAnswerTime":"2019-01-23 09:15:36","callEndTime":"2019-01-23 09:16:41","fwdUnaswRsn":0,"ulFailReason":0,"sipStatusCode":0,"callOutUnaswRsn":0,"recordFlag":1,"recordStartTime":"2019-01-23 09:15:37","recordDomain":"****.com","recordBucketName":"****","recordObjectName":"****.wav","ttsPlayTimes":0,"ttsTransDuration":0,"mptyId":"****","serviceType":"004","hostName":"callenabler246.huaweicaas.com"}]}
  • 开发准备 启动开发前需要准备的数据如下: 注:X号码(隐私号码)、Y号码(隐私号码)、A号码和B号码的格式要求,请参考接口文档中的参数说明。 参数名 取值样例 获取方式 相关文档 APP_Key a1************ 登录管理控制台,从“应用管理”页获取。 注:AXYB模式、AXE模式、AXB模式和AX模式对应不同的应用,请按需获取。 添加应用 APP_Secret cfc8**************** APP接入地址 https://rtcpns.cn-north-1.myhuaweicloud.com X号码(隐私号码) +86170****0001 登录管理控制台,根据“所属应用”从“号码订购”页下载号码表。 订购号码 Y号码(隐私号码) +86170****0002 区号(areaCode) 0755 A号码 +86186****5678 AXYB模式、AXE模式、AX模式和AXB模式下,A用户真实手机号或固定电话,请按需填写。 注:因运营商管控,当A号码为固话号码时,只能接收来自X号码的呼叫,不能作为主叫呼叫X号码。 - B号码 +86186****5679 AXYB模式、AXB模式下,B用户真实手机号或固定电话,请按需填写。 注:因运营商管控,当B号码为固话号码时,只能接收来自X号码的呼叫,不能作为主叫呼叫X号码。 - 访问URI /rest/provision/caas/privatenumber/v1.0 不同接口具有不同的URI,请从对应的接口文档中获取。 AX模式绑定接口
  • 短信通知接口 1 2 3 4 5 6 7 8 91011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 /** * 短信通知 * 客户平台收到隐私保护通话平台的短信通知的接口通知 *///短信通知样例var jsonBody = JSON.stringify({ 'appKey': '****', 'smsEvent': { 'smsIdentifier': '****', 'notificationMode': 'Block', 'calling': '+86138****0001', 'virtualNumber': '+86138****0000', 'event': 'TextSMS', 'timeStamp': '2018-09-13T09:46:16.023Z' }});console.log('jsonBody:', jsonBody);/** * 短信通知 * @brief 详细内容以接口文档为准 * @param jsonBody */function onSmsEvent(jsonBody) { var jsonObj = JSON.parse(jsonBody); //将通知消息解析为jsonObj if (!jsonObj.hasOwnProperty('smsEvent')) { console.log('param error: no smsEvent.'); return; } //console.log('appKey:', jsonObj.appKey); //商户应用的AppKey var smsEvent = jsonObj.smsEvent; //短信通知信息 /** * Example: 此处以解析notificationMode为例,请按需解析所需参数并自行实现相关处理 * * 'smsIdentifier': 短信唯一标识 * 'notificationMode': 通知模式 * 'calling': 真实发送方号码 * 'called': 真实接收方号码 * 'virtualNumber': 隐私号码(X号码) * 'event': 短信状态事件 * 'timeStamp': 短信事件发生的系统时间戳,UTC时间 * 'subscriptionId': 绑定ID * 'smsContent': 用户发送的短信内容 */ if (smsEvent.hasOwnProperty('notificationMode')) { if ('Block' === smsEvent.notificationMode) { //收到隐私保护通话平台的短信通知,若为Block模式,请参考接口文档回消息指示下一步操作 var actions = { 'operation': 'vNumberRoute', //操作类型:转发短信/丢弃短信 'message': { 'called': '+86138****7022', //真实接收方号码 'calling': '+86138****7021' //真实发送方号码 } }; var resp = JSON.stringify({'actions': [actions]}); //Block模式响应消息 console.log('resp:', resp); } else if ('Notify' === smsEvent.notificationMode) { //收到隐私保护通话平台的短信通知,若为Notify模式,请回HTTP状态码为200的空消息 //var statusCode = 200; console.log('This is AXB sms Notify mode.'); } else { console.log('notificationMode param error.'); } }}//短信通知处理onSmsEvent(jsonBody);
  • 呼叫事件通知接口 1 2 3 4 5 6 7 8 9 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 /** * 呼叫事件通知 * 客户平台收到隐私保护通话平台的呼叫事件通知的接口通知 *///呼叫事件通知样例var jsonBody = JSON.stringify({ 'eventType': 'disconnect', 'statusInfo': { 'sessionId': '1200_1029_4294967295_20190123091514@callenabler246.huaweicaas.com', 'timestamp': '2019-01-23 09:16:41', 'caller': '+86138****0021', 'called': '+86138****7021', 'stateCode': 0, 'stateDesc': 'The user releases the call.', 'subscriptionId': '****' }});console.log('jsonBody:', jsonBody);/** * 呼叫事件通知 * @brief 详细内容以接口文档为准 * @param jsonBody */function onCallEvent(jsonBody) { var jsonObj = JSON.parse(jsonBody); //将通知消息解析为jsonObj var eventType = jsonObj.eventType; //通知事件类型 if ('fee' === eventType) { console.log('EventType error:', eventType); return; } if (!jsonObj.hasOwnProperty('statusInfo')) { console.log('param error: no statusInfo.'); return; } var statusInfo = jsonObj.statusInfo; //呼叫状态事件信息 console.log('eventType:', eventType); //打印通知事件类型 //callin:呼入事件 if ('callin' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; } //callout:呼出事件 if ('callout' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; } //alerting:振铃事件 if ('alerting' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; } //answer:应答事件 if ('answer' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; } //disconnect:挂机事件 if ('disconnect' === eventType) { /** * Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理 * * 'timestamp': 呼叫事件发生时隐私保护通话平台的UNIX时间戳 * 'sessionId': 通话链路的标识ID * 'caller': 主叫号码 * 'called': 被叫号码 * 'stateCode': 通话挂机的原因值 * 'stateDesc': 通话挂机的原因值的描述 * 'subscriptionId': 绑定关系ID */ if (statusInfo.hasOwnProperty('sessionId')) { console.log('sessionId:', statusInfo.sessionId); } return; }}//呼叫事件处理onCallEvent(jsonBody);
  • 前提条件 请在迁移前提前清理原集群中异常的Pod资源。当Pod状态异常但是又挂载了PVC的资源时,在集群迁移后,PVC状态会处于pending状态。 请确保CCE侧集群中没有与被迁移集群侧相同的资源,因为Velero工具在检测到相同资源时,默认不进行恢复。 为确保集群迁移后容器镜像资源可以正常拉取,请将镜像资源迁移至 容器镜像服务 (SWR),具体操作方法请参见客户端上传镜像。 CCE不支持ReadWriteMany的云硬盘存储,在原集群中存在该类型资源时,需要先修改为ReadWriteOnce。 Velero集成Restic工具对存储卷进行备份还原,当前不支持HostPath类型的存储卷,详情请参见Restic限制。若您需备份该类型的存储卷,请将HostPath类型替换为Local类型。当备份任务中存在HostPath类型的存储,该类型存储卷将会被自动跳过并产生Warning信息,并不会导致备份失败。
  • 目标集群安装E-Backup插件 E-Backup为CCE提供集群备份恢复能力。如在CCE中使用E-Backup恢复,需要安装插件并创建存储库。 安装E-Backup插件 登录CCE控制台,单击左侧导航栏的“插件市场”,找到e-backup插件,单击e-backup插件下的“安装”。 在安装插件页面,选择要安装的集群,配置参数,然后单击“安装”。 当前支持配置如下参数。 volumeWorkerNum:备份volume的工作并发数量,默认为3。 创建密钥 获取访问密钥。 登录CCE控制台,在右上角用户名下选择“我的凭证”,在左侧选择“访问密钥”,单击“新增访问密钥”。 创建密钥文件,并通过 base64 格式化成字符串。 # 创建密钥文件$ vi credential-for-huawei-obsHUAWEI_CLOUD_AC CES S_KEY_ID=your_access_keyHUAWEI_CLOUD_SECRET_ACCESS_KEY=your_secret_key# 使用 base64 格式化字符串$ base64 -w 0 credential-for-huawei-obsXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXHWOBS 创建Secret。 按如下YAML文件创建Secret。 apiVersion: v1kind: Secretmetadata: labels: secret.everest.io/backup: 'true' #标识该secret用于E-Backup访问备份存储库 name: secret-secure-opaque namespace: velero #必须和E-Backup置于同一namespace,取值必须为velerotype: cfe/secure-opaquedata: # credential文件经过base64编码后得到的字符串 cloud: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXHWOBS secret 所在 namespace 必须和 E-Backup 实例所在namespace一致,即 velero。 secret.data 中存储的是访问 对象存储服务 的秘钥,其中 key 必须为 cloud,而 value 为2中通过 base64 编码得到的字符串。一般通过 base64 编码后显示的字符串会有换行符,请在写入 secret.data 中时手动去除这些换行符。 secret 需要打上标签“secret.everest.io/backup: true”,标识该 secret 是用于备份存储库的管理。 创建存储库 这里的备份存储库是指 E-Backup 用于获取和检测后端对象存储服务相关信息的 K8s 资源对象。 apiVersion: velero.io/v1kind: BackupStorageLocationmetadata: name: backup-location-001 namespace: velero #必须和E-Backup处于同一namespacespec: config: endpoint: obs.cn-north-4.myhuaweicloud.com # OBS的endpoint credential: name: secret-secure-opaque # 此前创建的secret的名字 key: cloud # secret.data中的key值 objectStorage: bucket: velero # OBS中的桶名 provider: huawei # 使用OBS服务 除了 prefix 字段为选填外,其他字段必填。provider 为固定值 huawei。 endpoint 可以到地区和终端节点获取,都需要保证集群内各节点可访问该地址。当endpoint 不带协议头时(http或者https),默认启用 https。 credential中的 name 和 key 需要配置正确,否则 E-Backup 无法访问后端存储库。 创建完成后等待30s用于备份存储库的检查和同步等工作,随后查看该备份存储库状态是否可用,PHASE 为 Available 表示可用,其他表示不可用。 $ kubectl get backupstoragelocations.velero.io backup-location-001 -n velero NAME PHASE LAST VALIDATED AGE DEFAULTbackup-location-001 Available 23s 23m 此处如果PHASE 长时间没有变成Available,可通过查看E-Backup的日志定位问题。E-Backup安装后会在velero命名空间创建一个名为velero的工作负载,查看velero的日志即可。
  • 操作场景 本节介绍在自建集群中使用开源Velero备份,在CCE集群使用E-Backup插件做恢复的方法。 本文使用Wordpress应用为例,将自建Kubernetes集群中的应用整体迁移到CCE集群。Wordpress应用包含Wordpress和MySQL两个组件,均为容器化实例,分别绑定了两个Local类型的本地存储卷,并通过NodePort服务对外提供访问。 迁移前通过浏览器访问Wordpress站点,创建站点名称为“Migrate to CCE”,并发布一篇文章用于验证迁移后PV数据的完整性。Wordpress中发布的文章会被存储在MySQL数据库的“wp_posts”表中,若迁移成功,数据库中的内容也将会被全量搬迁至新集群,可依此进行PV数据迁移校验。
  • 目标集群应用恢复(E-Backup) 如果在CCE中使用E-Backup恢复集群,可以使用如下步骤。 将某个立即备份作为数据源,恢复应用到另一个集群 中,全场景适用。 编辑 Restore 模板,如下所示,随后通过 kubectl create 命令创建。 apiVersion: velero.io/v1kind: Restoremetadata: name: restore-01 namespace: velerospec: backupName: wordpress-backup includedNamespaces: - default storageClassMapping: local: csi-disk imageRepositoryMapping: quay.io/coreos: swr.cn-north-4.myhuaweicloud.com/everest backupName:指定某个立即备份作为数据源,对该备份中的内容进行恢复,必填项。 storageClassMapping:改变备份资源PV、PVC等使用的storageClassName,要求StorageClass类型相同。本示例中将本地local改为CCE支持的csi-disk。 imageRepositoryMapping:改变备份资源的images字段,用于仓库的映射关系,不包含镜像名字和标签的改变(防止迁移和升级耦合在一起),比如:quay.io/coreos/etcd:2.5 搬迁到SWR后,使用本地镜像仓库下 swr.cn-north-4.myhuaweicloud.com/everest/etcd:2.5,配置格式为:quay.io/coreos: swr.cn-north-4.myhuaweicloud.com/everest 这里如果配置了storageClassMapping和imageRepositoryMapping,那镜像更新适配和StorageClass更新适配就无需再次适配。 其他参数请参见E-Backup。 恢复执行后,可通过如下命令查看恢复状态。 $ kubectl -n velero get restores restore-01 -o yaml | grep " phase" phase: Completed 状态Completed表示恢复完成,此时可以去CCE控制台查看具体应用恢复情况。
  • 镜像更新适配 由于本例使用的Wordpress和MySQL镜像均可从SWR正常拉取,因此不会出现镜像拉取失败(ErrImagePull)问题。如迁移应用为私有镜像,请执行以下步骤完成镜像更新适配。 将镜像资源迁移至容器 镜像服务 (SWR),具体步骤请参考客户端上传镜像。 登录SWR控制台查看获取迁移后的镜像地址。 镜像地址格式如下: 'swr.{区域}.myhuaweicloud.com/{所属组织名称}/{镜像名称}:{版本名称}' 使用如下命令对工作负载进行修改,并将YAML文件中的image字段替换成迁移后的镜像地址。 kubectl edit deploy wordpress 查看应用实例运行情况。
  • 访问服务更新适配 集群迁移后,原有集群的访问服务可能无法生效,可执行如下步骤更新服务。如原集群中设置了Ingress资源,迁移后需重新对接ELB,您可参考添加Ingress-对接已有ELB。 通过kubectl连接集群。 编辑对应Service的YAML文件,修改服务类型及端口。 kubectl edit svc wordpress LoadBanlancer资源进行更新时,需要重新对接ELB。请参考通过kubectl命令行创建-使用已有ELB,添加如下Annotation: annotations: kubernetes.io/elb.class: union #共享型ELB kubernetes.io/elb.id: 9d06a39d-xxxx-xxxx-xxxx-c204397498a3 #ELB的ID,可前往ELB控制台查询 kubernetes.io/elb.subnet-id: f86ba71c-xxxx-xxxx-xxxx-39c8a7d4bb36 #集群所在子网的ID kubernetes.io/session-affinity-mode: SOURCE_IP #开启会话保持,基于源IP地址 浏览器访问查看服务是否可用。
  • 数据库更新适配 本例中数据库为本地MySQL数据库,迁移后无需重新配置。若您通过 数据复制服务 DRS将本地数据库迁移至云数据库RDS,则在迁移后需重新配置数据库的访问,请您根据实际情况进行配置。 若云数据库RDS实例与CCE集群处于同一VPC下,则可通过内网地址访问,否则只能通过绑定EIP的方式进行公网访问。建议使用内网访问方式,安全性高,并且可实现RDS的较好性能。 请确认RDS所在安全组入方向规则已对集群放通,否则将连接失败。 登录RDS控制台,在该实例的“基本信息”页面获取其“内网地址”及端口。 使用如下命令对Wordpress工作负载进行修改。 kubectl edit deploy wordpress 设置env字段下的环境变量: WORDPRESS_DB_HOST:数据库的访问地址和端口,即上一步中获取的内网地址及端口。 WORDPRESS_DB_USER:访问数据库的用户名。 WORDPRESS_DB_PASSWORD:访问数据库的密码。 WORDPRESS_DB_NAME:需要连接的数据库名。 检查RDS数据库是否正常连接。
  • 数据库与存储迁移 数据库迁移 由运维或者开发人员基于华为云数据复制服务DRS完成数据库迁移,详情请参见跨云数据库在线迁移。 存储迁移 由运维或者开发人员基于华为云 对象存储迁移 服务 OMS 完成对象存储中的数据迁移,详情请参见对象存储迁移服务 OMS。 目前对象存储迁移服务OMS支持将亚马逊云、阿里云、微软云、百度云、金山云、青云、七牛云、腾讯云平台的对象存储数据迁移到华为云对象存储服务OBS。 在华为云对象存储服务OBS上创建桶,详情请参见创建桶。 在华为云对象存储迁移服务OMS上创建迁移任务,详情请参见创建单个迁移任务。
  • 准备对象存储MinIO MinIO官网地址:https://docs.min.io/ 准备对象存储,保存其AK/SK。 安装MinIO。 MinIO is a high performance,distributed,Kubernetes Native Object Storage. # 二进制安装 mkdir /opt/minio mkdir /opt/miniodata cd /opt/minio wget https://dl.minio.io/server/minio/release/linux-amd64/minio chmod +x minio export MINIO_ACCESS_KEY=minio export MINIO_SECRET_KEY=minio123 ./minio server /opt/miniodata/ & 浏览器输入: http://{minio所在节点的eip}:9000 (注意防火墙、安全组需要放开对应端口) # kubectl容器化安装 # 如果需要将minio发布为集群外可访问的服务,请修改00-minio-deployment.yaml中的服务类型为NodePort或LoadBalancer kubectl apply -f ./velero-v1.4.0-linux-amd64/examples/minio/00-minio-deployment.yaml 创建后面迁移需要使用的桶。 打开minio的web页面 使用MINIO_ACCESS_KEY/MINIO_SECRET_KEY登录minio,本文中为minio/minio123 单击‘+’上方的“Create bucket”,创建桶,本文中桶名为velero
  • 准备Velero Velero官网地址:https://velero.io/docs/v1.4/contributions/minio/ Velero is an open source tool to safely backup and restore, perform disaster recovery, and migrate Kubernetes cluster resources and persistent volumes. 在ACK和CCE的可执行kubectl命令的节点上执行如下操作: 下载迁移工具Velero 从https://github.com/heptio/velero/releases下载最新的稳定版 本文下载的是velero-v1.4.0-linux-amd64.tar.gz 安装Velero客户端 mkdir /opt/ack2cce cd /opt/ack2cce tar -xvf velero-v1.4.0-linux-amd64.tar.gz -C /opt/ack2cce cp /opt/ack2cce/velero-v1.4.0-linux-amd64/velero /usr/local/bin 安装Velero服务端 cd /opt/ack2cce # 准备minio认证文件,ak/sk要正确 vi credentials-velero [default] aws_access_key_id = minio aws_secret_access_key = minio123 # 安装velero服务端,注意s3Url要修改为正确的minio地址 velero install \ --provider aws \ --plugins velero/velero-plugin-for-aws:v1.0.0 \ --bucket velero \ --secret-file ./credentials-velero \ --use-restic \ --use-volume-snapshots=false \ --backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://{minio所在节点的eip}:9000
  • 安装Velero 首先前往OBS控制台或MinIO console界面,创建存放备份文件的桶并命名为velero。此处桶名称可自定义,但安装Velero时必须指定此桶名称,否则将无法访问导致备份失败,参见4。 原集群和目标集群中均需要安装部署Velero实例,安装步骤一致,分别用于备份和恢复。 CCE集群的Master节点不对外提供远程登录端口,您可通过kubectl操作集群完成Velero安装。 如果备份资源量较大,请调整Velero及Restic工具的cpu和内存资源(建议调整至1U1G及以上)。 用于存放备份文件的对象存储桶需要是空桶。 从Velero官方发布路径https://github.com/vmware-tanzu/velero/releases下载最新的稳定版二进制文件,本文以Velero 1.7.0版本为例。原集群和目标集群中的安装过程一致,请参考如下步骤。 下载Velero 1.7.0版本的二进制文件。 wget https://github.com/vmware-tanzu/velero/releases/download/v1.7.0/velero-v1.7.0-linux-amd64.tar.gz 安装Velero客户端。 tar -xvf velero-v1.7.0-linux-amd64.tar.gzcp ./velero-v1.7.0-linux-amd64/velero /usr/local/bin 创建备份对象存储访问密钥文件credentials-velero。 vim credentials-velero 文件内容如下,其中的AK/SK请根据实际情况进行替换。使用OBS时,可参考获取访问密钥(AK/SK)获取AK/SK。如使用MinIO,此处AK/SK则为2中所创建的用户名及密码。 [default]aws_access_key_id = {AK}aws_secret_access_key = {SK} 部署Velero服务端。注意其中--bucket参数需要修改为已创建的对象存储桶名称,本例中为velero。关于更多自定义安装参数,请参考自定义安装Velero。 velero install \ --provider aws \ --plugins velero/velero-plugin-for-aws:v1.2.1 \ --bucket velero \ --secret-file ./credentials-velero \ --use-restic \ --use-volume-snapshots=false \ --backup-location-config region=cn-north-4,s3ForcePathStyle="true",s3Url=http://obs.cn-north-4.myhuaweicloud.com 表1 Velero安装参数说明 安装参数 参数说明 --provider 声明使用“aws”提供的插件类型。 --plugins 使用AWS S3兼容的API组件,本文使用的OBS和MinIO对象存储均支持该S3协议。 --bucket 用于存放备份文件的对象存储桶名称,需提前创建。 --secret-file 访问对象存储的密钥文件,即3中创建的“credentials-velero”文件。 --use-restic 使用Restic工具支持PV数据备份,建议开启,否则将无法备份存储卷资源。 --use-volume-snapshots 是否创建 VolumeSnapshotLocation 对象进行PV快照,需要提供快照程序支持。该值设为false。 --backup-location-config 对象存储桶相关配置,包括region、s3ForcePathStyle、s3Url等。 region 对象存储桶所在区域。 OBS:请根据实际区域填写,如“cn-north-4”。 MinIO:参数值为minio。 s3ForcePathStyle 参数值为“true”,表示使用S3文件路径格式。 s3Url 对象存储桶的API访问地址。 OBS:该参数值需根据对象存储桶地域决定,参数值为“http://obs.{region}.myhuaweicloud.com”。例如区域为北京四(cn-north-4),则参数值为“http://obs.cn-north-4.myhuaweicloud.com”。 MinIO:该参数值需根据MinIO安装节点的IP及暴露端口确定,参数值为“http://{minio所在节点的eip}:9000”。 说明: s3Url中的访问端口需填写MinIO的API端口,而非console端口。MinIO API端口默认为9000。 访问集群外安装的MinIO时,需填写其公网IP地址。 Velero实例将默认创建一个名为velero的namespace,执行以下命令可查看pod状态。 $ kubectl get pod -n veleroNAME READY STATUS RESTARTS AGErestic-rn29c 1/1 Running 0 16svelero-c9ddd56-tkzpk 1/1 Running 0 16s 为防止在实际生产环境中备份时出现内存不足的情况,建议您修改Restic和Velero分配的CPU和内存大小。 查看Velero工具与对象存储的对接情况,状态需要为available。 $ velero backup-location getNAME PROVIDER BUCKET/PREFIX PHASE LAST VALIDATED ACCESS MODE DEFAULTdefault aws velero Available 2021-10-22 15:21:12 +0800 CS T ReadWrite true
  • 前提条件 原始自建集群Kubernetes版本需1.10及以上,且集群可正常使用DNS与互联网服务。 若您使用OBS存放备份文件,需已有OBS操作权限用户的AK/SK,请参考获取访问密钥(AK/SK)。 若您使用MinIO存放备份文件,则安装MinIO的服务器需要绑定EIP并在安全组中开放MinIO的API端口和Console端口。 已创建迁移的目标CCE集群。 原集群和目标集群中需要至少各拥有一个空闲节点,节点规格建议为4U8G及以上。
共99354条