之前博主在使用ansible +阿里云ecs 标签实现主机信息过滤和生成,但是每台ecs绑定的标签数量有限(20个),遂想要自己扩展下,干脆建个简陋的cmdb(只有db:mongodb)o( ̄┰ ̄*)ゞ

大致逻辑

获取 ECS 信息

创建RAM用户和AK/SK

安装SDK依赖

需要用到的api: DescribeInstances,安装依赖:

1
pip install alibabacloud_ecs20140526==2.1.1

简单样例,获取单挑数据

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
# -*- coding: utf-8 -*-
# This file is auto-generated, don't edit it. Thanks.
import sys

from typing import List

from alibabacloud_ecs20140526.client import Client as Ecs20140526Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_ecs20140526 import models as ecs_20140526_models


class Sample:
def __init__(self):
pass

@staticmethod
def create_client(
access_key_id: str,
access_key_secret: str,
) -> Ecs20140526Client:
"""
使用AK&SK初始化账号Client
@param access_key_id:
@param access_key_secret:
@return: Client
@throws Exception
"""
config = open_api_models.Config(
# 您的AccessKey ID,
access_key_id="access_key_id",
# 您的AccessKey Secret,
access_key_secret="access_key_secret"
)
# 访问的域名
config.endpoint = f'ecs-cn-hangzhou.aliyuncs.com'
return Ecs20140526Client(config)

@staticmethod
def main(
args: List[str],
) -> None:
client = Sample.create_client('accessKeyId', 'accessKeySecret')
describe_instances_request = ecs_20140526_models.DescribeInstancesRequest(
region_id='cn-shenzhen',
page_number=1,
page_size=1
)
# 复制代码运行请自行打印 API 的返回值
res = client.describe_instances(describe_instances_request).body.to_map()
print(json.dumps(res))

if __name__ == '__main__':
Sample.main(sys.argv[1:])

测试结果

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
{
"Instances":{
"Instance":[
{
"AutoReleaseTime":"",
"ClusterId":"",
"Cpu":8,
"CpuOptions":{
"CoreCount":4,
"Numa":"",
"ThreadsPerCore":2
},
"CreationTime":"2020-05-03T05:11Z",
"CreditSpecification":"",
"DedicatedHostAttribute":{
"DedicatedHostClusterId":"",
"DedicatedHostId":"",
"DedicatedHostName":""
},
"DedicatedInstanceAttribute":{
"Affinity":"",
"Tenancy":""
},
"DeletionProtection":false,
"DeploymentSetId":"",
"Description":"",
"DeviceAvailable":true,
"EcsCapacityReservationAttr":{
"CapacityReservationId":"",
"CapacityReservationPreference":""
},
"EipAddress":{
"AllocationId":"",
"InternetChargeType":"",
"IpAddress":""
},
"ExpiredTime":"2020-06-03T16:00Z",
"GPUAmount":0,
"GPUSpec":"",
"HibernationOptions":{
"Configured":false
},
"HostName":"iZz56sxxxxxzkZ",
"ImageId":"win2016_1607_x64_dtc_zh-cn_40G_alibase_20200311.vhd",
"ImageOptions":{

},
"InnerIpAddress":{
"IpAddress":[

]
},
"InstanceChargeType":"PrePaid",
"InstanceId":"i-iZz56sxxxxxzkZ",
"InstanceName":"launch-advisor-20200503",
"InstanceNetworkType":"vpc",
"InstanceType":"ecs.c7.2xlarge",
"InstanceTypeFamily":"ecs.c7",
"InternetChargeType":"PayByBandwidth",
"InternetMaxBandwidthIn":5000,
"InternetMaxBandwidthOut":20,
"IoOptimized":true,
"Memory":16384,
"MetadataOptions":{
"HttpEndpoint":"",
"HttpTokens":""
},
"NetworkInterfaces":{
"NetworkInterface":[
{
"MacAddress":"00:16:3e:12:a4:1b",
"NetworkInterfaceId":"eni-wz9xxx19imewia",
"PrimaryIpAddress":"10.200.1.144",
"PrivateIpSets":{
"PrivateIpSet":[
{
"Primary":true,
"PrivateIpAddress":"10.200.1.144"
}
]
},
"Type":"Primary"
}
]
},
"OSName":"Windows Server 2016 \u6570\u636e\u4e2d\u5fc3\u7248 64\u4f4d\u4e2d\u6587\u7248",
"OSNameEn":"Windows Server 2016 Data Center Edition 64bit Chinese Edition",
"OSType":"windows",
"OperationLocks":{
"LockReason":[

]
},
"PublicIpAddress":{
"IpAddress":[
"xxxxxxxxxxxxx"
]
},
"Recyclable":false,
"RegionId":"cn-shenzhen",
"ResourceGroupId":"",
"SaleCycle":"",
"SecurityGroupIds":{
"SecurityGroupId":[
"sg-wz9d0xxxt4i9fg36y3"
]
},
"SerialNumber":"00159a9c-74b2-4541-a02xxx29266ca",
"SpotPriceLimit":0,
"SpotStrategy":"NoSpot",
"StartTime":"2020-05-03T05:11Z",
"Status":"Running",
"StoppedMode":"Not-applicable",
"VlanId":"",
"VpcAttributes":{
"NatIpAddress":"",
"PrivateIpAddress":{
"IpAddress":[
"10.200.1.144"
]
},
"VSwitchId":"vsw-wz9cpxxln62ws0kmw0",
"VpcId":"vpc-wz95lwxxtg6pjctear"
},
"ZoneId":"cn-shenzhen-e"
}
]
},
"NextToken":"4f1d7cc9f51e189029a6ac20e03a54ba666795b580cac90be0b5e8707e68181f",
"PageNumber":1,
"PageSize":1,
"RequestId":"4E4E86B0-D344-5D25-A2D5-54AFCEB0FDE2",
"TotalCount":357
}

