utils.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. import logging
  2. import json
  3. import sys
  4. import time
  5. import requests
  6. import asyncio
  7. from aliyunsdkcore.client import AcsClient
  8. from aliyunsdkecs.request.v20140526.RunInstancesRequest import RunInstancesRequest
  9. from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import DescribeInstancesRequest
  10. from aliyunsdkecs.request.v20140526.DescribeNetworkInterfacesRequest import DescribeNetworkInterfacesRequest
  11. from aliyunsdkslb.request.v20140515.DescribeLoadBalancerAttributeRequest import DescribeLoadBalancerAttributeRequest
  12. from aliyunsdkecs.request.v20140526.RunCommandRequest import RunCommandRequest
  13. from aliyunsdkecs.request.v20140526.SendFileRequest import SendFileRequest
  14. from aliyunsdkecs.request.v20140526.StopInstancesRequest import StopInstancesRequest
  15. from aliyunsdkecs.request.v20140526.DeleteInstancesRequest import DeleteInstancesRequest
  16. from aliyunsdkecs.request.v20140526.DescribeInstanceStatusRequest import DescribeInstanceStatusRequest
  17. from aliyunsdkcore.request import CommonRequest
  18. logging.basicConfig(level=logging.INFO,
  19. format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
  20. datefmt='%a, %d %b %Y %H:%M:%S')
  21. def send_msg_to_feishu(webhook, key_word, msg_text):
  22. """发送消息到飞书"""
  23. headers = {'Content-Type': 'application/json'}
  24. payload_message = {
  25. "msg_type": "text",
  26. "content": {
  27. "text": '{}: {}'.format(key_word, msg_text)
  28. }
  29. }
  30. response = requests.request('POST', url=webhook, headers=headers, data=json.dumps(payload_message))
  31. logging.info(response.text)
  32. def connect_client(access_key_id, access_key_secret, region_id):
  33. """
  34. 初始化账号,连接客户端
  35. :param access_key_id: access key Id, type-string
  36. :param access_key_secret: access key secret, type-string
  37. :param region_id: region_id
  38. :return: clt
  39. """
  40. try:
  41. clt = AcsClient(ak=access_key_id, secret=access_key_secret, region_id=region_id)
  42. return clt
  43. except Exception as e:
  44. # 失败,记录报错信息,发送通知,停止并退出
  45. logging.error(e)
  46. sys.exit()
  47. def build_create_instances_request(image_id, vswitch_id, security_group_id, zone_id, instance_type, instance_name,
  48. disk_size, disk_category, key_pair_name, tags):
  49. """
  50. 购买服务器参数配置
  51. :param image_id: 使用的镜像信息 type-string
  52. :param vswitch_id: 选择的交换机 type-string
  53. :param security_group_id: 当前vpc类型的安全组 type-string
  54. :param zone_id: 服务器所在区域 type-string
  55. :param instance_type: 实例规格 type-string
  56. :param instance_name: 实例命名 type-string
  57. :param disk_size: 磁盘大小,单位:G,type-string
  58. :param disk_category: 磁盘类型 type-string
  59. :param key_pair_name: 密钥对名称 type-string
  60. :param tags: 标签 type-list, eg: [{"Key": "ecs", "Value": "rov-server.prod"}, ...]
  61. :return: request
  62. """
  63. request = RunInstancesRequest()
  64. request.set_ImageId(image_id)
  65. request.set_VSwitchId(vswitch_id)
  66. request.set_SecurityGroupId(security_group_id)
  67. request.set_ZoneId(zone_id)
  68. request.set_InstanceType(instance_type)
  69. request.set_InstanceName(instance_name)
  70. request.set_SystemDiskSize(disk_size)
  71. request.set_SystemDiskCategory(disk_category)
  72. request.set_KeyPairName(key_pair_name)
  73. request.set_Tags(tags)
  74. return request
  75. def send_request(client, request):
  76. """
  77. 发送API请求
  78. :param client: 客户端连接
  79. :param request: 请求配置
  80. :return: response
  81. """
  82. request.set_accept_format('json')
  83. try:
  84. response = client.do_action_with_exception(request)
  85. response = json.loads(response)
  86. # logging.info(response)
  87. return response
  88. except Exception as e:
  89. # 失败,记录报错信息,发送通知,停止并退出
  90. logging.error(e)
  91. sys.exit()
  92. def check_instance_running(client, instance_ids):
  93. """
  94. 检查服务器运行状态
  95. :param client: 客户端连接
  96. :param instance_ids: 实例id列表, type-list
  97. :return: running_count,Status为Running的实例数
  98. """
  99. try:
  100. request = DescribeInstancesRequest()
  101. request.set_InstanceIds(json.dumps(instance_ids))
  102. request.set_PageSize(100)
  103. response = send_request(client=client, request=request)
  104. if response.get('Code') is None:
  105. instances_list = response.get('Instances').get('Instance')
  106. running_count = 0
  107. running_instances = []
  108. for instance_detail in instances_list:
  109. if instance_detail.get('Status') == "Running":
  110. running_count += 1
  111. running_instances.append(instance_detail.get('InstanceId'))
  112. return running_count, running_instances
  113. else:
  114. # 失败,记录报错信息,发送通知,停止并退出
  115. logging.error(response)
  116. sys.exit()
  117. except Exception as e:
  118. # 失败,记录报错信息,发送通知,停止并退出
  119. logging.error(e)
  120. sys.exit()
  121. def create_multiple_instances(amount, client,
  122. image_id, vswitch_id, security_group_id, zone_id, instance_type, instance_name,
  123. disk_size, disk_category, key_pair_name, tags):
  124. """
  125. 创建多个ECS实例
  126. :param amount: 创建实例数 type-int 取值范围:[1, 100]
  127. :param client: 购买机器客户端连接
  128. :param image_id: 使用的镜像信息 type-string
  129. :param vswitch_id: 选择的交换机 type-string
  130. :param security_group_id: 当前vpc类型的安全组 type-string
  131. :param zone_id: 服务器所在区域 type-string
  132. :param instance_type: 实例规格 type-string
  133. :param instance_name: 实例命名 type-string
  134. :param disk_size: 磁盘大小,单位:G,type-string
  135. :param disk_category: 磁盘类型 type-string
  136. :param key_pair_name: 密钥对名称 type-string
  137. :param tags: 标签 type-list, eg: [{"Key": "ecs", "Value": "rov-server.prod"}, ...]
  138. :return:
  139. """
  140. logging.info(f"create instances start, request amount: {amount}.")
  141. # 1. 连接客户端
  142. # create_instances_clt = connect_client(
  143. # access_key_id=access_key_id, access_key_secret=access_key_secret, region_id=region_id
  144. # )
  145. # 2. 请求参数配置
  146. request = build_create_instances_request(
  147. image_id=image_id, vswitch_id=vswitch_id, security_group_id=security_group_id, zone_id=zone_id,
  148. instance_type=instance_type, instance_name=instance_name, disk_size=disk_size, disk_category=disk_category,
  149. key_pair_name=key_pair_name, tags=tags
  150. )
  151. request.set_Amount(amount)
  152. # 3. 发送API请求,购买机器并启动
  153. response = send_request(client=client, request=request)
  154. if response.get('Code') is None:
  155. instance_ids = response.get('InstanceIdSets').get('InstanceIdSet')
  156. logging.info(f"success amount: {len(instance_ids)}, instance ids: {instance_ids}.")
  157. # 获取机器运行状态
  158. running_amount = 0
  159. while running_amount < amount:
  160. time.sleep(10)
  161. running_amount, running_instances = check_instance_running(client=client, instance_ids=instance_ids)
  162. logging.info(f"running amount: {running_amount}, running instances: {running_instances}.")
  163. return instance_ids
  164. else:
  165. # 失败,记录报错信息,发送通知,停止并退出
  166. logging.error(response)
  167. sys.exit()
  168. def run_command(client, instance_ids, command):
  169. """
  170. 批量执行命令
  171. :param client: 客户端连接
  172. :param instance_ids: 实例id列表, type-list
  173. :param command: 命令 type-string
  174. :return:
  175. """
  176. request = RunCommandRequest()
  177. request.set_accept_format('json')
  178. request.set_Type("RunShellScript")
  179. request.set_CommandContent(command)
  180. request.set_InstanceIds(instance_ids)
  181. response = send_request(client=client, request=request)
  182. logging.info(response)
  183. def get_instance_ids(client, slb_id):
  184. """
  185. 获取slb下所有服务器instanceId
  186. :param client: 客户端连接
  187. :param slb_id: 负载均衡id type-string
  188. :return: instance_ids type-list
  189. """
  190. request = DescribeLoadBalancerAttributeRequest()
  191. request.set_accept_format('json')
  192. request.set_LoadBalancerId(slb_id)
  193. response = send_request(client=client, request=request)
  194. instance_ids = [instance["ServerId"] for instance in response["BackendServers"]["BackendServer"]]
  195. return instance_ids
  196. def get_ip_address(client, instance_id):
  197. """
  198. 获取实例IP地址
  199. :param client: 客户端连接
  200. :param instance_id: 实例id, type-string
  201. :return: ip_address, type-string
  202. """
  203. request = DescribeNetworkInterfacesRequest()
  204. request.set_accept_format('json')
  205. request.set_InstanceId(instance_id)
  206. response = send_request(client=client, request=request)
  207. ip_address = response['NetworkInterfaceSets']['NetworkInterfaceSet'][0]['PrivateIpAddress']
  208. return ip_address
  209. def set_weight_for_instances(client, slb_id, instance_id_list, weight):
  210. """
  211. 同时设置多台服务器的slb权重,权重一样
  212. :param client: 客户端连接
  213. :param slb_id: slb_id
  214. :param instance_id_list: 服务器id list
  215. :param weight: 权重值
  216. :return: None
  217. """
  218. BackendServers = [{"ServerId": instance_id, "Weight": weight} for instance_id in instance_id_list]
  219. request = CommonRequest()
  220. request.set_accept_format('json')
  221. request.set_domain('slb.aliyuncs.com')
  222. request.set_version('2014-05-15')
  223. request.set_method('POST')
  224. request.set_action_name('SetBackendServers')
  225. request.add_query_param('BackendServers', BackendServers)
  226. request.add_query_param('LoadBalancerId', slb_id)
  227. response = send_request(client=client, request=request)
  228. return response
  229. def send_file_to_ecs(client, instance_id_list, target_dir, name, content):
  230. """
  231. 发送文件到ecs
  232. :param client:
  233. :param instance_id_list:
  234. :param target_dir: 文件存放目录 type-string
  235. :param name: 文件名 type-string
  236. :param content: 文件内容 type-string
  237. :return:
  238. """
  239. request = SendFileRequest()
  240. request.set_Content(content)
  241. request.set_TargetDir(target_dir)
  242. request.set_Name(name)
  243. request.set_Overwrite(True)
  244. request.set_InstanceIds(instance_id_list)
  245. response = send_request(client=client, request=request)
  246. return response
  247. def stop_instances(client, instance_ids, force_stop=False):
  248. """
  249. 停止实例
  250. :param client:
  251. :param instance_ids: 实例ID, type-list
  252. :param force_stop: 是否强制关机, True-强制关机, False-正常关机, type-bool
  253. :return:
  254. """
  255. request = StopInstancesRequest()
  256. request.set_InstanceIds(instance_ids)
  257. request.set_ForceStop(force_stop)
  258. response = send_request(client=client, request=request)
  259. return response
  260. def release_instances(client, instance_ids, force=False):
  261. """
  262. 释放实例
  263. :param client:
  264. :param instance_ids: instance_id, type-list
  265. :param force: 是否强制释放, True-强制释放, False-正常释放, type-bool
  266. :return:
  267. """
  268. request = DeleteInstancesRequest()
  269. request.set_InstanceIds(instance_ids)
  270. request.set_Force(force)
  271. response = send_request(client=client, request=request)
  272. return response
  273. def get_instances_status(client, instance_ids):
  274. """
  275. 获取实例运行状态
  276. :param client:
  277. :param instance_ids: instance_id, type-liist
  278. :return:
  279. """
  280. request = DescribeInstanceStatusRequest()
  281. request.set_InstanceIds(instance_ids)
  282. request.set_PageSize(50)
  283. response = send_request(client=client, request=request)
  284. return response
  285. def set_instance_weight_process(client, slb_id, instance_id_list, weight_list):
  286. """
  287. 修改服务器的权重值
  288. :param client: slb客户端连接
  289. :param slb_id: slb id
  290. :param instance_id_list: instance id list
  291. :param weight_list: 权重修改列表 type-list [(weight, sleep_time), ...]
  292. :return:
  293. """
  294. for weight, sleep_time in weight_list:
  295. flag = True
  296. while flag:
  297. try:
  298. set_weight_for_instances(client=client, slb_id=slb_id, instance_id_list=instance_id_list, weight=weight)
  299. time.sleep(sleep_time)
  300. flag = False
  301. except Exception as e:
  302. time.sleep(10)
  303. continue