searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

天翼云OpenAPI实践——创建一台云主机

2023-05-24 06:08:24
597
0

一、  准备

来到天翼云官网(中国电信-天翼云,云网融合,安全可信,专享定制),注册相应账号。

点击“文档”,后选择“弹性云主机”

分别点击“API参考”、“调用前必知”,我们看到“概述”、“终端节点”、“请求状态码”

我们从这三部分得知相应信息:

① 目前支持的资源池(以当前为例,选择华东1);

② 终端节点为ctecs-global.ctapi.ctyun.cn

③ API请求出现的状态码代表的含义。

点击“如何调用API”,我们看到“构造请求”、“认证鉴权”

从这两部分信息得知重要信息:

① 请求URI格式为{URI-scheme}://{Endpoint}/{resource-path}?{query-string};

② 需要使用AK SK信息。

AK、SK信息我们点击右上角“个人中心”,进入界面后,分别点击的“安全设置”、“用户AccessKey”、“查看”

二、  使用OpenAPI

1、  python调用示例模板

# -*- coding: utf8 -*-
import urllib
import datetime
import requests
import json
import hashlib
import base64
import hmac
import datetime
import uuid
import os
import sys

if sys.version_info.major == 2:
    from urllib import quote

    reload(sys)
    sys.setdefaultencoding('utf8')
    VERSION = '2'
else:
    VERSION = '3'
    from urllib.parse import quote
    from importlib import reload
from urllib3 import encode_multipart_formdata
from collections import OrderedDict

reload(sys)

METHOD_GET = 'GET'
METHOD_POST = 'POST'
file = False
# 官网accessKey
ak = ''
# 官网securityKey
sk = ''


def hmac_sha256(secret, data):
    if type(secret) == bytes:
        secret = bytearray(secret)
    else:
        secret = bytearray(secret, 'utf8')
    data = bytearray(data, 'utf8')
    return hmac.new(secret, data, digestmod=hashlib.sha256).digest()


def base64_of_hmac(data):
    return base64.b64encode(data)


def get_request_uuid():
    return str(uuid.uuid1())


def get_sorted_str(data, method):
    """
    鉴权用的参数整理
    :param data: dict 需要整理的参数
    :return: str
    """
    sorted_data = sorted(data.items(), key=lambda item: item[0])
    str_list = map(lambda x_y: '%s=%s' % (x_y[0], x_y[1]), sorted_data)

    return '&'.join(str_list)


def build_sign(query_params, body_params, eop_date, request_uuid, method):
    """
    计算鉴权字段
    :param query_params: dict get请求中的参数
    :param body_params: dict post请求中的参数
    :param eop_date: str 请求时间,格式为:'%Y%m%dT%H%M%SZ'
    :return: str
    """
    body_str = ""
    if not file:
        body_str = json.dumps(body_params) if body_params else ''
    if method == METHOD_POST:
        if file:
            body_digest = hashlib.sha256(body_params).hexdigest()
        else:
            if isinstance(body_params, dict):
                body_digest = hashlib.sha256(json.dumps(body_params).encode('utf-8')).hexdigest()
            else:
                body_digest = hashlib.sha256(body_params.encode('utf-8')).hexdigest()
    else:
        body_digest = hashlib.sha256(body_str.encode('utf-8')).hexdigest()
    # 请求头中必要的两个参数
    header_str = 'ctyun-eop-request-id:%s\neop-date:%s\n' % (request_uuid, eop_date)
    # url中的参数,或get参数
    query_str = encodeQueryStr(get_sorted_str(query_params, method))
    signature_str = '%s\n%s\n%s' % (header_str, query_str, body_digest)
    print_log(repr('signature_str is: %s' % signature_str))
    sign_date = eop_date.split('T')[0]

    # 计算鉴权密钥
    k_time = hmac_sha256(sk, eop_date)
    k_ak = hmac_sha256(k_time, ak)
    k_date = hmac_sha256(k_ak, sign_date)

    signature_base64 = base64_of_hmac(hmac_sha256(k_date, signature_str))
    # 构建请求头的鉴权字段值
    sign_header = '%s Headers=ctyun-eop-request-id;eop-date Signature=%s' % (ak, signature_base64.decode('utf8'))
    print("sign_header             :" + sign_header)
    return sign_header.encode('utf8')


def get_sign_headers(query_params, body, method, content_type):
    """
    获取鉴权用的请求头参数
    :param query_params: dict get请求中的参数
    :param body: dict post请求中的参数
    :return:
    """
    now = datetime.datetime.now()
    eop_date = datetime.datetime.strftime(now, '%Y%m%dT%H%M%SZ')
    request_uuid = get_request_uuid()
    headers = {  # 三个鉴权用的参数
        'Content-type': content_type,
        'ctyun-eop-request-id': request_uuid,
        'Eop-Authorization': build_sign(query_params=query_params, body_params=body, eop_date=eop_date,
                                        request_uuid=request_uuid, method=method),
        'Eop-date': eop_date,
    }
    return headers


def get(url, query="", params=None, header_params=None):
    return execute(url, query, method=METHOD_GET, params=params, header_params=header_params)


def post(url, query="", params=None, header_params=None, content_type='application/json;charset=UTF-8'):
    return execute(url, query, method=METHOD_POST, params=params, header_params=header_params,
                   content_type=content_type)


