HUAWEI CLOUD分享
import json
import random
from huaweicloudsdkcore.auth.credentials import BasicCredentials
from huaweicloudsdkcore.exceptions import exceptions
from huaweicloudsdkcore.http.http_config import HttpConfig
from huaweicloudsdkecs.v2 import *
from huaweicloudsdkecs.v2.region.ecs_region import EcsRegion
from huaweicloudsdkims.v2 import ImsClient, ListImagesRequest
from huaweicloudsdkims.v2.region.ims_region import ImsRegion
from huaweicloudsdkvpc.v2 import ListSubnetsRequest, VpcClient, ListVpcsRequest, CreateVpcRequest, CreateVpcOption, \
CreateVpcRequestBody, CreateSubnetRequest, CreateSubnetOption, CreateSubnetRequestBody, ListSecurityGroupsRequest, \
CreateSecurityGroupRequest, CreateSecurityGroupOption, CreateSecurityGroupRequestBody, \
DeleteSecurityGroupRuleRequest, CreateSecurityGroupRuleRequest, CreateSecurityGroupRuleOption, \
CreateSecurityGroupRuleRequestBody
from huaweicloudsdkvpc.v2.region.vpc_region import VpcRegion
from base.international import t_
from app import app_settings
from app.applications import logger
def try_catch(func_name):
def decorator(func):
def execute(*args, **kwargs):
try:
response = func(*args, **kwargs)
return response
except exceptions.ClientRequestException as e:
error_msg = f'status_code={e.status_code}, request_id={e.request_id}, error_code={e.error_code}, ' \
f'error_msg={e.error_msg}'
logger.error(f'【{func_name}-异常(HW)】:{error_msg}')
raise Exception(t_('【{0}-异常(HW)】:{1}').format(func_name, error_msg), True)
except Exception as ex:
error_msg = f'ex={str(ex)}'
logger.error(f'【{func_name}-异常(HW)】:{error_msg}')
raise Exception(t_('【{0}-异常(HW)】:{1}').format(func_name, error_msg), True)
return execute
return decorator
class _HWCloudBaseHandler(object):
def __init__(self, project_name, ak, sk, timeout=None):
self.project_name = project_name
self.ak = ak
self.sk = sk
self.admin_pass = app_settings.ADMIN_PASS
self.timeout = timeout
self.image_name = app_settings.DEFAULT_IMAGE_NAME
self.vpc_data = app_settings.DEFAULT_VPC_DATA
self.vpc_subnet_data = app_settings.DEFAULT_VPC_SUBNET_DATA
self.security_groups_name = app_settings.DEFAULT_SECURITY_GROUP_NAME
self.security_group_rules = app_settings.SECURTY_GROUP_RULES
self.root_volume_type = app_settings.ROOT_VOLUME_TYPE
self.data_volume_type = app_settings.DATA_VOLUME_TYPE
self.data_volume_size = app_settings.DATA_VOLUME_SIZE
self.eip_type = app_settings.EIP_TYPE
self.eip_size = app_settings.EIP_SIZE
self.eip_share_type = app_settings.EIP_SHARE_TYPE
self.eip_charge_mode = app_settings.EIP_CHARGE_MODE
def ecs_client(self):
"""
初始化 弹性云服务器(ESC)客户端
https://support.huaweicloud.com/sdkreference-mpc/mpc_05_0076.html
添加日志:.with_file_log()
"""
credentials = BasicCredentials(self.ak, self.sk)
region = EcsRegion.value_of(self.project_name)
ecs_client = EcsClient.new_builder() \
.with_credentials(credentials) \
.with_region(region) \
.with_http_config(self.http_config) \
.build()
return ecs_client
def vpc_client(self):
"""
初始化 虚拟私有云 客户端
https://support.huaweicloud.com/sdkreference-mpc/mpc_05_0076.html
"""
credentials = BasicCredentials(self.ak, self.sk)
region = VpcRegion.value_of(self.project_name)
vpc_client = VpcClient.new_builder() \
.with_credentials(credentials) \
.with_region(region) \
.with_http_config(self.http_config) \
.build()
return vpc_client
def image_client(self):
"""
初始化 镜像 客户端
https://support.huaweicloud.com/sdkreference-mpc/mpc_05_0076.html
"""
credentials = BasicCredentials(self.ak, self.sk)
region = ImsRegion.value_of(self.project_name)
image_client = ImsClient.new_builder() \
.with_credentials(credentials) \
.with_region(region) \
.with_http_config(self.http_config) \
.build()
return image_client
@property
def http_config(self):
"""
默认连接超时时间为60秒,读取超时时间为120秒
:param timeout: (connect timeout, read timeout)
"""
http_config = HttpConfig.get_default_config()
if self.timeout:
http_config.timeout = self.timeout
return http_config
@classmethod
def format_server_ids(cls, server_ids):
return [ServerId(id=item) for item in server_ids]
@classmethod
def format_response(cls, response):
json_response = json.loads(str(response))
if json_response.get('error'):
raise Exception(
f'error_code={json_response["error"]["code"]}, error_message={json_response["error"]["message"]}')
return json_response
class _HWListAvailabilityZones(_HWCloudBaseHandler):
@try_catch('查询可用区列表')
def list_available_zones(self):
"""
查询可用区列表
https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/doc?api=NovaListAvailabilityZones
:returns: list of available
"""
request = NovaListAvailabilityZonesRequest()
response = self.ecs_client().nova_list_availability_zones(request)
zone_list = response.availability_zone_info
return [item.zone_name for item in zone_list]
class _HWListFlavors(_HWCloudBaseHandler):
@try_catch('查询规格详情和规格扩展信息列表')
def list_flavors(self, zone_name):
"""
查询可用区可用规格
https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/sdk?api=ListFlavors
:returns: list of flavors
"""
request = ListFlavorsRequest()
request.availability_zone = zone_name
response = self.ecs_client().list_flavors(request)
flavor_list = response.flavors
return [item.id for item in flavor_list]
class _HWListImages(_HWCloudBaseHandler):
@try_catch('查询镜像列表')
def list_images(self, image_name=None):
"""
查询镜像列表
https://console.huaweicloud.com/apiexplorer/#/openapi/IMS/debug?locale=zh-cn&consoleCurrentProductId=ims&consoleCurrentProductshort=&api=ListImages
"""
_image_name = image_name or self.image_name
request = ListImagesRequest()
request.name = _image_name
response = self.image_client().list_images(request)
return json.loads(str(response))['images']
def filter_images(self, image_name=None):
"""
根据 image_name 进行过滤,返回匹配的ID
"""
_image_name = image_name or self.image_name
image_list = self.list_images(_image_name)
for item in image_list:
if _image_name == item.get('name'):
return item['id']
class _HWListVpcs(_HWCloudBaseHandler):
@try_catch('查询VPC列表')
def list_vpcs(self):
"""
查询VPC列表
https://console.huaweicloud.com/apiexplorer/#/openapi/VPC/doc?api=ListVpcs&version=v2
"""
request = ListVpcsRequest()
response = self.vpc_client().list_vpcs(request)
return json.loads(str(response))['vpcs']
def filter_vpcs(self, vpc_name=None):
"""
根据vpc_name进行过滤,返回匹配的ID
"""
_vpc_name = vpc_name or self.vpc_data['name']
vpc_list = self.list_vpcs()
for item in vpc_list:
if _vpc_name == item.get('name'):
return item['id']
class _HWListSubnets(_HWCloudBaseHandler):
@try_catch('查询子网列表')
def list_subnet(self, vpc_id):
"""
查询 vpc_id 子网信息
https://console.huaweicloud.com/apiexplorer/#/openapi/VPC/doc?api=ListSubnets&version=v2
"""
request = ListSubnetsRequest()
request.vpc_id = vpc_id
response = self.vpc_client().list_subnets(request)
return json.loads(str(response))['subnets']
def filter_subnets(self, vpc_id, subnet_name=None):
"""
根据 subnet_name 进行过滤,返回匹配的ID
"""
_subnet_name = subnet_name or self.vpc_subnet_data['name']
subnet_list = self.list_subnet(vpc_id)
for item in subnet_list:
if _subnet_name == item.get('name'):
return item['id']
class _HWListSecurityGroup(_HWCloudBaseHandler):
@try_catch('查询安全组列表')
def list_security_groups(self, vpc_id):
"""
根据 vpc_id 查询对应安全组
https://console.huaweicloud.com/apiexplorer/#/openapi/VPC/sdk?version=v2&api=ListSecurityGroups
"""
request = ListSecurityGroupsRequest()
request.vpc_id = vpc_id
response = self.vpc_client().list_security_groups(request)
return json.loads(str(response))['security_groups']
def filter_security_groups(self, vpc_id, security_groups_name=None):
"""
根据 security_groups_name 进行过滤,返回匹配的ID
"""
_security_groups_name = security_groups_name or self.security_groups_name
security_groups_list = self.list_security_groups(vpc_id)
for item in security_groups_list:
if _security_groups_name == item.get('name'):
return item['id']
class _HWShowJob(_HWCloudBaseHandler):
@try_catch('查询任务的执行状态')
def job_status(self, job_id):
"""
查询任务的执行状态,成功SUCCESS、RUNNING运行中、FAIL失败、INIT正在初始化、PENDING_PAYMENT待支付
https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/doc?api=ShowJob
:param job_id: Job id
return:
{
"job_id": "ff8080828848fa840189239bd5ef6c1f",
"job_type": "createServer",
"begin_time": "2023-07-05T01:12:42.733Z",
"end_time": "2023-07-05T01:13:50.062Z",
"status": "SUCCESS",
"error_code": null,
"fail_reason": null,
"entities": {
"sub_jobs_total": 1,
...
}
}
"""
request = ShowJobRequest()
request.job_id = job_id
response = self.ecs_client().show_job(request)
return json.loads(str(response))
class _HWServiceDetails(_HWCloudBaseHandler):
@try_catch("查询云服务器详情列表")
def server_details(self, server_ids=None, limit=None):
"""
查询云服务器详情列表
https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/doc?api=ListServersDetails
:param server_ids: 服务器server_id列表
:param limit: 显示多少条,默认25,最大500
return:
"""
request = ListServersDetailsRequest()
if server_ids:
request.server_id = server_ids[:500]
if limit:
request.limit = limit
response = self.ecs_client().list_servers_details(request)
return json.loads(str(response))['servers']
class _HWCreateServers(_HWCloudBaseHandler):
@try_catch("创建云服务器(按需)")
def create_server(self, server_name, image_id, flavor_ref, vpcid, subnet_id, security_groups=None,
admin_pass=None, tags=None, count=1):
"""
创建云服务器(按需):异步
https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/doc?api=CreatePostPaidServers
:param server_name: 服务器名,str
:param image_id: 镜像ID,str
:param flavor_ref: 规格名称,如:s6.small.1,str
:param vpcid: vpc id, str
:param subnet_id: 所属vpc的子网id
:param security_groups: 安全组列表,list
:param admin_pass: 服务器密码(一般私有镜像无效),str
:param tags: 服务器标签,如:{"key": key, "value": value},dict
:param count: 创建台数,int
return:
{
"job_id": "ff808082739334d80173941567f21d4f",
"serverIds": [
"118258c5-0c6a-4e73-8b51-8d1ea3272e1b",
"e51b9e20-7088-4914-a4f3-6c76bcfed0be"
]
}
"""
logger.info(f'【创建云服务器(按需)-开始】name={server_name}, region={self.project_name}, image_id={image_id}')
server_body = PostPaidServer(
name=server_name,
image_ref=image_id,
admin_pass=admin_pass or self.admin_pass,
availability_zone=self.get_availability_zone(flavor_ref),
data_volumes=self.get_data_volume(),
flavor_ref=flavor_ref,
nics=self.get_subnets(subnet_id),
publicip=self.get_public_cip(),
root_volume=self.get_volume_type(),
server_tags=self.get_server_tags(tags),
vpcid=vpcid,
count=count,
is_auto_rename=False,
security_groups=self.get_security_groups(security_groups)
)
request = CreatePostPaidServersRequest()
request.body = CreatePostPaidServersRequestBody(
server=server_body
)
response = self.ecs_client().create_post_paid_servers(request)
logger.info(f'【创建云服务器(按需)-请求成功】name={server_name}, image_id={image_id}, res={response}')
return self.format_response(response)
@classmethod
def get_server_tags(cls, server_tags):
if server_tags is None:
return
return [
PostPaidServerTag(
key=server_tags['key'],
value=server_tags['value']
)
]
def get_data_volume(self):
"""
数据盘
"""
return [
PostPaidServerDataVolume(
volumetype=self.data_volume_type,
size=self.data_volume_size
)
]
def get_volume_type(self):
"""
系统盘类型
"""
return PostPaidServerRootVolume(
volumetype=self.root_volume_type
)
def get_public_cip(self):
"""
配置云服务器的弹性IP信息
"""
return PostPaidServerPublicip(
eip=self.get_eip()
)
def get_eip(self):
"""
配置云服务器自动分配弹性IP时, 创建弹性IP的配置参数
"""
return PostPaidServerEip(
iptype=self.eip_type,
bandwidth=self.get_bandwidth_eip()
)
def get_bandwidth_eip(self):
"""
弹性IP地址带宽参数
"""
return PostPaidServerEipBandwidth(
size=self.eip_size,
sharetype=self.eip_share_type,
chargemode=self.eip_charge_mode
)
def get_subnets(self, subnet_id):
return [PostPaidServerNic(subnet_id=subnet_id)]
def get_security_groups(self, security_groups):
"""
构造安全组数据
:param security_groups: 安全组列表
"""
return [PostPaidServerSecurityGroup(id=item) for item in security_groups]
def get_availability_zone(self, flavor_ref):
"""
根据规格筛选可用可用区
(部分规格在部分可用区无法使用)
"""
zone_list = _HWListAvailabilityZones(project_name=self.project_name, ak=self.ak, sk=self.sk).list_available_zones()
if not zone_list:
raise Exception(f'当前区域:{self.project_name} 无可用区可用')
available_flavor_zone = []
for item in zone_list:
res = _HWListFlavors(project_name=self.project_name, ak=self.ak, sk=self.sk).list_flavors(item)
if flavor_ref in res:
available_flavor_zone.append(item)
return random.choice(available_flavor_zone)
class _HWStopServer(_HWCloudBaseHandler):
@try_catch('批量关闭云服务器')
def stop_servers(self, server_ids):
"""
批量关闭云服务器:异步,最大1000
https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/doc?api=BatchStopServers
:param server_ids: 服务器server_id列表
return:
{
"job_id": "70a599e0-31e7-49b7-b260-868f441e862b"
}
"""
logger.info(f'【批量关闭云服务器-开始】server_ids=[{server_ids}]')
request = BatchStopServersRequest()
request.body = BatchStopServersOption(
servers=self.format_server_ids(server_ids)
)
response = self.ecs_client().batch_stop_servers(request)
logger.info(f'【批量关闭云服务器-请求成功】server_ids=[{server_ids}], response={response}')
return self.format_response(response)
class _HWRestartServer(_HWCloudBaseHandler):
@try_catch('批量重启云服务器')
def restart_servers(self, server_ids, type='SOFT'):
"""
重启服务器:异步
https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/doc?api=BatchRebootServers
:param server_ids: 服务器server_id列表
:param type: 重启类型,SOFT/HARD
return:
{
"job_id": "70a599e0-31e7-49b7-b260-868f441e862b"
}
"""
logger.info(f'【批量重启云服务器-开始】server_ids=[{server_ids}]')
request = BatchRebootServersRequest()
request.body = BatchRebootServersRequestBody(
reboot=BatchRebootSeversOption(
servers=self.format_server_ids(server_ids),
type=type
)
)
response = self.ecs_client().batch_reboot_servers(request)
logger.info(f'【批量重启云服务器-请求成功】server_ids=[{server_ids}], response={response}')
return self.format_response(response)
class _HWReinstallServer(_HWCloudBaseHandler):
@try_catch('重装弹性云服务器操作系统(未安装Cloud init)')
def reinstall_server(self, server_id):
"""
重装弹性云服务器操作系统(开机状态下重建会先关闭服务器,再进行重建)
https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/sdk?api=ReinstallServerWithoutCloudInit
:param server_id: 服务器server_id
return:
{
"job_id": "70a599e0-31e7-49b7-b260-868f441e862b"
}
"""
logger.info(f'【重装弹性云服务器操作系统(未安装Cloud init)-开始】server_id=[{server_id}]')
request = ReinstallServerWithoutCloudInitRequest()
request.server_id = server_id
os_reinstall_body = ReinstallServerWithoutCloudInitOption(
mode="withStopServer"
)
request.body = ReinstallServerWithoutCloudInitRequestBody(
os_reinstall=os_reinstall_body
)
response = self.ecs_client().reinstall_server_without_cloud_init(request)
logger.info(
f'【重装弹性云服务器操作系统(未安装Cloud init)-请求成功】server_ids=[{server_id}], response={response}')
return self.format_response(response)
class _HWDeleteServer(_HWCloudBaseHandler):
@try_catch('删除云服务器')
def delete_servers(self, server_ids):
"""
删除服务器:异步
https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/doc?api=DeleteServers
:param server_ids: 服务器server_id列表
return:
{
"job_id": "70a599e0-31e7-49b7-b260-868f441e862b"
}
"""
logger.info(f'【删除云服务器-开始】server_ids=[{server_ids}]')
request = DeleteServersRequest()
request.body = DeleteServersRequestBody(
servers=self.format_server_ids(server_ids),
delete_volume=True,
delete_publicip=True
)
response = self.ecs_client().delete_servers(request)
logger.info(f'【删除云服务器-请求成功】server_ids=[{server_ids}], response={response}')
return self.format_response(response)
class _HWCreateVpcs(_HWCloudBaseHandler):
@try_catch('创建VPC')
def create_vpcs(self):
"""
创建VPC
https://console.huaweicloud.com/apiexplorer/#/openapi/VPC/doc?api=CreateVpc&version=v2
"""
request = CreateVpcRequest()
vpcbody = CreateVpcOption(
cidr=self.vpc_data['cidr'],
name=self.vpc_data['name'],
description=self.vpc_data['description']
)
request.body = CreateVpcRequestBody(
vpc=vpcbody
)
response = self.vpc_client().create_vpc(request)
return json.loads(str(response))['vpc']['id']
class _HWCreateVpcSubnet(_HWCloudBaseHandler):
@try_catch('创建子网')
def create_vpc_subnet(self, vpc_id):
"""
创建子网
https://console.huaweicloud.com/apiexplorer/#/openapi/VPC/doc?api=CreateSubnet&version=v2
"""
request = CreateSubnetRequest()
subnetbody = CreateSubnetOption(
name=self.vpc_subnet_data['name'],
description=self.vpc_subnet_data['description'],
cidr=self.vpc_subnet_data['cidr'],
vpc_id=vpc_id,
gateway_ip=self.vpc_subnet_data['gateway_ip'],
dhcp_enable=self.vpc_subnet_data['dhcp_enable']
)
request.body = CreateSubnetRequestBody(
subnet=subnetbody
)
response = self.vpc_client().create_subnet(request)
return json.loads(str(response))['subnet']['id']
class _HWCreateSecurityGroup(_HWCloudBaseHandler):
@try_catch('查询安全组列表')
def create_security_groups(self, vpc_id):
"""
创建默认安全组
https://console.huaweicloud.com/apiexplorer/#/openapi/VPC/debug?version=v2&api=CreateSecurityGroup
"""
request = CreateSecurityGroupRequest()
securityGroupbody = CreateSecurityGroupOption(
name=self.security_groups_name,
vpc_id=vpc_id
)
request.body = CreateSecurityGroupRequestBody(
security_group=securityGroupbody
)
response = self.vpc_client().create_security_group(request)
return json.loads(str(response))['security_group']
def create_security_groups_rules(self, group_id):
for group_rule in self.security_group_rules:
request = CreateSecurityGroupRuleRequest()
securityGroupRulebody = CreateSecurityGroupRuleOption(
security_group_id=group_id,
direction=group_rule["direction"],
ethertype=group_rule["ethertype"]
)
request.body = CreateSecurityGroupRuleRequestBody(
security_group_rule=securityGroupRulebody
)
self.vpc_client().create_security_group_rule(request)
def delete_security_groups_rules(self, rule_ids):
"""
删除安全组规则
https://console.huaweicloud.com/apiexplorer/#/openapi/VPC/debug?version=v2&api=DeleteSecurityGroupRule
"""
for rule_id in rule_ids:
request = DeleteSecurityGroupRuleRequest()
request.security_group_rule_id = rule_id
self.vpc_client().delete_security_group_rule(request)
class HWQueryManager(_HWListAvailabilityZones,
_HWListImages,
_HWListSubnets,
_HWListVpcs,
_HWListFlavors,
_HWListSecurityGroup,
_HWShowJob,
_HWServiceDetails):
"""
查询类
"""
pass
class HWOperateManger(_HWCreateServers,
_HWStopServer,
_HWRestartServer,
_HWReinstallServer,
_HWDeleteServer,
_HWCreateVpcSubnet,
_HWCreateVpcs,
_HWCreateSecurityGroup):
"""
操作类
"""
pass
class HWServerManager(HWQueryManager,
HWOperateManger):
pass
'''
使用示例:
# 查询VPC下的子网
res = HWServerManager().get_subnets()
print(res)
# [{"subnet_id": "93089f63-ba1e-2345-8b7a-8c58b5c123678"}]
'''
settings文件:根据实际情况填写
```python
HUAWEI_CLOUD_USERNAME = 'username'
HUAWEI_CLOUD_AK = "ak"
HUAWEI_CLOUD_SK = "sk"
ROOT_VOLUME_TYPE = "SSD"
DATA_VOLUME_TYPE = "SSD"
DATA_VOLUME_SIZE = 40
EIP_SHARE_TYPE = "PER"
EIP_CHARGE_MODE = 'traffic'
EIP_TYPE = '5_sbgp'
EIP_SIZE = 1
DEFAULT_IMAGE_NAME = "image_name"
DEFAULT_SECURITY_GROUP_NAME = "vpn_security_group"
DEFAULT_VPC_DATA = {
"cidr": "10.10.0.0/16",
"name": "vpn_vpc",
"description": "用于vpn网络"
}
DEFAULT_VPC_SUBNET_DATA = {
"name": "vpn_subnet",
"description": "用于vpn_vpc的子网",
"cidr": "10.10.10.0/24",
"gateway_ip": "10.10.10.254",
"dhcp_enable": True
}
SECURTY_GROUP_RULES = [
{"direction": "egress", "ethertype": "IPv4"},
{"direction": "egress", "ethertype": "IPv6"},
{"direction": "ingress", "ethertype": "IPv4"},
{"direction": "ingress", "ethertype": "IPv6"},
]
ADMIN_PASS = 'admin_pass'
DEFAULT_SECURITY_GROUPS = ['feebe7f3-fdad-4b14-81c8-90febc5ea6c2']
QUERY_SERVER_STATUS_INTERVAL = 120