CMDB开发(三)

6.API验证

# 加密复习
#1.简单的对称加密,token是固定的
客户端请求:
import requests
# 1.自定义token值
token = 'cxiong_token'

# token认证都是在请求头里携带
res = requests.get('http://127.0.0.1:8000/getInfo',
                   headers={'token': token}
                   )
print(res.text)

# 服务端修改:autoserver/views.py
# 修改getInfo函数
def getInfo(requests):
    # 数据获取
    if requests.method == 'POST':
        server_info = json.loads(requests.body)
        print(server_info)
        # for k,v in server_info.items():
        #     print(k,v)
        return HttpResponse('OK')
    # 服务端如何获取请求头里面的数据
    # requests.POST
equests.FILES
equests.GET
    # HTTP_TOKEN
    # print(requests.META)
    token = requests.META.get('HTTP_TOKEN')
    server_token ='cxiong_token'
    if token != server_token:
        return HttpResponse('token值错误')
    # 链接后台数据库获取主机名列表并返回
    return HttpResponse(['c1.com', 'c2.com'])

# 2. 加盐处理:动态加盐
            一般使用当前时间结合token加密再发送
# client请求
# 自定义token值
token = 'cxiong_token1'
import time

client_time = time.time()
tmp = '%s|%s' % (token, client_time)
print(tmp)
# 加密
import hashlib

md5 = hashlib.md5()  # 使用md5加密
# 传入明文数据:数据必须是bytes类型
md5.update(tmp.encode('utf8'))
# 生成密文数据
res = md5.hexdigest()
print(res)
# 将加密的密文数据发送给服务端,也需要将加密之前的明文数据也发给服务端,让服务端通过相同的方法进行比对
client_md5_token = '%s|%s' % (res, client_time)
# token认证都是请求头里携带
data = requests.get('http://127.0.0.1:8000/getInfo',
                    headers={'token': client_md5_token}
                    )
print(data.text)

# 服务端处理
token = requests.META.get('HTTP_TOKEN')
    server_token = 'cxiong_token'
    client_md5_token,client_time = token.split('|')
    print(client_md5_token,client_time)
    # 加密认证
    tmp = '%s|%s'%(server_token,client_time)
    #加密
    import hashlib
    md5 = hashlib.md5()
    md5.update(tmp.encode('utf8'))
    res = md5.hexdigest()
    print(res)
    if res != client_md5_token:
        return HttpResponse('token值错误')

# 3.设置token失效时间
    client_md5_token,client_time = token.split('|')
    import time
    server_time = time.time()
    if server_time - client_time > 10:
        return HttpResponse('token超时了')
"""大部分API验证做到第三步就可以了"""
# 4.设置token使用次数
"""
黑客可能会在失效时间之前截取并发送请求
    思路:
        第一次来的时候先去redis中判断是否存在
        如果存在则说明token已经使用过了
        不存在则添加到redis中(并且设置保存的超时时间)
"""
# 5. 固定token由服务端生成发到客户端再组合的方式(看业务是否重要了)

7.后台目录规划

# django是一款专注于开发app的框架
"""
1.api:接收数据并处理入库(API验证)
2.backend : 后台管理
# 上述两个app都需要使用模型表,那么模型表写在哪个models文件
3.repository:单独存储模型表相关代码
"""

 8.模型表设计

"""
#{'board': {'status': 10000, 'data': {'manufacturer': 'Parallels Software International Inc.', 'model': 'Parallels Virtual Platform', 'sn': 'Parallels-1A 1B CB 3B 64 66 4B 13 86 B0 86 FF 7E 2B 20 30'}}

1.设计模型表的时候字段名与收集过来的字典的key保持一致
    models.User.objects.create(name='jason',age=18)
    d = {'name':'cxiong','age':32}
    models.User.objects.create(**d) # **d将数据打散为name=jason这种模式
    为了录入数据的时候可以借助于**快速录入
2.模型表设计及表关系判断,根据情况自己关联
"""
如下图

9.模型表数据录入

1.11张表相关统计表
2.数据库迁移命令
    makemigrations
    migrate
3.django admin后台管理
    1.创建超级用户:createsuperuser
    2.注册模型表
repository/admin.py
4.手动录入数据 1.业务线表 2.服务器表
对应表:
# repository/models.py
from django.db import models


