服务器管理系统

 

错误/便跟日志结束

内存硬盘网卡灯入库结束

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 三种方式实现硬件采集。

  • 根据该软件可能使用场景进行三种情况的处理,并使用参数传递的方式,实现兼容(只需要在配置中小小的修改下)

资源服务器架构 服务器资源管理系统_硬件信息

资源服务器架构 服务器资源管理系统_资源服务器架构_02

1 MODE = {
2     "MODE": "agent",
3 
4 }

Settings

资源服务器架构 服务器资源管理系统_硬件信息

资源服务器架构 服务器资源管理系统_资源服务器架构_02

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

调用

资源服务器架构 服务器资源管理系统_硬件信息

资源服务器架构 服务器资源管理系统_资源服务器架构_02

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

  • 数据入库可以采用前面插件式的方式实现兼容也可以使用反射查看是否存在,这里使用发射举例,详细代码会贴在后面

资源服务器架构 服务器资源管理系统_硬件信息

资源服务器架构 服务器资源管理系统_资源服务器架构_02

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()