def execute(url, query, method, params=None, header_params=None, content_type='application/json;charset=UTF-8'):
    if 'application/x-www-form-urlencoded' in content_type:
        if VERSION == '2':
            params = urllib.urlencode(params)
        else:
            params = urllib.parse.urlencode(params)
    params = params or {}
    header_params = header_params or {}
    if method == METHOD_GET:
        query_params, body = (params, {})
    else:
        query_params = {}
        if len(query) > 0:
            for q in query.split('&'):
                query_params[q.split("=")[0]] = q.split("=")[1]
        body = params
    headers = get_sign_headers(query_params, body, method, content_type)
    headers.update(header_params)
    headers.update(
        {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/110.0"}
    )

    url = url + "?" + encodeQueryStr(query)
    print_log('url: %s' % url)
    print_log('请求方式: %s' % method)
    print_log('请求头:\n %s' % headers)
    print_log('请求参数:\n %s' % params)
    print_log('请求参数类型:\n %s' % type(params))
    requests.packages.urllib3.disable_warnings()
    if method == METHOD_GET:
        res = requests.get(url, params=params, headers=headers, verify=False)
    else:
        if 'application/x-www-form-urlencoded' in content_type:
            res = requests.post(url, data=params, headers=headers, verify=False)
        elif 'multipart/form-data' in content_type:
            res = requests.post(url, data=params, headers=headers, verify=False)
        else:
            res = requests.post(url, json=params, headers=headers, verify=False)

    print_log('返回状态码: %s' % res.status_code)
    print_log('返回: %s' % res.json())
    return res


def print_log(log_info):
    now = datetime.datetime.now()
    log_info = '[%s]: %s' % (str(now), log_info)
    print(log_info)


def encodeQueryStr(query):
    afterQuery = ""
    if (len(query) != 0):
        param = query.split("&")
        param.sort()
        for str in param:
            if (len(afterQuery) < 1):
                s = str.split("=")
                if (len(s) <= 2):
                    encodeStr = quote(s[1])
                    str = s[0] + "=" + encodeStr
                    afterQuery = afterQuery + str
                else:
                    encodeStr = ""
                    str = s[0] + "=" + encodeStr
                    afterQuery = afterQuery + str
            else:
                s = str.split("=")
                if (len(s) >= 2):
                    encodeStr = quote(s[1])
                    str = s[0] + "=" + encodeStr
                    afterQuery = afterQuery + "&" + str
                else:
                    encodeStr = ""
                    str = s[0] + "=" + encodeStr
                    afterQuery = afterQuery + "&" + str

    return afterQuery


params = {
    "regionID": "bb9fdb42056f11eda1610242ac110002",
    "azName": "cn-huadong1-jsnj2A-public-ctcloud",
    "ID": "0dea5ec4-6220-a0bf-f65a-dc323c579d8b"
}
# 发起请求
post("https://ctecs-global.ctapi.ctyun.cn/v4/ecs/instance-list", query='', params=params)


def generate_body(file_list, boundary, params):
    lastbody = bytearray()
    for file in file_list:
        file_name_key = list(file.keys())[0]
        file_path = list(file.values())[0]
        file_name = os.path.basename(file_path)
        body1_array = bytearray(
            '--' + boundary + "\r\n" + "Content-Disposition: form-data; name=\"" + file_name_key + "\"; filename=\"" + file_name + "\r\n" + "Content-Type: application/octet-stream" + "\r\n" + "\r\n",
            'utf8')
        if os.path.exists(file_path):
            with open(file_path, 'rb') as f:
                body2_array = bytearray(f.read())
        body3_array = bytearray("\r\n", 'utf8')
        lastbody.extend(body1_array)
        lastbody.extend(body2_array)
        lastbody.extend(body3_array)

    for param in params:
        body1_array = bytearray(
            '--' + boundary + "\r\n" + "Content-Disposition: form-data; name=\"" + list(param.keys())[
                0] + "\r\n" + "\r\n" + list(param.values())[0] + "\r\n", 'utf8')
        body3_array = bytearray("\r\n")
        lastbody.extend(body1_array)
        lastbody.extend(body3_array)
    body4_array = bytearray('--' + boundary + "--\r\n", 'utf8')
    lastbody.extend(body4_array)
    return lastbody

通过该示例,我们只需知道自己的用户AK、SK,以及要调用的OpenAPI信息(包括uri、请求方式和参数),便可调用天翼云OpenAPI了。

 

2、  创建云主机参数准备

创建云主机所需信息较多,我们通过官网文档找出关键信息。

我们打开创建云主机的OpenAPI文档,摘取出重要的三个信息:

① URI为 /v4/ecs/create-instance

② 请求方式为POST

③ 参数包括必填参数与非必填参数,这里我们以最简单的例子使用必填参数来创建(如有需求,可按照必填参数方式增加对应参数)

 

必填参数一部分是需要我们自行填写,还有一部分需要我们通过其他方式进行查询

自行填写参数:

参数

类型

说明

clientToken

String

保证幂等性的参数,可自行填写

instanceName

String

云主机名称,可自行填写

displayName

String

展示名称,可自行填写

imageType

Integer

镜像类型,这里选择共有镜像,即1

bootDiskType

String

系统盘类型,这里选SATA,普通云盘

bootDiskSize

Integer

系统盘大小,这里使用最小值40

onDemand

Boolean

包周期还是按需,这里我们选用按需(true)

extIP

String

是否使用弹性公网IP,这里选择不使用(“0”)

需要再查询的参数:

参数

类型

说明

regionID

String

资源池ID,需要查询

azName

String

可用区名称,需要查询

flavorID

String

规格ID,需要查询

imageID

String

镜像ID,需要查询共有镜像

vpcID

String

虚拟私有云ID,需要查询

networkCardList

String

网卡列表,需要查子网ID(subnetID)

 

3、  调用OpenAPI查询参数信息

部分参数我们需要通过调用其他OpenAPI来查询或创建出相应资源。

(1)regionID

找到天翼云OpenAPI弹性云主机文档,点击“资源池信息”、“资源池列表查询”

还是得到重要的三个信息:

① URI: /v4/region/list-regions

② GET请求

③ 无需参数

由此我们可以按照上述python方法进行调用:

# get方法调用
res_list_regions = get("https://ctecs-global.ctapi.ctyun.cn/v4/region/list-regions", query='', params={})

得到结果:

[2023-05-23 10:10:09.770222]: 返回状态码: 200
[2023-05-23 10:10:09.771228]: 返回: {'returnObj': {'regionList': [{'isMultiZones': False, 'regionParent': '湖南', 'regionID': '6f925df23a3811e9948d0242ac110002', 'regionType': 'openstack', 'zoneList': [], 'regionName': '郴州'},
... # 省略
, 'errorCode': '', 'message': '', 'description': '', 'statusCode': 800}

使用python进行筛选结果:

regionList = res_list_regions.json().get("returnObj").get("regionList")
region_huadong = list(filter(lambda x: x.get("regionName") == "华东1", regionList))
print("华东1: ", region_huadong)
region_id_huadong = region_huadong[0].get("regionID")
print("华东1 regionID: ", region_id_huadong)

得到结果:

华东1: [{'isMultiZones': True, 'regionParent': '华东', 'regionID': 'bb9fdb42056f11eda1610242ac110002', 'regionType': 'openstack', 'zoneList': ['cn-huadong1-jsnj1A-public-ctcloud', 'cn-huadong1-jsnj2A-public-ctcloud', 'cn-huadong1-jsnj3A-public-ctcloud'], 'regionName': '华东1'}]
华东1 regionID: bb9fdb42056f11eda1610242ac11000

 

(2)azName

同样的方式在天翼云官网文档中,找到资源池可用区查询

得到重要的三个信息:

① URI: /v4/region/get-zones

② GET请求

③ 参数regionID

 

调用OpenAPI查询:

res_get_zones = get("https://ctecs-global.ctapi.ctyun.cn/v4/region/get-zones", query='', params=dict(regionID=region_id_huadong))

结果:

[2023-05-23 10:22:03.018835]: 返回状态码: 200

[2023-05-23 10:22:03.018835]: 返回: {'returnObj': {'zoneList': [{'name': 'cn-huadong1-jsnj1A-public-ctcloud', 'azDisplayName': '可用区1'}, {'name': 'cn-huadong1-jsnj2A-public-ctcloud', 'azDisplayName': '可用区2'}, {'name': 'cn-huadong1-jsnj3A-public-ctcloud', 'azDisplayName': '可用区3'}]}, 'errorCode': '', 'message': '', 'description': '', 'statusCode': 800}

取出相应结果:

# 这里我们选取可用区1

zoneList = res_get_zones.json().get("returnObj").get("zoneList")

zone1 = list(filter(lambda x: x.get("azDisplayName") == '可用区1', zoneList))

print("可用区1: ", zone1)

az_name1 = zone1[0].get("name")

print("可用区1 azName: ", az_name1)

结果:

res_get_zones = get("https://ctecs-global.ctapi.ctyun.cn/v4/region/get-zones", query='', params=dict(regionID=region_id_huadong))

 

(3)flavorID

找到资源池可用区查询,得到重要的三个信息:

① URI: /v4/ecs/flavor/list

② POST请求

③ 参数regionID、azName

 

调用OpenAPI查询:

query_flavor_params = dict(regionID=region_id_huadong, azName=az_name1)

res_flavor_list = post("https://ctecs-global.ctapi.ctyun.cn/v4/ecs/flavor/list", query='', params=query_flavor_params)

结果:

[2023-05-23 14:40:38.974314]: 返回状态码: 200

[2023-05-23 14:40:38.974732]: 返回: {'returnObj': {'flavorList': [{'flavorID': '34e1b6f6-e974-1575-20b2-172ba0e0bf83', 'flavorName': 's7.small.1', 'flavorCPU': 1, 'flavorRAM': 1, 'baseBandwidth': 0.1, 'bandwidth': 0.8, 'flavorSeries': 's', 'nicMultiQueue': 1, 'pps': 10, 'flavorType': 'CPU_S7', 'cpuInfo': 'x86', 'gpuVendor': None, 'videoMemSize': None, 'gpuType': None, 'gpuCount': None},

..., # 省略

'videoMemSize': 24, 'gpuType': 'A10', 'gpuCount': 1}]}, 'details': '', 'message': 'SUCCESS', 'description': '成功', 'statusCode': 800}

我们选用规格s7.small.1

flavorList = res_flavor_list.json().get("returnObj").get("flavorList")

s7_flavor = list(filter(lambda x: x.get("flavorName") == "s7.small.1", flavorList))

print("s7.small.1 信息:", s7_flavor)

s7_flavor_id = s7_flavor[0].get("flavorID")

结果:

s7.small.1 信息: [{'flavorID': '34e1b6f6-e974-1575-20b2-172ba0e0bf83', 'flavorName': 's7.small.1', 'flavorCPU': 1, 'flavorRAM': 1, 'baseBandwidth': 0.1, 'bandwidth': 0.8, 'flavorSeries': 's', 'nicMultiQueue': 1, 'pps': 10, 'flavorType': 'CPU_S7', 'cpuInfo': 'x86', 'gpuVendor': None, 'videoMemSize': None, 'gpuType': None, 'gpuCount': None}]

s7.small.1规格的flavorID为: 34e1b6f6-e974-1575-20b2-172ba0e0bf83

 

(4)imageID

我们切换产品列表,切换产品时,勿忘查询终端节点,镜像服务的终端节点为:ctimage-global.ctapi.ctyun.cn

选择“镜像服务”,分别点击“API”、“信息查询功能”、“查询可以使用的镜像资源”

得到重要的三个信息:

① URI: /v4/image/list

② GET请求

③ 参数regionID、azName、visibility(由于之前选用了公共镜像类型,所以这里查询要限制查询镜像类型,公共镜像为1)

 

调用OpenAPI查询:

image_list_params = dict(regionID=region_id_huadong, azName=az_name1, visibility=1)

res_image_list = get("https://ctimage-global.ctapi.ctyun.cn/v4/image/list", query='', params=image_list_params)

结果:

[2023-05-23 14:53:46.538614]: 返回状态码: 200

[2023-05-23 14:53:46.538614]: 返回: {'returnObj': {'images': [

... # 省略

{'architecture': 'x86_64', 'azName': None, 'bootMode': 'bios', 'containerFormat': 'bare', 'createdTime': 1664276810, 'description': '', 'destinationUser': None, 'diskFormat': 'raw', 'diskID': None, 'diskSize': 40, 'imageClass': 'ECS', 'imageID': 'e8929d7e-6cfd-4a03-bf76-1bc7f2aeb883', 'imageName': 'CentOS8.1 64位', 'imageType': None, 'maximumRAM': None, 'minimumRAM': None, 'osDistro': 'centos', 'osType': 'linux', 'osVersion': '8.1', 'projectID': None, 'sharedListLength': None, 'size': 42949672960, 'sourceServerID': None, 'sourceUser': None, 'status': 'active', 'tags': None, 'updatedTime': None, 'visibility': 'public'}], 'pageNo': 1, 'pageSize': 10, 'totalCount': 38}, 'errorCode': '', 'message': '', 'description': '', 'statusCode': 800}

我们选用镜像为CentOS7.8 64位的镜像

imageList = res_image_list.json().get("returnObj").get("images")

centos7_image = list(filter(lambda x: x.get("imageName") == "CentOS7.8 64位", imageList))

print("CentOS7.8 64位的镜像信息:", centos7_image)

centos7_8_image_id = centos7_image[0].get("imageID")

print("CentOS7.8 64位镜像的imageID为:", centos7_8_image_id)

结果:

CentOS7.8 64位的镜像信息: [{'architecture': 'x86_64', 'azName': None, 'bootMode': 'bios', 'containerFormat': 'bare', 'createdTime': 1664232729, 'description': '', 'destinationUser': None, 'diskFormat': 'raw', 'diskID': None, 'diskSize': 40, 'imageClass': 'ECS', 'imageID': '6e95ed90-b8b6-40ee-ae38-d851b6dc35e4', 'imageName': 'CentOS7.8 64位', 'imageType': None, 'maximumRAM': None, 'minimumRAM': None, 'osDistro': 'centos', 'osType': 'linux', 'osVersion': '7.8', 'projectID': None, 'sharedListLength': None, 'size': 42949672960, 'sourceServerID': None, 'sourceUser': None, 'status': 'active', 'tags': None, 'updatedTime': None, 'visibility': 'public'}]

CentOS7.8 64位镜像的imageID为: 6e95ed90-b8b6-40ee-ae38-d851b6dc35e4

 

(5)vpcID、subnetID

如果我们没有VPC和子网时,我们需要先创建VPC和子网:

我们切换产品列表至虚拟私有云,切换产品时,勿忘查询终端节点,虚拟私有云的终端节点为:ctvpc-global.ctapi.ctyun.cn

选择“镜像服务”,分别点击“API”、“VPC”、“创建VPC”

得到重要的三个信息:

① URI: /v4/vpc/create

② POST请求

③ 参数regionID、azName、clientToken、name、CIDR

 

调用OpenAPI创建(这里clientToken与name我们自定义,CIDR按照文档选用192.168.0.0/16)

vpc_create_params = dict(regionID=region_id_huadong, azName=az_name1, clientToken="createVPC0523", name="vpc-0523", CIDR="192.168.0.0/16")

res_vpc_create = post("https://ctvpc-global.ctapi.ctyun.cn/v4/vpc/create", query="", params=vpc_create_params)

结果:

[2023-05-23 15:31:40.438960]: 返回状态码: 200

[2023-05-23 15:31:40.438960]: 返回: {'statusCode': 800, 'errorCode': 'SUCCESS', 'message': 'success', 'description': '成功', 'returnObj': {'vpcID': 'vpc-hnkpq1dn46'}}

'destinationUser': None, 'diskFormat': 'raw', 'diskID': None, 'diskSize': 40, 'imageClass': 'ECS', 'imageID': 'e8929d7e-6cfd-4a03-bf76-1bc7f2aeb883', 'imageName': 'CentOS8.1 64位', 'imageType': None, 'maximumRAM': None, 'minimumRAM': None, 'osDistro': 'centos', 'osType': 'linux', 'osVersion': '8.1', 'projectID': None, 'sharedListLength': None, 'size': 42949672960, 'sourceServerID': None, 'sourceUser': None, 'status': 'active', 'tags': None, 'updatedTime': None, 'visibility': 'public'}], 'pageNo': 1, 'pageSize': 10, 'totalCount': 38}, 'errorCode': '', 'message': '', 'description': '', 'statusCode': 800}

 