获取单个地域所有样例

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
# -*- coding: utf-8 -*-
# This file is auto-generated, don't edit it. Thanks.
import sys
import time
from typing import List
import json

from alibabacloud_ecs20140526.client import Client as Ecs20140526Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_ecs20140526 import models as ecs_20140526_models

class EcsInfo:
def __init__(self):
pass

@staticmethod
def create_client(
access_key_id: str,
access_key_secret: str,
) -> Ecs20140526Client:
"""
使用AK&SK初始化账号Client
@param access_key_id:
@param access_key_secret:
@return: Client
@throws Exception
"""
config = open_api_models.Config(
# 您的AccessKey ID,
access_key_id="access_key_id",
# 您的AccessKey Secret,
access_key_secret="access_key_secret"
)
# 访问的域名
config.endpoint = f'ecs.cn-shenzhen.aliyuncs.com'
return Ecs20140526Client(config)

@staticmethod
def main(
args: List[str],
) -> list:
client = EcsInfo.create_client('accessKeyId', 'accessKeySecret')
# tag_0 = ecs_20140526_models.DescribeInstancesRequestTag(
# key='os',
# value='windows'
# )
# tag_1 = ecs_20140526_models.DescribeInstancesRequestTag(
# key='owner',
# value='zhangke'
# )
next_token = ''
describe_instances_request = ecs_20140526_models.DescribeInstancesRequest(
region_id='cn-shenzhen',
max_results=10,
next_token=next_token

# tag=[
# tag_0,
# tag_1
# ]
)
res = client.describe_instances(describe_instances_request).body.to_map()
next_token = res['NextToken']
# print(next_token)
instances_list = res['Instances']['Instance']

while next_token:
time.sleep(0.5)
# next_token = res['NextToken']
describe_instances_request = ecs_20140526_models.DescribeInstancesRequest(
region_id='cn-shenzhen',
max_results=10,
next_token=next_token
)
res = client.describe_instances(describe_instances_request).body.to_map()
next_token = res['NextToken']
instances_list.extend(res['Instances']['Instance'])

return instances_list

if __name__ == '__main__':
#print(sys.argv)
#print(sys.argv[1:])
instances = EcsInfo.main(sys.argv[1:])
print(len(instances))
for instance in instances:
print(instance)

测试结果

将数据写入mongodb

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
# 创建一个Cmdb类
class Cmdb:
def __init__(self):
pass
@staticmethod
def create_db_client(
db_user: str,
db_pass: str,
db_host: str,
db_port: str,
db_name: str
) -> object:
DbClient = pymongo.MongoClient("mongodb://"+ db_user + ':' + db_pass + '@' + db_host + ':' +db_port + "/")
return DbClient

