服务器管理系统
错误/便跟日志结束
内存硬盘网卡灯入库结束
API,Client结束
开发背景
起初公司使用 samba 搭建了一个共享平台,里面放了一张表格,每次公司服务器硬件信息变更后,都需要手动进行录入,这样的话,随着时间的推移,数据的可信度越来越低(总有人忘写);
为了搭建运维自动化平台,服务器管理系统是它们的基础
目标
实现服务器硬件信息变更的自动采集
通过API可以向其它人提供资料
设计
采集资产(从每台服务器上采集硬件信息)
API进行比较分析以及存储
后台管理
实现
采集资产
主要涉及内容
1. shell 命令 - 获取硬件信息;
- 根据运维的需求,与运维协商后调整命令代码,shell命令 主要由运维提供;
2. resquests - 使用代码发送 post 请求;
import requests
response = requests.post(self.api, json=hardward_dict) # 将数据转成json 格式发送过去,数据存在在 request.body 中
3. 高可扩展 - 模仿Django中加载中间件的方式,增强可扩展性;
- settings中设置,存放法师为,前面为路劲,最后一个单词为类名
COLLECT_HARDWARE_DICT = {
"basic": "src.plugins.basic.Basic",
"bord": "src.plugins.bord.Bord",
"Nic": "src.plugins.Nic.Nic",
"disk": "src.plugins.disk.Disk",
"memory": "src.plugins.memory.Memory",
}
- 使用方法,循环插件字典并利用 rsplit 进行切片,最大切片次数为1;通过反射实例化并执行这个类中的方法(这个方法里面包含了主要代码,名字可以自定义)
plugins_path, clsName = v.rsplit('.', maxsplit=1)
cls_model_obj = import_module(plugins_path)
cls_obj = getattr(cls_model_obj, clsName)
if cls_obj:
ret = cls_obj().exec(self.handel, self.test)
4. settings配置灵活 - 将不常用的配置文件与经常需要修改的配置文件分开写后合并;
- 在主文件开头的环境变量中存入 自定义settings 的路径
os.environ["custom_settings_path"] = "conf.settings"
- 在单独的文件中合并两个 settings 文件,被用作导入
import os
from importlib import import_module
from . import local_settings
class SettingsHandle(object):
def __init__(self):
self.process()
def process(self):
# 获取 local_settings 中的配置数据
for item in dir(local_settings):
if item.isupper():
setattr(self, item, getattr(local_settings, item))
# 获取自定义的配置数据
custom_settings_path = os.environ.get("custom_settings_path")
settings_obj = import_module(custom_settings_path)
for item in dir(settings_obj):
if item.isupper():
try:
getattr(self, item).update(getattr(settings_obj, item))
except Exception as e:
setattr(self, item, getattr(settings_obj, item))
settings = SettingsHandle()
5. 兼容性设置 - 通过传参数或者类继承的方式使用 agent/ssh/salt 三种方式实现硬件采集。
- 根据该软件可能使用场景进行三种情况的处理,并使用参数传递的方式,实现兼容(只需要在配置中小小的修改下)
1 MODE = {
2 "MODE": "agent",
3
4 }
Settings
1 from importlib import import_module
2 import subprocess
3 import traceback
4 import paramiko
5 from lib.config import settings
6
7 class CollectHardwareInfo(object):
8 def __init__(self, hostname=None):
9 self.hardware_dict = settings.COLLECT_HARDWARE_DICT
10 self.mode = settings.MODE['MODE'].upper()
11 self.test = settings.TEST
12 if self.mode == "SSH":
13 self.hostname = hostname
14 self.port = settings.MODE["port"]
15 self.username = settings.MODE["username"]
16 self.password = settings.MODE["password"]
17 elif self.mode == "SALT":
18 self.server_name = settings.MODE["serverName"]
19
20 def run(self):
21 print("this funcion is run for plugins")
22 # 获取到插件对应的 类 ----------------------------------------------
23 hardward_dict = {}
24 plugins_dict = self.hardware_dict
25 for k, v in plugins_dict.items():
26 tmp = {"flag": True, "error_info": None, "date": None}
27 try:
28 plugins_path, clsName = v.rsplit('.', maxsplit=1)
29 cls_model_obj = import_module(plugins_path)
30 cls_obj = getattr(cls_model_obj, clsName)
31 if cls_obj:
32 ret = cls_obj().exec(self.handel, self.test)
33 tmp["date"] = ret
34 except Exception as e:
35 tmp["flag"] = False
36 tmp["error_info"].append(traceback.format_exc())
37
38 hardward_dict[k] = tmp
39 # input(tmp)
40 return hardward_dict
41
42
43
44 def handel(self, cmd):
45 if self.mode == "AGENT":
46 result = subprocess.getoutput(cmd)
47 elif self.mode == "SSH":
48 ssh = paramiko.SSHClient()
49 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
50 ssh.connect(hostname=self.hostname, port=self.port, username=self.username, password=self.password)
51 stdin, stdout, stderr = ssh.exec_command(cmd)
52 result = stdout.read()
53 ssh.close()
54 elif self.mode == "SALT":
55 result = subprocess.getoutput('salt "%s" cmd.run "%s"' % (self.server_name, cmd))
56 else:
57 raise ("程序运行模式错误,只能是 AGENT/SSH/SALT 中的一种")
58
59 return result
调用
1 class Basic(object):
2 def __init__(self):
3 pass
4
5
6 def exec(self, func, test):
7 # print("there is basic function of exec")
8 if test:
9 output = {
10 'os_platform': "linux",
11 'os_version': "CentOS release 6.6 (Final)\nKernel \r on an \m",
12 'hostname': 'c1.com'
13 }
14 else:
15 output = {
16 'os_platform': func("uname").strip(),
17 'os_version': func("cat /etc/issue").strip().split('\n')[0],
18 'hostname': func("hostname").strip(),
19 }
20
21 return output
22
23
24 def tell(self):
25 print(self)
插件中的实现
API
- 数据入库可以采用前面插件式的方式实现兼容也可以使用反射查看是否存在,这里使用发射举例,详细代码会贴在后面
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # Date: 2017/9/30
4 from repository import models
5 import random
6
7 class HardwareInfoHandle(object):
8
9 def __init__(self):
10 self.server_obj = None
11 # self.server_exist = True
12
13
14
15 def handle(self, info_dict):
16 try:
17 self.server_obj = models.Server.objects.filter(hostname=info_dict["basic"]["date"]["hostname"]).first()
18 create_dict = {}
19 create_dict.update(info_dict["basic"]["date"])
20 info_dict.pop("basic")
21 create_dict.update(info_dict["bord"]["date"])
22 info_dict.pop("bord")
23 if not self.server_obj:
24 self.server_obj = models.Server.objects.create(**create_dict)
25 else:
26 for k, v in create_dict.items():
27 if str(getattr(self.server_obj, k)) != v:
28 setattr(self.server_obj, k, v)
29 self.server_obj.save()
30 except Exception as e:
31 self.new_error_log(title="主板信息未采集成功",
32 content=e)
33
34 for k, v in info_dict.items():
35 if not v.get("flag"):
36 error_info = "服务器{0} {1}信息采集错误,错误内容:{2}".format(
37 info_dict["basic"]["hardware_info"]["basic"]["hostname"],
38 k,
39 v["error_info"])
40 self.new_error_log(title="%s信息未采集成功"%k,
41 content=error_info)
42 else:
43 # [bord, Nic, basic, disk, memory,]
44 funcName = "deal_%s"%k
45 if hasattr(self, funcName):
46 func = getattr(self, funcName)
47 ret = func(v["date"])
48
49
50 def new_error_log(self, title, content):
51 models.ErrorLog.objects.create(server_obj=self.server_obj,
52 title=title,
53 content=content)
54
55
56 def new_server_record(self, content):
57 models.ServerRecord.objects.create(server_obj=self.server_obj,
58 content=content)
59
60
61 def process(self, hardwarm_dict):
62 old_Nic_set = set(
63 [i[0] for i in models.NIC.objects.filter(server_obj=self.server_obj).values_list("name") if i])
64 new_Nic_set = set(hardwarm_dict.keys())
65
66 update_set = old_Nic_set & new_Nic_set
67 append_set = new_Nic_set - old_Nic_set
68 delete_set = old_Nic_set - new_Nic_set
69 if append_set:
70 for item in append_set:
71 hardwarm_dict[item]["server_obj"] = self.server_obj
72 hardwarm_dict[item]["name"] = item
73 models.NIC.objects.create(**hardwarm_dict[item])
74 log_info = ("服务器{0} 卡槽{1}网卡信息采集添加成功".format(self.server_obj.hostname, item))
75 self.new_server_record(content=log_info)
76 if delete_set:
77 for item in delete_set:
78 models.NIC.objects.filter(name=item).delete()
79 log_info = ("服务器{0} 卡槽{1}网卡信息移除成功".format(self.server_obj.hostname, item))
80 self.new_server_record(content=log_info)
81 if update_set:
82 for item in update_set:
83 if random.randint(0, 1):
84 hardwarm_dict[item]['netmask'] = 'asdfasfdfa'
85
86 obj = models.NIC.objects.filter(name=item).first()
87 for k, v in hardwarm_dict[item].items():
88 if getattr(obj, k) != v:
89 setattr(obj, k, v)
90 log_info = ("服务器{0} 卡槽{1}网卡信息跟新成功为{2}".format(self.server_obj.hostname,
91 item, getattr(obj, k)))
92 self.new_server_record(content=log_info)
93 obj.save()
94
95
96 class MyHardWare(HardwareInfoHandle):
97 def deal_Nic(self, hardwarm_dict):
98 """
99 {'eth0': {
100 'up': True,
101 'hwaddr': '00:1c:42:a5:57:7a',
102 'netmask': '255.255.255.0',
103 'ipaddrs': '10.211.55.4'
104 }
105 }
106 """
107
108 old_Nic_set = set([i[0] for i in models.NIC.objects.filter(server_obj=self.server_obj).values_list("name") if i])
109 new_Nic_set = set(hardwarm_dict.keys())
110
111 update_set = old_Nic_set&new_Nic_set
112 append_set = new_Nic_set - old_Nic_set
113 delete_set = old_Nic_set - new_Nic_set
114 if append_set:
115 for item in append_set:
116 hardwarm_dict[item]["server_obj"] = self.server_obj
117 hardwarm_dict[item]["name"] = item
118 models.NIC.objects.create(**hardwarm_dict[item])
119 log_info = ("服务器{0} 卡槽{1}网卡信息采集添加成功".format(self.server_obj.hostname, item))
120 self.new_server_record(content=log_info)
121 if delete_set:
122 for item in delete_set:
123 models.NIC.objects.filter(name=item).delete()
124 log_info = ("服务器{0} 卡槽{1}网卡信息移除成功".format(self.server_obj.hostname, item))
125 self.new_server_record(content=log_info)
126 if update_set:
127 for item in update_set:
128 if random.randint(0, 1):
129 hardwarm_dict[item]['netmask'] = 'asdfasfdfa'
130
131 obj = models.NIC.objects.filter(name=item).first()
132 for k, v in hardwarm_dict[item].items():
133 if getattr(obj, k) != v:
134 setattr(obj, k, v)
135 log_info = ("服务器{0} 卡槽{1}网卡信息跟新成功为{2}".format(self.server_obj.hostname, item, getattr(obj, k)))
136 self.new_server_record(content=log_info)
137 obj.save()
138
139
140 def deal_disk(self, hardwarm_dict):
141
142 if random.randint(0, 1):
143 hardwarm_dict.popitem()
144
145 old_disk_set = set([i[0] for i in models.Disk.objects.filter(server_obj=self.server_obj).values_list("slot")])
146 new_disk_set = set(hardwarm_dict.keys())
147
148 update_set = old_disk_set & new_disk_set
149 append_set = new_disk_set - old_disk_set
150 delete_set = old_disk_set - new_disk_set
151
152 if append_set:
153 for item in append_set:
154 hardwarm_dict[item]["server_obj"] = self.server_obj
155 models.Disk.objects.create(**hardwarm_dict[item])
156 log_info = ("服务器{0} 卡槽{1}硬盘信息采集添加成功".format(self.server_obj.hostname, item))
157 self.new_server_record(content=log_info)
158 if delete_set:
159 for item in delete_set:
160 models.Disk.objects.filter(slot=item).delete()
161 log_info = ("服务器{0} 卡槽{1}硬盘信息移除成功".format(self.server_obj.hostname, item))
162 self.new_server_record(content=log_info)
163
164 if update_set:
165 for item in update_set:
166 if random.randint(0, 1):
167 hardwarm_dict[item]['model'] = 'asdfasfdfa'
168
169 obj = models.Disk.objects.filter(slot=item).first()
170 for k, v in hardwarm_dict[item].items():
171 # print(k, v)
172 if str(getattr(obj, k)) != str(v):
173 # print(getattr(obj, k), type(v), getattr(obj, k) != v)
174 setattr(obj, k, v)
175 log_info = ("服务器{0} 卡槽{1}Disk信息跟新成功为{2}".format(
176 self.server_obj.hostname, item, getattr(obj, k)))
177 self.new_server_record(content=log_info)
178 obj.save()
179
180
181 def deal_memory(self, hardwarm_dict):
182 if random.randint(0,1):
183 hardwarm_dict.popitem()
184
185 old_memory_set = set([i[0] for i in models.Memory.objects.filter(server_obj=self.server_obj).values_list("slot")])
186 new_memory_set = set(hardwarm_dict.keys())
187
188 update_set = old_memory_set & new_memory_set
189 append_set = new_memory_set - old_memory_set
190 delete_set = old_memory_set - new_memory_set
191
192 if append_set:
193 for item in append_set:
194 hardwarm_dict[item]["server_obj"] = self.server_obj
195 models.Memory.objects.create(**hardwarm_dict[item])
196 log_info = ("服务器{0} 卡槽{1}内存信息采集添加成功".format(self.server_obj.hostname, item))
197 self.new_server_record(content=log_info)
198 if delete_set:
199 for item in delete_set:
200 models.Memory.objects.filter(slot=item).delete()
201 log_info = ("服务器{0} 卡槽{1}内存信息移除成功".format(self.server_obj.hostname, item))
202 self.new_server_record(content=log_info)
203
204 if update_set:
205
206 for item in update_set:
207
208 if random.randint(0, 1):
209 hardwarm_dict[item]['sn'] = 'asdfasfdfa'
210
211 obj = models.Memory.objects.filter(slot=item).first()
212 for k, v in hardwarm_dict[item].items():
213 if getattr(obj, k) != v:
214 setattr(obj, k, v)
215 log_info = ("服务器{0} 卡槽{1}内存信息跟新成功为{2}".format(
216 self.server_obj.hostname, item, getattr(obj, k)))
217 self.new_server_record(content=log_info)
218 obj.save()
反射详细代码
后台管理
其他
traceback.format_exc( ) 获取详细的报错信息
import traceback
except Exception as e:
info['status'] = False
info['msg'] = traceback.format_exc()