subnet同理,打开创建子网

得到重要的三个信息:

① URI: /v4/vpc/create-subnet

② POST请求

③ 参数regionID、vpcID、clientToken、name、CIDR

 

调用OpenAPI创建(这里clientToken与name我们自定义,CIDR按照文档选用192.168.1.0/24)

subnet_create_params = dict(regionID=region_id_huadong, clientToken="createSubnet0523", name="subnet-0523", CIDR="192.168.1.0/24", vpcID='vpc-hnkpq1dn46')

res_subnet_create = post("https://ctvpc-global.ctapi.ctyun.cn/v4/vpc/create-subnet", query="", params=subnet_create_params)

结果:

[2023-05-23 15:36:52.879846]: 返回状态码: 200

[2023-05-23 15:36:52.879846]: 返回: {'statusCode': 800, 'errorCode': 'SUCCESS', 'message': 'success', 'description': '成功', 'returnObj': {'subnetID': 'subnet-v47niplom1'}}

 

如果已有对应VPC,可以查询VPC列表来拿到对应vpcID和subnetID

得到重要的三个信息:

① URI: /v4/vpc/list

② GET请求

③ 参数regionID、azName

 

调用OpenAPI查询:

vpc_list_params = dict(regionID=region_id_huadong, azName=az_name1)