@staticmethod
def update_ecs_info(instances: list) -> None:
db_client = Cmdb.create_db_client(
db_user="cmdbadminxxxx",
db_pass="123456xxxx",
db_host="192.168.174.132",
db_port="27017",
db_name="cmdb"
)

mydb = db_client["cmdb"]
mycol = mydb["ecsinfo2"]
for doc in instances:
# 根据实例id唯一进行查找判断是更新还是新增
query = {'InstanceId': doc['InstanceId']}
res = mycol.find_one(query)
# 后期新增自定义的字段EcsTag,需要保留
if res and 'EcsTag' in res:
doc['EcsTag'] = res['EcsTag']
print("==[update]==",doc)
mycol.update_one(query,{"$set": doc})
else:
doc['EcsTag'] = []
print("==[insert]==",doc)
x = mycol.insert_one(doc)

测试更新或新增

从Mongodb获取ecs信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 在Cmdb类中创建查询方法
@staticmethod
def get_ecs_info(
query: dict,
) -> None:
db_client = Cmdb.create_db_client(
db_user="cmdbadminxxxx",
db_pass="123456xxxx",
db_host="192.168.174.132",
db_port="27017",
db_name="cmdb"
)

mydb = db_client["cmdb"]
mycol = mydb["ecsinfo2"]
# print(query)
query_lists = list(mycol.find(query))
return query_lists

测试查询结果

新增自定义字段

在原有的数据中已存在Tag字段,该字段是从阿里云ECS openapi获取的ecs 标签,想了想还是新建一个字段EcsTag吧~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 扩展字段如下,可以自行再扩展如:增加用户信息,创建时间等
{
"EcsTag": [
{
'ProjectName': 'demo_project1',
'ProjectService': 'demo_service1',
'BuildVersion': '#1254',
'env': 'test',
'status': 'running'
},
{
'ProjectName': 'demo_project2',
'ProjectService': 'demo_service2',
'BuildVersion': '#1254',
'env': 'test',
'status': 'running'
}
]
}

新增打标方法

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
# 再原有的Cmdb类中新增一个打标方法,一次只更新一台ecs的一个或多个标签
@staticmethod
def update_ecs_info_ecstag(query:dict,data:list)->None:
db_client = Cmdb.create_db_client(
db_user="cmdbadminxxxx",
db_pass="123456xxxx",
db_host="192.168.174.132",
db_port="27017",
db_name="cmdb"
)

mydb = db_client["cmdb"]
mycol = mydb["ecsinfo2"]

query = query
data = data

# 先获取已有标签
res = mycol.find_one(query,{'_id':0,"EcsTag":1})
res_list = res['EcsTag']
print(len(res_list),res_list)

# 处理已有标签的更新或新增数据的合并
if res_list:
datatmp = data[:]
for i in data:
for j in res_list:
if i['ProjectName'] == j['ProjectName'] and i['ProjectService'] == j['ProjectService']:
# print(res_list.index(j))
print("==[update]==",i)
update_query = {
'$and': [
query,
{
'EcsTag.ProjectName': j['ProjectName'],
'EcsTag.ProjectService': j['ProjectService']
}
]
}
mycol.update_one(update_query ,{
'$set': {
'EcsTag.$': i
}
})
datatmp.remove(i)

print("==[insert]==", datatmp)
mycol.update_one(query, {'$addToSet': {'EcsTag': {'$each': datatmp}}})

else:
print("==[insert]==",data)
mycol.update_one(query, {'$set': {'EcsTag': data }})

db_client.close()

测试更新

调用get_ecs_info()查询项目所在服务器

1
2
3
4
5
6
7
8
# 先自行打标~
if __name__ == '__main__':
tagkey_query = {'EcsTag.ProjectName': 'demo_project2','EcsTag.ProjectService': 'demo_service2'}
tagkey_option = {'_id':0,'InstanceId':1,'VpcAttributes.PrivateIpAddress':1}
ecs_infos = Cmdb.get_ecs_info(tagkey_query,tagkey_option)
print(len(ecs_infos))
for i in ecs_infos:
print(i)