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\requests.FILES\requests.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)
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.
# 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"} } }
13. 数据可视化
highcharts
echarts
antv
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了