res_vpc_list = get("https://ctvpc-global.ctapi.ctyun.cn/v4/vpc/list", query="", params=vpc_list_params)

结果:

[2023-05-23 15:10:45.332877]: 返回状态码: 200

[2023-05-23 15:10:45.332877]: 返回: {'statusCode': 800, 'errorCode': 'SUCCESS', 'message': 'success', 'description': '成功', 'returnObj': {'vpcs': [{'vpcID': 'vpc-xdgj37uuxc', 'name': 'cndc-hub-test-5-sh', 'description': '', 'CIDR': '10.0.0.0/24', 'ipv6Enabled': False, 'secondaryCIDRs': [], 'subnetIDs': ['subnet-80oyaklfdu'], 'natGatewayIDs': [], 'ipv6CIDRS': []},

... # 省略

]}, 'currentCount': 10, 'totalCount': 62, 'totalPage': 7}

我们选用第一个VPC和其中一个subnet

vpcList = res_vpc_list.json().get("returnObj").get("vpcs")

vpc_id = vpcList[0].get("vpcID")

print("vpcID:", vpc_id)

subnet_id = vpcList[0].get("subnetIDs")[0]

print("subnetID:", subnet_id)

结果:

vpcID: vpc-xdgj37uuxc

subnetID: subnet-80oyaklfdu

网卡networkCardList参数组装为:

network_card_list = [{"subnetID": subnet_id, "isMaster": true}]

 

4、  创建云主机

至此,我们的参数已经全部查询完毕,组装一下:

create_instance_params = {

        # 自行填写参数

        "clientToken": "createInstance0523",

        "instanceName": "api-create0523",

        "displayName": "ecm-0523",

        "imageType": 1,

        "bootDiskType": "SATA",

        "bootDiskSize": 40,

        "onDemand": True,

        "extIP": "0",

        # 查询参数

        "regionID": region_id_huadong,

        "azName": az_name1,

        "flavorID": s7_flavor_id,

        "imageID": centos7_8_image_id,

        "vpcID": vpc_id,

        "networkCardList": [{"subnetID": subnet_id, "isMaster": True}]

}

调用OpenAPI进行创建:

res_create_instance = post("https://ctecs-global.ctapi.ctyun.cn/v4/ecs/create-instance", query='', params=create_instance_params)

结果:

[2023-05-23 15:50:56.545144]: 返回状态码: 200

[2023-05-23 15:50:56.545144]: 返回: {'returnObj': {'masterResourceID': 'ee872731d881406d9f08d8ac073d2b6a', 'masterOrderNO': '20230523155045331859', 'regionID': 'bb9fdb42056f11eda1610242ac110002', 'masterOrderID': '853b6238f93e11edb0b10242ac110007'}, 'details': '', 'message': 'success', 'description': '成功', 'statusCode': 800}

 

5、  查看结果

对于创建的云主机,我们可以通过三种方式来看是否创建成功

(1)  控制台查看

最简单的就是登录天翼云官网,选择“控制中心”,资源池选择“华东”->“华东1”,然后点击“弹性云主机”

我们可以看到,展示名称ecm-0523的云主机处于“运行中”状态,此时说明云主机已创建成功。

 

(2)  OpenAPI查询云主机列表

官网文档找到查询云主机列表

得到重要的三个信息:

① URI: /v4/ecs/list-instances

② POST请求

③ 请求参数:regionID、azName、resourceID/instanceName(作为筛选条件)

 

a. 通过填写创建云主机时拿到的masterResourceID来查询(推荐)

instance_list_params = dict(regionID=region_id_huadong, azName=az_name1, resourceID="ee872731d881406d9f08d8ac073d2b6a")

res_instance_list = post("https://ctecs-global.ctapi.ctyun.cn/v4/ecs/list-instances", query='', params=instance_list_params)

b. 通过限制云主机名称来查询云主机

instance_list_params = dict(regionID=region_id_huadong, azName=az_name1, instanceName="api-create0523")

res_instance_list = post("https://ctecs-global.ctapi.ctyun.cn/v4/ecs/list-instances", query='', params=instance_list_params)

结果:

[2023-05-23 16:01:30.784235]: 返回状态码: 200

[2023-05-23 16:01:30.784235]: 返回: {'returnObj': {'currentCount': 1, 'totalCount': 1, 'totalPage': 1, 'results': [{'azName': 'cn-huadong1-jsnj1A-public-ctcloud', 'displayName': 'ecm-0523', 'instanceName': 'api-create0523', 'osType': 5, 'instanceStatus': 'running',

... # 省略

}]}, 'details': '', 'message': 'SUCCESS', 'description': '成功', 'statusCode': 800}

可以看出该云主机已存在,且云主机状态为running(运行中)

 

(3)  OpenAPI查询订单状态

官网文档中也提供了根据订单号查资源ID的方式,选择“弹性云主机”、“公共接口”,点击“根据订单号查询Uuid”

 

得到重要的三个信息:

① URI: /v4/order/queryUuid

② GET请求

③ 参数masterOrderId

 

调用OpenAPI查询:

query_order_params = dict(masterOrderId="853b6238f93e11edb0b10242ac110007")

res_query_order = get("https://ctecs-global.ctapi.ctyun.cn/v4/order/queryUuid", query='', params=query_order_params)

结果:

[2023-05-23 16:29:29.481958]: 返回状态码: 200

[2023-05-23 16:29:29.481958]: 返回: {'returnObj': {'resourceType': 'VM', 'resourceUUID': ['a905aba2-3052-60f6-ec37-aa45c9b6da25'], 'orderStatus': '3'}, 'errorCode': '', 'message': '', 'description': '', 'statusCode': 800}

可以看出订单状态为3,对照订单状态枚举表,得知为“完成”

 

 

三、  总结

       本文介绍了如何调用天翼云OpenAPI来创建云主机,通过官网文档,我们可以很好地找到对应参数,并且也可以通过OpenAPI进行查询。比较贴心的是,目前创建云主机的文档参数说明中已增加了相应需要查找的参数的OpenAPI文档超链接。可以很好的指导我们调用天翼云OpenAPI。

 