class UserProfile(models.Model):
    """
    用户信息
    python2  字符串前面需要加u: u姓名
    """
    name = models.CharField(u'姓名', max_length=32)
    email = models.EmailField(u'邮箱')
    phone = models.CharField(u'座机', max_length=32)
    mobile = models.CharField(u'手机', max_length=32)
    password = models.CharField(u'密码', max_length=64)

    # 规定django-admin后台管理显示的中文表
    class Meta:
        verbose_name_plural = "用户表"

    def __str__(self):
        return self.name


class UserGroup(models.Model):
    """
    用户组
    """
    name = models.CharField(max_length=32, unique=True)
    users = models.ManyToManyField('UserProfile')

    class Meta:
        verbose_name_plural = "用户组表"

    def __str__(self):
        return self.name


class BusinessUnit(models.Model):
    """
    业务线
    """
    name = models.CharField('业务线', max_length=64, unique=True)
    contact = models.ForeignKey('UserGroup', verbose_name='业务联系人', related_name='c')
    manager = models.ForeignKey('UserGroup', verbose_name='系统管理员', related_name='m')

    class Meta:
        verbose_name_plural = "业务线表"

    def __str__(self):
        return self.name


class IDC(models.Model):
    """
    机房信息
    """
    name = models.CharField('机房', max_length=32)
    floor = models.IntegerField('楼层', default=1)

    class Meta:
        verbose_name_plural = "机房表"

    def __str__(self):
        return self.name


class Tag(models.Model):
    """
    资产标签
    """
    name = models.CharField('标签', max_length=32, unique=True)

    class Meta:
        verbose_name_plural = "标签表"

    def __str__(self):
        return self.name


class Server(models.Model):
    """
    服务器信息
    """
    device_type_choices = (
        (1, '服务器'),
        (2, '交换机'),
        (3, '防火墙'),
    )
    device_status_choices = (
        (1, '上架'),
        (2, '在线'),
        (3, '离线'),
        (4, '下架'),
    )
    device_type_id = models.IntegerField('服务器类型', choices=device_type_choices, default=1)
    device_status_id = models.IntegerField('服务器状态', choices=device_status_choices, default=1)

    cabinet_num = models.CharField('机柜号', max_length=30, null=True, blank=True)
    cabinet_order = models.CharField('机柜中序号', max_length=30, null=True, blank=True)

    idc = models.ForeignKey('IDC', verbose_name='IDC机房', null=True, blank=True)
    business_unit = models.ForeignKey('BusinessUnit', verbose_name='属于的业务线', null=True, blank=True)

    tag = models.ManyToManyField('Tag')

    hostname = models.CharField('主机名', max_length=128, unique=True)
    sn = models.CharField('SN号', max_length=64, db_index=True)
    manufacturer = models.CharField(verbose_name='制造商', max_length=64, null=True, blank=True)
    model = models.CharField('型号', max_length=64, null=True, blank=True)

    manage_ip = models.GenericIPAddressField('管理IP', null=True, blank=True)

    os_platform = models.CharField('系统', max_length=16, null=True, blank=True)
    os_version = models.CharField('系统版本', max_length=16, null=True, blank=True)

    cpu_count = models.IntegerField('CPU个数', null=True, blank=True)
    cpu_physical_count = models.IntegerField('CPU物理个数', null=True, blank=True)
    cpu_model = models.CharField('CPU型号', max_length=128, null=True, blank=True)

    create_at = models.DateTimeField(auto_now_add=True, blank=True)

    class Meta:
        verbose_name_plural = "服务器表"

    def __str__(self):
        return self.hostname


class Disk(models.Model):
    """
    硬盘信息
    """
    slot = models.CharField('插槽位', max_length=8)
    model = models.CharField('磁盘型号', max_length=32)
    capacity = models.CharField('磁盘容量GB', max_length=32)
    pd_type = models.CharField('磁盘类型', max_length=32)
    server_obj = models.ForeignKey('Server', related_name='disk')

    class Meta:
        verbose_name_plural = "硬盘表"

    def __str__(self):
        return self.slot


class NIC(models.Model):
    """
    网卡信息
    """
    name = models.CharField('网卡名称', max_length=128)
    hwaddr = models.CharField('网卡mac地址', max_length=64)
    netmask = models.CharField(max_length=64)
    ipaddrs = models.CharField('ip地址', max_length=256)
    up = models.BooleanField(default=False)
    server_obj = models.ForeignKey('Server', related_name='nic')

    class Meta:
        verbose_name_plural = "网卡表"

    def __str__(self):
        return self.name