0条评论
0 / 1000
踏歌风雪
2文章数
0粉丝数
踏歌风雪
2 文章 | 0 粉丝
踏歌风雪
2文章数
0粉丝数
踏歌风雪
2 文章 | 0 粉丝
原创

天翼云OpenAPI实践——创建一台云主机

2023-05-24 06:08:24
597
0

一、  准备

来到天翼云官网(中国电信-天翼云,云网融合,安全可信,专享定制),注册相应账号。

点击“文档”,后选择“弹性云主机”

分别点击“API参考”、“调用前必知”,我们看到“概述”、“终端节点”、“请求状态码”

我们从这三部分得知相应信息:

① 目前支持的资源池(以当前为例,选择华东1);

② 终端节点为ctecs-global.ctapi.ctyun.cn

③ API请求出现的状态码代表的含义。

点击“如何调用API”,我们看到“构造请求”、“认证鉴权”

从这两部分信息得知重要信息:

① 请求URI格式为{URI-scheme}://{Endpoint}/{resource-path}?{query-string};

② 需要使用AK SK信息。

AK、SK信息我们点击右上角“个人中心”,进入界面后,分别点击的“安全设置”、“用户AccessKey”、“查看”

二、  使用OpenAPI

1、  python调用示例模板

# -*- coding: utf8 -*-
import urllib
import datetime
import requests
import json
import hashlib
import base64
import hmac
import datetime
import uuid
import os
import sys

if sys.version_info.major == 2:
    from urllib import quote

    reload(sys)
    sys.setdefaultencoding('utf8')
    VERSION = '2'
else:
    VERSION = '3'
    from urllib.parse import quote
    from importlib import reload
from urllib3 import encode_multipart_formdata
from collections import OrderedDict

reload(sys)

METHOD_GET = 'GET'
METHOD_POST = 'POST'
file = False
# 官网accessKey
ak = ''
# 官网securityKey
sk = ''


def hmac_sha256(secret, data):
    if type(secret) == bytes:
        secret = bytearray(secret)
    else:
        secret = bytearray(secret, 'utf8')
    data = bytearray(data, 'utf8')
    return hmac.new(secret, data, digestmod=hashlib.sha256).digest()


def base64_of_hmac(data):
    return base64.b64encode(data)


def get_request_uuid():
    return str(uuid.uuid1())


def get_sorted_str(data, method):
    """
    鉴权用的参数整理
    :param data: dict 需要整理的参数
    :return: str
    """
    sorted_data = sorted(data.items(), key=lambda item: item[0])
    str_list = map(lambda x_y: '%s=%s' % (x_y[0], x_y[1]), sorted_data)

    return '&'.join(str_list)


def build_sign(query_params, body_params, eop_date, request_uuid, method):
    """
    计算鉴权字段
    :param query_params: dict get请求中的参数
    :param body_params: dict post请求中的参数
    :param eop_date: str 请求时间,格式为:'%Y%m%dT%H%M%SZ'
    :return: str
    """
    body_str = ""
    if not file:
        body_str = json.dumps(body_params) if body_params else ''
    if method == METHOD_POST:
        if file:
            body_digest = hashlib.sha256(body_params).hexdigest()
        else:
            if isinstance(body_params, dict):
                body_digest = hashlib.sha256(json.dumps(body_params).encode('utf-8')).hexdigest()
            else:
                body_digest = hashlib.sha256(body_params.encode('utf-8')).hexdigest()
    else:
        body_digest = hashlib.sha256(body_str.encode('utf-8')).hexdigest()
    # 请求头中必要的两个参数
    header_str = 'ctyun-eop-request-id:%s\neop-date:%s\n' % (request_uuid, eop_date)
    # url中的参数,或get参数
    query_str = encodeQueryStr(get_sorted_str(query_params, method))
    signature_str = '%s\n%s\n%s' % (header_str, query_str, body_digest)
    print_log(repr('signature_str is: %s' % signature_str))
    sign_date = eop_date.split('T')[0]

    # 计算鉴权密钥
    k_time = hmac_sha256(sk, eop_date)
    k_ak = hmac_sha256(k_time, ak)
    k_date = hmac_sha256(k_ak, sign_date)

    signature_base64 = base64_of_hmac(hmac_sha256(k_date, signature_str))
    # 构建请求头的鉴权字段值
    sign_header = '%s Headers=ctyun-eop-request-id;eop-date Signature=%s' % (ak, signature_base64.decode('utf8'))
    print("sign_header             :" + sign_header)
    return sign_header.encode('utf8')


def get_sign_headers(query_params, body, method, content_type):
    """
    获取鉴权用的请求头参数
    :param query_params: dict get请求中的参数
    :param body: dict post请求中的参数
    :return:
    """
    now = datetime.datetime.now()
    eop_date = datetime.datetime.strftime(now, '%Y%m%dT%H%M%SZ')
    request_uuid = get_request_uuid()
    headers = {  # 三个鉴权用的参数
        'Content-type': content_type,
        'ctyun-eop-request-id': request_uuid,
        'Eop-Authorization': build_sign(query_params=query_params, body_params=body, eop_date=eop_date,
                                        request_uuid=request_uuid, method=method),
        'Eop-date': eop_date,
    }
    return headers


def get(url, query="", params=None, header_params=None):
    return execute(url, query, method=METHOD_GET, params=params, header_params=header_params)


def post(url, query="", params=None, header_params=None, content_type='application/json;charset=UTF-8'):
    return execute(url, query, method=METHOD_POST, params=params, header_params=header_params,
                   content_type=content_type)


def execute(url, query, method, params=None, header_params=None, content_type='application/json;charset=UTF-8'):
    if 'application/x-www-form-urlencoded' in content_type:
        if VERSION == '2':
            params = urllib.urlencode(params)
        else:
            params = urllib.parse.urlencode(params)
    params = params or {}
    header_params = header_params or {}
    if method == METHOD_GET:
        query_params, body = (params, {})
    else:
        query_params = {}
        if len(query) > 0:
            for q in query.split('&'):
                query_params[q.split("=")[0]] = q.split("=")[1]
        body = params
    headers = get_sign_headers(query_params, body, method, content_type)
    headers.update(header_params)
    headers.update(
        {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/110.0"}
    )

    url = url + "?" + encodeQueryStr(query)
    print_log('url: %s' % url)
    print_log('请求方式: %s' % method)
    print_log('请求头:\n %s' % headers)
    print_log('请求参数:\n %s' % params)
    print_log('请求参数类型:\n %s' % type(params))
    requests.packages.urllib3.disable_warnings()
    if method == METHOD_GET:
        res = requests.get(url, params=params, headers=headers, verify=False)
    else:
        if 'application/x-www-form-urlencoded' in content_type:
            res = requests.post(url, data=params, headers=headers, verify=False)
        elif 'multipart/form-data' in content_type:
            res = requests.post(url, data=params, headers=headers, verify=False)
        else:
            res = requests.post(url, json=params, headers=headers, verify=False)

    print_log('返回状态码: %s' % res.status_code)
    print_log('返回: %s' % res.json())
    return res


def print_log(log_info):
    now = datetime.datetime.now()
    log_info = '[%s]: %s' % (str(now), log_info)
    print(log_info)


def encodeQueryStr(query):
    afterQuery = ""
    if (len(query) != 0):
        param = query.split("&")
        param.sort()
        for str in param:
            if (len(afterQuery) < 1):
                s = str.split("=")
                if (len(s) <= 2):
                    encodeStr = quote(s[1])
                    str = s[0] + "=" + encodeStr
                    afterQuery = afterQuery + str
                else:
                    encodeStr = ""
                    str = s[0] + "=" + encodeStr
                    afterQuery = afterQuery + str
            else:
                s = str.split("=")
                if (len(s) >= 2):
                    encodeStr = quote(s[1])
                    str = s[0] + "=" + encodeStr
                    afterQuery = afterQuery + "&" + str
                else:
                    encodeStr = ""
                    str = s[0] + "=" + encodeStr
                    afterQuery = afterQuery + "&" + str

    return afterQuery


params = {
    "regionID": "bb9fdb42056f11eda1610242ac110002",
    "azName": "cn-huadong1-jsnj2A-public-ctcloud",
    "ID": "0dea5ec4-6220-a0bf-f65a-dc323c579d8b"
}
# 发起请求
post("https://ctecs-global.ctapi.ctyun.cn/v4/ecs/instance-list", query='', params=params)


def generate_body(file_list, boundary, params):
    lastbody = bytearray()
    for file in file_list:
        file_name_key = list(file.keys())[0]
        file_path = list(file.values())[0]
        file_name = os.path.basename(file_path)
        body1_array = bytearray(
            '--' + boundary + "\r\n" + "Content-Disposition: form-data; name=\"" + file_name_key + "\"; filename=\"" + file_name + "\r\n" + "Content-Type: application/octet-stream" + "\r\n" + "\r\n",
            'utf8')
        if os.path.exists(file_path):
            with open(file_path, 'rb') as f:
                body2_array = bytearray(f.read())
        body3_array = bytearray("\r\n", 'utf8')
        lastbody.extend(body1_array)
        lastbody.extend(body2_array)
        lastbody.extend(body3_array)

    for param in params:
        body1_array = bytearray(
            '--' + boundary + "\r\n" + "Content-Disposition: form-data; name=\"" + list(param.keys())[
                0] + "\r\n" + "\r\n" + list(param.values())[0] + "\r\n", 'utf8')
        body3_array = bytearray("\r\n")
        lastbody.extend(body1_array)
        lastbody.extend(body3_array)
    body4_array = bytearray('--' + boundary + "--\r\n", 'utf8')
    lastbody.extend(body4_array)
    return lastbody

通过该示例,我们只需知道自己的用户AK、SK,以及要调用的OpenAPI信息(包括uri、请求方式和参数),便可调用天翼云OpenAPI了。

 

2、  创建云主机参数准备

创建云主机所需信息较多,我们通过官网文档找出关键信息。

我们打开创建云主机的OpenAPI文档,摘取出重要的三个信息:

① URI为 /v4/ecs/create-instance

② 请求方式为POST

③ 参数包括必填参数与非必填参数,这里我们以最简单的例子使用必填参数来创建(如有需求,可按照必填参数方式增加对应参数)

 

必填参数一部分是需要我们自行填写,还有一部分需要我们通过其他方式进行查询

自行填写参数:

参数

类型

说明

clientToken

String

保证幂等性的参数,可自行填写

instanceName

String

云主机名称,可自行填写

displayName

String

展示名称,可自行填写

imageType

Integer

镜像类型,这里选择共有镜像,即1

bootDiskType

String

系统盘类型,这里选SATA,普通云盘

bootDiskSize

Integer

系统盘大小,这里使用最小值40

onDemand

Boolean

包周期还是按需,这里我们选用按需(true)

extIP

String

是否使用弹性公网IP,这里选择不使用(“0”)

需要再查询的参数:

参数

类型

说明

regionID

String

资源池ID,需要查询

azName

String

可用区名称,需要查询

flavorID

String

规格ID,需要查询

imageID

String

镜像ID,需要查询共有镜像

vpcID

String

虚拟私有云ID,需要查询

networkCardList

String

网卡列表,需要查子网ID(subnetID)

 

3、  调用OpenAPI查询参数信息

部分参数我们需要通过调用其他OpenAPI来查询或创建出相应资源。

(1)regionID

找到天翼云OpenAPI弹性云主机文档,点击“资源池信息”、“资源池列表查询”

还是得到重要的三个信息:

① URI: /v4/region/list-regions

② GET请求

③ 无需参数

由此我们可以按照上述python方法进行调用:

# get方法调用
res_list_regions = get("https://ctecs-global.ctapi.ctyun.cn/v4/region/list-regions", query='', params={})

得到结果:

[2023-05-23 10:10:09.770222]: 返回状态码: 200
[2023-05-23 10:10:09.771228]: 返回: {'returnObj': {'regionList': [{'isMultiZones': False, 'regionParent': '湖南', 'regionID': '6f925df23a3811e9948d0242ac110002', 'regionType': 'openstack', 'zoneList': [], 'regionName': '郴州'},
... # 省略
, 'errorCode': '', 'message': '', 'description': '', 'statusCode': 800}

使用python进行筛选结果:

regionList = res_list_regions.json().get("returnObj").get("regionList")
region_huadong = list(filter(lambda x: x.get("regionName") == "华东1", regionList))
print("华东1: ", region_huadong)
region_id_huadong = region_huadong[0].get("regionID")
print("华东1 regionID: ", region_id_huadong)

得到结果:

华东1: [{'isMultiZones': True, 'regionParent': '华东', 'regionID': 'bb9fdb42056f11eda1610242ac110002', 'regionType': 'openstack', 'zoneList': ['cn-huadong1-jsnj1A-public-ctcloud', 'cn-huadong1-jsnj2A-public-ctcloud', 'cn-huadong1-jsnj3A-public-ctcloud'], 'regionName': '华东1'}]
华东1 regionID: bb9fdb42056f11eda1610242ac11000

 

(2)azName

同样的方式在天翼云官网文档中,找到资源池可用区查询

得到重要的三个信息:

① URI: /v4/region/get-zones

② GET请求

③ 参数regionID

 

调用OpenAPI查询:

res_get_zones = get("https://ctecs-global.ctapi.ctyun.cn/v4/region/get-zones", query='', params=dict(regionID=region_id_huadong))

结果:

[2023-05-23 10:22:03.018835]: 返回状态码: 200

[2023-05-23 10:22:03.018835]: 返回: {'returnObj': {'zoneList': [{'name': 'cn-huadong1-jsnj1A-public-ctcloud', 'azDisplayName': '可用区1'}, {'name': 'cn-huadong1-jsnj2A-public-ctcloud', 'azDisplayName': '可用区2'}, {'name': 'cn-huadong1-jsnj3A-public-ctcloud', 'azDisplayName': '可用区3'}]}, 'errorCode': '', 'message': '', 'description': '', 'statusCode': 800}

取出相应结果:

# 这里我们选取可用区1

zoneList = res_get_zones.json().get("returnObj").get("zoneList")

zone1 = list(filter(lambda x: x.get("azDisplayName") == '可用区1', zoneList))

print("可用区1: ", zone1)

az_name1 = zone1[0].get("name")

print("可用区1 azName: ", az_name1)

结果:

res_get_zones = get("https://ctecs-global.ctapi.ctyun.cn/v4/region/get-zones", query='', params=dict(regionID=region_id_huadong))

 

(3)flavorID

找到资源池可用区查询,得到重要的三个信息:

① URI: /v4/ecs/flavor/list

② POST请求

③ 参数regionID、azName

 

调用OpenAPI查询:

query_flavor_params = dict(regionID=region_id_huadong, azName=az_name1)

res_flavor_list = post("https://ctecs-global.ctapi.ctyun.cn/v4/ecs/flavor/list", query='', params=query_flavor_params)

结果:

[2023-05-23 14:40:38.974314]: 返回状态码: 200

[2023-05-23 14:40:38.974732]: 返回: {'returnObj': {'flavorList': [{'flavorID': '34e1b6f6-e974-1575-20b2-172ba0e0bf83', 'flavorName': 's7.small.1', 'flavorCPU': 1, 'flavorRAM': 1, 'baseBandwidth': 0.1, 'bandwidth': 0.8, 'flavorSeries': 's', 'nicMultiQueue': 1, 'pps': 10, 'flavorType': 'CPU_S7', 'cpuInfo': 'x86', 'gpuVendor': None, 'videoMemSize': None, 'gpuType': None, 'gpuCount': None},

..., # 省略

'videoMemSize': 24, 'gpuType': 'A10', 'gpuCount': 1}]}, 'details': '', 'message': 'SUCCESS', 'description': '成功', 'statusCode': 800}

我们选用规格s7.small.1

flavorList = res_flavor_list.json().get("returnObj").get("flavorList")

s7_flavor = list(filter(lambda x: x.get("flavorName") == "s7.small.1", flavorList))

print("s7.small.1 信息:", s7_flavor)

s7_flavor_id = s7_flavor[0].get("flavorID")

结果:

s7.small.1 信息: [{'flavorID': '34e1b6f6-e974-1575-20b2-172ba0e0bf83', 'flavorName': 's7.small.1', 'flavorCPU': 1, 'flavorRAM': 1, 'baseBandwidth': 0.1, 'bandwidth': 0.8, 'flavorSeries': 's', 'nicMultiQueue': 1, 'pps': 10, 'flavorType': 'CPU_S7', 'cpuInfo': 'x86', 'gpuVendor': None, 'videoMemSize': None, 'gpuType': None, 'gpuCount': None}]

s7.small.1规格的flavorID为: 34e1b6f6-e974-1575-20b2-172ba0e0bf83

 

(4)imageID

我们切换产品列表,切换产品时,勿忘查询终端节点,镜像服务的终端节点为:ctimage-global.ctapi.ctyun.cn

选择“镜像服务”,分别点击“API”、“信息查询功能”、“查询可以使用的镜像资源”

得到重要的三个信息:

① URI: /v4/image/list

② GET请求

③ 参数regionID、azName、visibility(由于之前选用了公共镜像类型,所以这里查询要限制查询镜像类型,公共镜像为1)

 

调用OpenAPI查询:

image_list_params = dict(regionID=region_id_huadong, azName=az_name1, visibility=1)

res_image_list = get("https://ctimage-global.ctapi.ctyun.cn/v4/image/list", query='', params=image_list_params)

结果:

[2023-05-23 14:53:46.538614]: 返回状态码: 200

[2023-05-23 14:53:46.538614]: 返回: {'returnObj': {'images': [

... # 省略

{'architecture': 'x86_64', 'azName': None, 'bootMode': 'bios', 'containerFormat': 'bare', 'createdTime': 1664276810, 'description': '', 'destinationUser': None, 'diskFormat': 'raw', 'diskID': None, 'diskSize': 40, 'imageClass': 'ECS', 'imageID': 'e8929d7e-6cfd-4a03-bf76-1bc7f2aeb883', 'imageName': 'CentOS8.1 64位', 'imageType': None, 'maximumRAM': None, 'minimumRAM': None, 'osDistro': 'centos', 'osType': 'linux', 'osVersion': '8.1', 'projectID': None, 'sharedListLength': None, 'size': 42949672960, 'sourceServerID': None, 'sourceUser': None, 'status': 'active', 'tags': None, 'updatedTime': None, 'visibility': 'public'}], 'pageNo': 1, 'pageSize': 10, 'totalCount': 38}, 'errorCode': '', 'message': '', 'description': '', 'statusCode': 800}

我们选用镜像为CentOS7.8 64位的镜像

imageList = res_image_list.json().get("returnObj").get("images")

centos7_image = list(filter(lambda x: x.get("imageName") == "CentOS7.8 64位", imageList))

print("CentOS7.8 64位的镜像信息:", centos7_image)

centos7_8_image_id = centos7_image[0].get("imageID")

print("CentOS7.8 64位镜像的imageID为:", centos7_8_image_id)

结果:

CentOS7.8 64位的镜像信息: [{'architecture': 'x86_64', 'azName': None, 'bootMode': 'bios', 'containerFormat': 'bare', 'createdTime': 1664232729, 'description': '', 'destinationUser': None, 'diskFormat': 'raw', 'diskID': None, 'diskSize': 40, 'imageClass': 'ECS', 'imageID': '6e95ed90-b8b6-40ee-ae38-d851b6dc35e4', 'imageName': 'CentOS7.8 64位', 'imageType': None, 'maximumRAM': None, 'minimumRAM': None, 'osDistro': 'centos', 'osType': 'linux', 'osVersion': '7.8', 'projectID': None, 'sharedListLength': None, 'size': 42949672960, 'sourceServerID': None, 'sourceUser': None, 'status': 'active', 'tags': None, 'updatedTime': None, 'visibility': 'public'}]

CentOS7.8 64位镜像的imageID为: 6e95ed90-b8b6-40ee-ae38-d851b6dc35e4

 

(5)vpcID、subnetID

如果我们没有VPC和子网时,我们需要先创建VPC和子网:

我们切换产品列表至虚拟私有云,切换产品时,勿忘查询终端节点,虚拟私有云的终端节点为:ctvpc-global.ctapi.ctyun.cn

选择“镜像服务”,分别点击“API”、“VPC”、“创建VPC”

得到重要的三个信息:

① URI: /v4/vpc/create

② POST请求

③ 参数regionID、azName、clientToken、name、CIDR

 

调用OpenAPI创建(这里clientToken与name我们自定义,CIDR按照文档选用192.168.0.0/16)

vpc_create_params = dict(regionID=region_id_huadong, azName=az_name1, clientToken="createVPC0523", name="vpc-0523", CIDR="192.168.0.0/16")

res_vpc_create = post("https://ctvpc-global.ctapi.ctyun.cn/v4/vpc/create", query="", params=vpc_create_params)

结果:

[2023-05-23 15:31:40.438960]: 返回状态码: 200

[2023-05-23 15:31:40.438960]: 返回: {'statusCode': 800, 'errorCode': 'SUCCESS', 'message': 'success', 'description': '成功', 'returnObj': {'vpcID': 'vpc-hnkpq1dn46'}}

'destinationUser': None, 'diskFormat': 'raw', 'diskID': None, 'diskSize': 40, 'imageClass': 'ECS', 'imageID': 'e8929d7e-6cfd-4a03-bf76-1bc7f2aeb883', 'imageName': 'CentOS8.1 64位', 'imageType': None, 'maximumRAM': None, 'minimumRAM': None, 'osDistro': 'centos', 'osType': 'linux', 'osVersion': '8.1', 'projectID': None, 'sharedListLength': None, 'size': 42949672960, 'sourceServerID': None, 'sourceUser': None, 'status': 'active', 'tags': None, 'updatedTime': None, 'visibility': 'public'}], 'pageNo': 1, 'pageSize': 10, 'totalCount': 38}, 'errorCode': '', 'message': '', 'description': '', 'statusCode': 800}

 