class Memory(models.Model):
    """
    内存信息
    """
    slot = models.CharField('插槽位', max_length=32)
    manufacturer = models.CharField('制造商', max_length=32, null=True, blank=True)
    model = models.CharField('型号', max_length=64)
    capacity = models.FloatField('容量', null=True, blank=True)
    sn = models.CharField('内存SN号', max_length=64, null=True, blank=True)
    speed = models.CharField('速度', max_length=16, null=True, blank=True)

    server_obj = models.ForeignKey('Server', related_name='memory')

    class Meta:
        verbose_name_plural = "内存表"

    def __str__(self):
        return self.slot


class AssetRecord(models.Model):
    """
    资产变更记录,creator为空时,表示是资产汇报的数据。
    """
    asset_obj = models.ForeignKey('Server', related_name='ar')
    content = models.TextField(null=True)  # 新增硬盘
    creator = models.ForeignKey('UserProfile', null=True, blank=True)
    create_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "资产记录表"

    def __str__(self):
        return "%s-%s-%s" % (self.asset_obj.idc.name, self.asset_obj.cabinet_num, self.asset_obj.cabinet_order)


class ErrorLog(models.Model):
    """
    错误日志,如:agent采集数据错误 或 运行错误
    """
    asset_obj = models.ForeignKey('Server', null=True, blank=True)
    title = models.CharField(max_length=16)
    content = models.TextField()
    create_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "错误日志表"

    def __str__(self):
        return self.title

# 注册:repository/admin.py
# 注册模型表
from django.contrib import admin
from repository import models
# Register your models here.

admin.site.register(models.Server)
admin.site.register(models.Disk)
admin.site.register(models.IDC)
admin.site.register(models.NIC)
admin.site.register(models.Tag)
admin.site.register(models.Memory)
admin.site.register(models.ErrorLog)
admin.site.register(models.AssetRecord)
admin.site.register(models.UserProfile)
admin.site.register(models.UserGroup)
admin.site.register(models.BusinessUnit)
11张表

9.视图函数

# API/views.py中getinfo函数将获取客户端上传的数据采集并处理
# 资产是否存在
server_info = json.loads(requests.body)
        # 通过主机名获取老的数据对应的记录
        hostname = server_info['basic']['data']['hostname']
        # 去数据库中查询是否有该服务器信息
        old_server_info = models.Server.objects.filter(hostname=hostname).first()
        # 判断
        if not old_server_info:
            return HttpResponse("资产不存在")

以硬盘数据为例,进行增删改查

# 先判断status状态码是否正确
# 对数据进行筛选入库
        # 以硬盘数据为例
        # 1.校验相应状态码
        if server_info['disk']['status'] != 10000:
            # 2.记录错误日志
            models.ErrorLog.objects.create(
                asset_obj=old_server_info,
                title='%s 采集硬盘数据出错了' % hostname,
                content=server_info['disk']['data']
            )
            # 最新硬盘数据
        new_disk_info = server_info['disk']['data']
        """
        new_slot_list = [0,2]
        old_slot_list = [0,1]
        新增:new_slot_list - old_slot_list = 2
        删除:old_slot_list - new_slot_list = 1
        修改:交集
        """
        # 老的硬盘数据
        old_disk_info = models.Disk.objects.filter(server_obj=old_server_info).all()
        # 先获取新硬盘数据所有slot号
        new_slot_list = list(new_disk_info.keys())
        # 再获取旧的硬盘数据所有的slot号
        old_slot_list = [obj.slot for obj in old_disk_info]
        # 利用集合操作获取新增的数据
        add_slot_list = set(new_slot_list) - set(old_slot_list)
        if add_slot_list:
            record_list = []
            for slot in add_slot_list:
                disk_res = new_disk_info[slot]
                # 添加变更记录
                tmp = '添加插槽是:{slot},磁盘类型:{pd_type},磁盘容量是:{capacity},磁盘型号:{model}'.format(**disk_res)
                # 添加disk记录  将服务器对象直接添加到字典中,之后直接利用**打散
                disk_res['server_obj'] = old_server_info
                models.Disk.objects.create(**disk_res)
                record_list.append(tmp)
            # 将变更信息添加到记录表中
            record_str = ';'.join(record_list)
            models.AssetRecord.objects.create(asset_obj=old_server_info,content=record_str)

        return HttpResponse('OK')