subnet同理,打开创建子网

得到重要的三个信息:

① URI: /v4/vpc/create-subnet

② POST请求

③ 参数regionID、vpcID、clientToken、name、CIDR

 

调用OpenAPI创建(这里clientToken与name我们自定义,CIDR按照文档选用192.168.1.0/24)

subnet_create_params = dict(regionID=region_id_huadong, clientToken="createSubnet0523", name="subnet-0523", CIDR="192.168.1.0/24", vpcID='vpc-hnkpq1dn46')

res_subnet_create = post("https://ctvpc-global.ctapi.ctyun.cn/v4/vpc/create-subnet", query="", params=subnet_create_params)

结果:

[2023-05-23 15:36:52.879846]: 返回状态码: 200

[2023-05-23 15:36:52.879846]: 返回: {'statusCode': 800, 'errorCode': 'SUCCESS', 'message': 'success', 'description': '成功', 'returnObj': {'subnetID': 'subnet-v47niplom1'}}

 

如果已有对应VPC,可以查询VPC列表来拿到对应vpcID和subnetID

得到重要的三个信息:

① URI: /v4/vpc/list

② GET请求

③ 参数regionID、azName

 

调用OpenAPI查询:

vpc_list_params = dict(regionID=region_id_huadong, azName=az_name1)

res_vpc_list = get("https://ctvpc-global.ctapi.ctyun.cn/v4/vpc/list", query="", params=vpc_list_params)

结果:

[2023-05-23 15:10:45.332877]: 返回状态码: 200

[2023-05-23 15:10:45.332877]: 返回: {'statusCode': 800, 'errorCode': 'SUCCESS', 'message': 'success', 'description': '成功', 'returnObj': {'vpcs': [{'vpcID': 'vpc-xdgj37uuxc', 'name': 'cndc-hub-test-5-sh', 'description': '', 'CIDR': '10.0.0.0/24', 'ipv6Enabled': False, 'secondaryCIDRs': [], 'subnetIDs': ['subnet-80oyaklfdu'], 'natGatewayIDs': [], 'ipv6CIDRS': []},

... # 省略

]}, 'currentCount': 10, 'totalCount': 62, 'totalPage': 7}

我们选用第一个VPC和其中一个subnet

vpcList = res_vpc_list.json().get("returnObj").get("vpcs")

vpc_id = vpcList[0].get("vpcID")

print("vpcID:", vpc_id)

subnet_id = vpcList[0].get("subnetIDs")[0]

print("subnetID:", subnet_id)

结果:

vpcID: vpc-xdgj37uuxc

subnetID: subnet-80oyaklfdu

网卡networkCardList参数组装为:

network_card_list = [{"subnetID": subnet_id, "isMaster": true}]

 

4、  创建云主机

至此,我们的参数已经全部查询完毕,组装一下:

create_instance_params = {

        # 自行填写参数

        "clientToken": "createInstance0523",

        "instanceName": "api-create0523",

        "displayName": "ecm-0523",

        "imageType": 1,

        "bootDiskType": "SATA",

        "bootDiskSize": 40,

        "onDemand": True,

        "extIP": "0",

        # 查询参数

        "regionID": region_id_huadong,

        "azName": az_name1,

        "flavorID": s7_flavor_id,

        "imageID": centos7_8_image_id,

        "vpcID": vpc_id,

        "networkCardList": [{"subnetID": subnet_id, "isMaster": True}]

}

调用OpenAPI进行创建:

res_create_instance = post("https://ctecs-global.ctapi.ctyun.cn/v4/ecs/create-instance", query='', params=create_instance_params)

结果:

[2023-05-23 15:50:56.545144]: 返回状态码: 200

[2023-05-23 15:50:56.545144]: 返回: {'returnObj': {'masterResourceID': 'ee872731d881406d9f08d8ac073d2b6a', 'masterOrderNO': '20230523155045331859', 'regionID': 'bb9fdb42056f11eda1610242ac110002', 'masterOrderID': '853b6238f93e11edb0b10242ac110007'}, 'details': '', 'message': 'success', 'description': '成功', 'statusCode': 800}

 

5、  查看结果

对于创建的云主机,我们可以通过三种方式来看是否创建成功

(1)  控制台查看

最简单的就是登录天翼云官网,选择“控制中心”,资源池选择“华东”->“华东1”,然后点击“弹性云主机”

我们可以看到,展示名称ecm-0523的云主机处于“运行中”状态,此时说明云主机已创建成功。

 

(2)  OpenAPI查询云主机列表

官网文档找到查询云主机列表

得到重要的三个信息:

① URI: /v4/ecs/list-instances

② POST请求

③ 请求参数:regionID、azName、resourceID/instanceName(作为筛选条件)

 

a. 通过填写创建云主机时拿到的masterResourceID来查询(推荐)

instance_list_params = dict(regionID=region_id_huadong, azName=az_name1, resourceID="ee872731d881406d9f08d8ac073d2b6a")

res_instance_list = post("https://ctecs-global.ctapi.ctyun.cn/v4/ecs/list-instances", query='', params=instance_list_params)

b. 通过限制云主机名称来查询云主机

instance_list_params = dict(regionID=region_id_huadong, azName=az_name1, instanceName="api-create0523")

res_instance_list = post("https://ctecs-global.ctapi.ctyun.cn/v4/ecs/list-instances", query='', params=instance_list_params)

结果:

[2023-05-23 16:01:30.784235]: 返回状态码: 200

[2023-05-23 16:01:30.784235]: 返回: {'returnObj': {'currentCount': 1, 'totalCount': 1, 'totalPage': 1, 'results': [{'azName': 'cn-huadong1-jsnj1A-public-ctcloud', 'displayName': 'ecm-0523', 'instanceName': 'api-create0523', 'osType': 5, 'instanceStatus': 'running',

... # 省略

}]}, 'details': '', 'message': 'SUCCESS', 'description': '成功', 'statusCode': 800}

可以看出该云主机已存在,且云主机状态为running(运行中)

 

(3)  OpenAPI查询订单状态

官网文档中也提供了根据订单号查资源ID的方式,选择“弹性云主机”、“公共接口”,点击“根据订单号查询Uuid”

 

得到重要的三个信息:

① URI: /v4/order/queryUuid

② GET请求

③ 参数masterOrderId

 

调用OpenAPI查询:

query_order_params = dict(masterOrderId="853b6238f93e11edb0b10242ac110007")

res_query_order = get("https://ctecs-global.ctapi.ctyun.cn/v4/order/queryUuid", query='', params=query_order_params)

结果:

[2023-05-23 16:29:29.481958]: 返回状态码: 200

[2023-05-23 16:29:29.481958]: 返回: {'returnObj': {'resourceType': 'VM', 'resourceUUID': ['a905aba2-3052-60f6-ec37-aa45c9b6da25'], 'orderStatus': '3'}, 'errorCode': '', 'message': '', 'description': '', 'statusCode': 800}

可以看出订单状态为3,对照订单状态枚举表,得知为“完成”

 

 

三、  总结

       本文介绍了如何调用天翼云OpenAPI来创建云主机,通过官网文档,我们可以很好地找到对应参数,并且也可以通过OpenAPI进行查询。比较贴心的是,目前创建云主机的文档参数说明中已增加了相应需要查找的参数的OpenAPI文档超链接。可以很好的指导我们调用天翼云OpenAPI。

 

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0