硬盘增加插槽
# 硬盘数据的删除
        del_slot_list = set(old_slot_list) - set(new_slot_list)
        if del_slot_list:
            # 数据的删除:slot__in多条数据
            models.Disk.objects.filter(slot__in=del_slot_list,server_obj=old_server_info).delete()
            # 记录变更
            record_str = "删除的槽位slot是:%s"%(';'.join(del_slot_list))
            models.AssetRecord.objects.create(asset_obj=old_server_info,content=record_str)
删除硬盘槽位
# API/views.py
        # 硬盘数据的修改
        up_slot_list = set(new_slot_list) & set(old_slot_list)
        if up_slot_list:
            record_list = []
            for slot in up_slot_list:
                new_disk_row = new_disk_info[slot] # 新数据 字典
                # 数据库中老数据 对象
                old_disk_row = models.Disk.objects.filter(slot=slot,server_obj=old_server_info).first()
                # 反射:通过字符串操作对象的属性和方法(出现字符串和对象毫不犹豫使用反射的思路)
                for k,new_v in new_disk_row.items():
                    # 1.利用反射先从老的数据中获取数据
                    old_v = getattr(old_disk_row,k)
                    # 2.判断老的数据和新的数据是否相同
                    if new_v != old_v:
                        tmp = '槽位:%s,%s由原来的%s变成了%s'%(slot,k,old_v,new_v)
                        record_list.append(tmp)
                        # 3.更新数据:将新数据设置到老的数据对象中
                        setattr(old_disk_row,k,new_v)
                        # 4.调用对象的save方法,更新数据
                        old_disk_row.save()
            if record_list:
                models.AssetRecord.objects.create(asset_obj=old_server_info,content=';'.join(record_list))

        return HttpResponse('OK')
硬盘数据的修改

 10.前后端分离

to B:to business  面向企业产品
to C:to client    面向客户产品
    to C:
        前后端分离
            讲究美观和用户体验
        前端
            vue react angular.js
        # 前后端分离网站:路飞  
    to B:(CMDB)
        前后端不分离
            讲究用途和功能齐全
        前端
            bootstrap、layui
        # 前后端不分离网站:很丑的基本都是

 11.前端框架使用layui

https://www.layui.com/demo/admin.html

1.使用CDN模式
    https://www.bootcdn.cn/

2.直接下载文档
        使用文档中的css和js
            layui/layui.all.js
# <script src="/static/layui/layui.all.js"></script>
            layui/css/layui.css
# <link rel="stylesheet" href="/static/layui/css/layui.css">

 12. Xadmin使用(后台管理)

  Django-xadmin介绍

    Django是python的重量级web框架,写得少,做得多,非常适合后端开发,它很大的一个亮点是,自带后台管理模块,但它自带的后台管理有点丑,而Xadmin是基于bootstrap开发的一套后台管理框架,界面非常美观,只需几步就可以替换自带的Django_admin
xadmin有很多小bug,使用的时候最好心理准备,可能需要你自己修改源码
    基于bootstrap的后台管理
vue-element-admin:最新使用的
    基于vue开发的非常酷炫的后台管理

安装步骤:

1.在python 2.x版本中安装方法
    pip install xadmin

2.xadmin在python3.6.x时代的安装方法,需要以下插件
    pip3 install django-import-export
    pip3 install django-reversion
    pip3 install django-formtools==2.1
    pip3 install future
    pip3 install httplib2
    pip3 install six
    pip3 install django-crispy-forms

bootstrap高级模板:https://wrapbootstrap.com/

3.下载xadmin
  https://github.com/sshwsfc/xadmin
   只需要zip文件中的xadmin目录

4.在django中的根目录下创建extra_apps(如果不存在此文件夹则创建, 然后鼠标右键extra_app 随后 mark as sources root,用来创建第三方应用
Python Package是带init文件的,跟普通Package不同)
创建完extra_apps,需要在settings中配置一下extra_apps。设置为可搜索的路径
# 1.创建完extra_apps,需要在settings中配置一下extra_apps。设置为可搜索的路径

import sys
sys.path.insert(0, os.path.join(BASE_DIR, 'extra_apps')) # 把extra_apps文件夹添加到搜索目录中

# 2.配置到 INSTALLED_APPS
## 显示中文
LANGUAGE_CODE = 'zh-hans'
 
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'
# xadmin后台界面显示中文
LANGUAGE_CODE = 'zh-hans'
# 新增 INSTALLED_APPS = [ 'xadmin', 'crispy_forms', # 注意crispy_forms之间是下划线隔开,不是横线 ] # 3. urls.py中把admin换成xadmin import xadmin from django.conf.urls import url from django.contrib import admin urlpatterns = [ # url('admin/', admin.site.urls), url(r'^xadmin/', xadmin.site.urls), ] # 4.迁移文件:迁移完成之后数据库会多几张xadmin_开头的表 python3 manage.py makemigrations python3 manage.py migrate """因为版本的原因,有一些报错慢慢解决""" #5.pycharm创建superuser 用户 python3 manage.py 访问:http://127.0.0.1:8000/xadmin/
Xadmin使用
#1.需要在后台管理app中创建adminx.py文件
import xadmin
from repository import models
from xadmin import views


class UserProfileAdmin(object):
        # 1.显示的字段名称
    list_display = ['id','name' ,'email','phone','mobile']
    
        # 2.搜索时可输入的字段内容
    search_fields = ['id', 'name', 'email', 'phone']
    
    # 3.点击id可进入详细界面进行编辑(默认的)
    list_display_links = ('id',)  
    
    # 4.可编辑的列名
    list_editable = ['name' ,'email','phone','mobile']
    # list_filter = ['name' ,'email','phone','mobile']
    
    # 5.每页显示多少条
    list_per_page = 20 
    
        #6.根据id排序 
    ordering = ('id',) 
     
    # 7.设置只读字段 
    readonly_fields = ('id',) 
    
    #8.显示本条数据的所有信息
    show_detail_fields = ['asset_name'] 
    
    
    # data_charts = {
    #     "user_count": {'title': u"用户分布", "x-field": "name", "y-field": ("id",),},
    #     # "avg_count": {'title': u"Avg Report", "x-field": "date", "y-field": ('avg_count',), "order": ('date',)}
    # }

xadmin.site.register(models.UserProfile,UserProfileAdmin)

# 2.是否设置书签名
默认是开启书签的
show_bookmarks 属性: 设置是否开启书签功能, 默认为 True
list_bookmarks 属性: 设置默认的书签. 用户可以在列表页面添加自己的书签, 你也可以实现设定好一些书签

list_bookmarks = [{
    "title": "存在邮箱",   # 书签的名称, 显示在书签菜单中
    "query": {"user_email__contains": '@'}, 过滤参数, 是标准的 queryset 过滤
    "order": ("-user_name",), # 排序参数
    "cols": ('user_name', 'user_email', 'user_mobile'),# 显示的列
    }]

# 3.数据导出
"""
如果想要导出Excel数据,需要安装xlwt。

默认情况下,xadmin会提供Excel,CSV,XML,json四种格式的数据导出,可以通过设置OptionClass的list_export属性来指定使用哪些导出格式(四种格式分别用xls,csv,xml,json表示)或是将list_export设置为None来禁用数据导出功能
"""
list_export = ('xls', 'xml', 'json')
list_export_fields = ('id', 'name', 'title')

# 4. 设置全局配置
#如下的代码可以在任何的app中
import xadmin
from repository import models

from xadmin import views
# 全局修改,固定写法
class GlobalSettings(object):
    # 修改title
    site_title = 'xxx后台管理界面'
    # 修改footer
    site_footer = 'xxx的公司'
    # 收起菜单
    menu_style = 'accordion'
    
        # 设置 models图标
    # https://v3.bootcss.com/components/
        global_search_models = [models.Disk, models.Server]
    global_models_icon = {
        # Server: "glyphicon glyphicon-tree-conifer", Pool: "fa fa-cloud"
        models.Server: "fa fa-linux", 
          models.Disk: "fa fa-cloud"
    }

    
# 将title和footer信息进行注册
xadmin.site.register(views.CommAdminView,GlobalSettings)


# 创建xadmin的最基本管理器配置,并与view绑定
class BaseSetting(object):
    # 开启主题功能
    enable_themes = True
    use_bootswatch = True

# 将基本配置管理与view绑定
xadmin.site.register(views.BaseAdminView,BaseSetting)

#5.图表显示(不好看不用)
data_charts = {
  "host_idc_counts": {
    'title': '机房统计',
    'x-field': "idc",
    'y-field': ("idc",),
    'option': {
        "series": {"bars": {"align": "center", "barWidth": 0.3, "show": True}},
        "xaxis": {"aggregate": "count", "mode": "categories"}
    }
}
xadmin使用

13. 数据可视化

highcharts
echarts
antv

原文地址:https://www.cnblogs.com/yangmeichong/p/14464762.html