【IMU_Ops】------Ⅱ------ IMU自动化运维平台之CMDB(models)
说明
本文中所有内容仅作为学习使用,请勿用于任何商业用途。
本文为原创,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接和本声明。
#A 我理解的 CMDB
CMDB翻译过来,Configuration Management DataBase,其实也是配置管理的意思,但从实际情况看,CMDB的概念定义已经出现了很大的局限性。在ITIL时代,太多的CMDB落地项目,但鲜有成功。如果要将CMDB中的“配置”两字要从这个里面去掉,那么必须重新给它设定一个边界。在这个边界划分上,我们可以借鉴CMDB需要经历的三个阶段:资产管理、配置管理到今天的IT资源管理。
资产管理围绕硬件属性、采购属性、成本属性、采购属性、固件管理等等;配置管理围绕服务管理、配置管理等等,但配置范畴太广,导致容易管理失控,比如说应用配置、环境配置、中间件参数管理等不适合进入CMDB,分布式配置中心Apollo、Nacos便是佐证;IT资源管理进一步缩小其管理能力范围,把不能够产生服务能力的东西都剥离出去,不纳入管理范围。
一旦我们收敛了CMDB的管理边界是IT资源,此时就需要知道构成资源服务能力的不仅仅是资源自身,还有其关联的资源,资源图谱概念由此产生。资源图谱旨在描述IT世界中存在的各种实体与资源及其关系,构成一张复杂的实体与资源关系图,节点表示资源实体,边则由关系构成。在资源图谱的概念中,对现实世界的图谱描述有两层,第一层是实体层,第二层是资源层,最后是他们的关系表达。实体是从真实的世界出发,直观看到的,比如说网络、防火墙、应用系统、数据库中间件等等,通常一个实体代表一类服务能力。再往下,实体构成对外的服务能力一定是其关联的资源实现的,比如说网络上的端口、应用系统的部署和服务资源、基础设施主机上的CPU/网络/存储等等资源。
那到底什么是IT资源?一切能直接或者间接产生业务服务价值的IT实体都可以称之为资源。本次设计的I·M·U自动化运维平台也将加入CMDB模块,通过将IT资源的整合为运维提供最可靠的基石,然后再结合应用和价值服务来实现我们的运维核心价值。
#B IMU运维平台中的CMDB
这次设计过程中主要是根据个人对运维的理解来完成,当然如果大家有什么不合理的地方或者有待优化的地方还请大家指正出来。当然这个平台全部编写完成后我会将它的github地址更新到文章中,一个平台只要被不停地迭代相信总有一天它会变得非常好用。
在创建models前,我们要确定我们的CMDB中都要包含那些IT资源,每个资源都有那些属性使我们关心的。这里我就直接粘贴我做的图好了。
#C 编写models.py
前段时间忙着备考MBA,所以就没有太多时间来更新博客。今天开始继续慢慢搞这个平台吧。首先我们创建一个名为IMU_DevOps的工程,并在工程目录中新建一个Python package,并命名为Assets。然后编辑Assets中的models.py
切记要导入两个必备的包:models和User(暂时先用Django自带的User,后期根据需求再调整)
首先是创建一个资产共有属性表:Assets
1 class Assets(models.Model): 2 """资产共有属性表""" 3 # 资产类型 4 assets_type = ( 5 ('server', '服务器'), 6 ('network', '网络设备'), 7 ('storage', '存储设备'), 8 ('security', '安全设备'), 9 ('software', '软件资产'), 10 ('office', '办公设备'), 11 ('other', '其他设备'), 12 ) 13 # 资产状态 14 assets_status = ( 15 ('0', '在线'), 16 ('1', '下线'), 17 ('2', '故障'), 18 ('3', '备用'), 19 ('4', '未知'), 20 ) 21 22 assets_type = models.CharField(choices=assets_type, max_length=64, default='other', verbose_name='资产类型') 23 assets_num = models.CharField(max_length=128, unique=True, null=True, verbose_name='资产编号') 24 sn = models.CharField(max_length=128, unique=True, verbose_name="资产序列号") 25 assets_name = models.CharField(max_length=64, unique=True, editable=True, verbose_name='资产名称') 26 assets_status = models.SmallIntegerField(choices=assets_status, default=4, verbose_name='资产状态') 27 assets_manufacturer = models.ForeignKey('ManufacturerAssets', null=True, blank=True, verbose_name='厂商', 28 on_delete=models.SET_NULL) 29 assets_business = models.ForeignKey('BusinessAssets', null=True, blank=True, on_delete=models.SET_NULL, 30 verbose_name='所属业务线') 31 assets_manage_ip = models.GenericIPAddressField(null=True, blank=True, verbose_name='管理IP地址') 32 assets_tags = models.ManyToManyField('TagAssets', blank=True, verbose_name='资产标签') 33 assets_admin = models.ForeignKey(User, blank=True, null=True, related_name='asset_admin', 34 verbose_name='资产管理员', on_delete=models.PROTECT) 35 assets_idc = models.ForeignKey('IDCAssets', related_name='assets_idc', on_delete=models.PROTECT, null=True, 36 blank=True, verbose_name='所在机房') 37 assets_cabinet = models.ForeignKey('CabinetAssets', related_name='assets_cabinet', null=True, blank=True, 38 verbose_name='所在机柜', on_delete=models.PROTECT) 39 assets_contract = models.ForeignKey('ContractAssets', null=True, blank=True, on_delete=models.SET_NULL, 40 verbose_name='合同') 41 assets_purchase_day = models.DateField(null=True, blank=True, verbose_name='购买日期') 42 assets_expire_day = models.DateField(null=True, blank=True, verbose_name='过保日期') 43 assets_price = models.CharField(max_length=100, null=True, blank=True, verbose_name='价格(RMB)') 44 assets_approved = models.ForeignKey(User, null=True, blank=True, related_name='asset_approved', 45 on_delete=models.SET_NULL, verbose_name='批准人') 46 assets_c_time = models.DateTimeField(auto_now_add=True, verbose_name='批准时间') 47 assets_u_time = models.DateTimeField(auto_now_add=True, verbose_name='更新时间') 48 assets_memo = models.TextField(null=True, blank=True, verbose_name='备注') 49 50 def __str__(self): 51 return '<%s> %s' % (self.get_assets_type_display(), self.assets_name) 52 53 class Meta: 54 db_table = 'imu_assets' 55 verbose_name = '资产总表' 56 verbose_name_plural = verbose_name 57 ordering = ['-assets_c_time']
服务器资产表:ServerAssets
1 class ServerAssets(models.Model): 2 """服务器资产表""" 3 4 server_type_choice = ( 5 (0, '塔式服务器'), 6 (1, '刀片服务器'), 7 (2, '机架式服务器'), 8 (3, '虚拟机'), 9 (4, '容器'), 10 (5, '云主机'), 11 ) 12 server_create_by_choice = ( 13 ('auto', '自动发现'), 14 ('manual', '手工录入'), 15 ('batch', '批量导入'), 16 ) 17 # 一对一关联,assets删除时同时删除ServerAssets 18 assets = models.OneToOneField('Assets', on_delete=models.CASCADE) 19 type = models.SmallIntegerField(choices=server_type_choice, default=2, verbose_name="服务器类型") 20 create_by = models.CharField(choices=server_create_by_choice, max_length=32, default='manual', 21 verbose_name='添加方式') 22 hostname = models.CharField(max_length=100, unique=True, null=True, editable=True, verbose_name='主机名') 23 host_on = models.ForeignKey('self', related_name='host_on_server', blank=True, null=True, 24 verbose_name='宿主机', on_delete=models.CASCADE) 25 model = models.CharField(max_length=128, blank=True, null=True, verbose_name='服务器型号') 26 raid_type = models.CharField(max_length=512, blank=True, null=True, verbose_name='RAID卡类型') 27 os_type = models.CharField(max_length=64, blank=True, null=True, verbose_name='操作系统类型') 28 os_distribution = models.CharField(max_length=64, blank=True, null=True, verbose_name='发行商') 29 os_release = models.CharField(max_length=64, blank=True, null=True, verbose_name='内核版本') 30 31 def __str__(self): 32 return '%s--%s--%s--<assets_num:%s>' % (self.assets.assets_name, self.get_type_display(), 33 self.model, self.assets.assets_num) 34 35 class Meta: 36 db_table = 'imu_server_assets' 37 verbose_name = '服务器资产表' 38 verbose_name_plural = verbose_name
网络设备资产表:NetworkAssets
1 class NetworkAssets(models.Model): 2 """网络设备资产表""" 3 4 network_type_choice = ( 5 (0, '路由器'), 6 (1, '交换机'), 7 (2, 'WLC'), 8 (3, 'AP'), 9 (4, '流量控制'), 10 ) 11 assets = models.OneToOneField('Assets', on_delete=models.CASCADE) 12 type = models.SmallIntegerField(choices=network_type_choice, default=1, verbose_name='设备类型') 13 hostname = models.CharField(max_length=100, unique=True, editable=True, verbose_name='主机名') 14 model = models.CharField(max_length=128, blank=True, null=True, verbose_name='设备型号') 15 port_num = models.IntegerField(blank=True, null=True, verbose_name='端口数') 16 firmware = models.CharField(max_length=100, blank=True, null=True, verbose_name='固件版本') 17 18 def __str__(self): 19 return '%s--%s--%s--<assets_sn:%s>' % (self.assets.assets_name, self.get_type_display(), 20 self.model, self.assets.assets_num) 21 22 class Meta: 23 db_table = 'imu_network_assets' 24 verbose_name = '网络设备资产表' 25 verbose_name_plural = verbose_name
安全设备资产表:SecurityAssets
1 class SecurityAssets(models.Model): 2 """安全设备资产表""" 3 4 security_type_choice = ( 5 (0, 'FireWall'), 6 (1, 'IPS'), 7 (2, 'IDS'), 8 (3, 'vpn'), 9 (4, 'AD'), 10 (5, 'AC'), 11 (6, 'WAF'), 12 (7, 'DBScan'), 13 (8, 'AES'), 14 (9, 'other'), 15 ) 16 assets = models.OneToOneField('Assets', on_delete=models.CASCADE) 17 type = models.SmallIntegerField(choices=security_type_choice, default=9, verbose_name='产品类型') 18 hostname = models.CharField(max_length=100, unique=True, editable=True, verbose_name='主机名') 19 model = models.CharField(max_length=128, blank=True, null=True, verbose_name='设备型号') 20 firmware = models.CharField(max_length=100, blank=True, null=True, verbose_name='固件版本') 21 22 def __str__(self): 23 return self.assets.assets_name + "--" + self.get_type_display() + str(self.model) + "id:%s" \ 24 % self.id 25 26 class Meta: 27 db_table = 'imu_security_assets' 28 verbose_name = '安全设备资产表' 29 verbose_name_plural = verbose_name
存储设备资产表:StorageAssets
1 class StorageAssets(models.Model): 2 """存储设备资源表""" 3 4 storage_type_choice = ( 5 (0, '磁盘阵列'), 6 (1, '网络存储'), 7 (2, '磁带库'), 8 (3, '磁带机'), 9 (4, '其他'), 10 ) 11 assets = models.OneToOneField('Assets', on_delete=models.CASCADE) 12 type = models.SmallIntegerField(choices=storage_type_choice, default=4, verbose_name='存储类型') 13 model = models.CharField(max_length=128, blank=True, null=True, verbose_name='设备型号') 14 firmware = models.CharField(max_length=100, blank=True, null=True, verbose_name='固件版本') 15 16 def __str__(self): 17 return '%s--%s--%s<id:%s>' % (self.assets.assets_name, self.get_type_display(), str(self.model), 18 self.id) 19 20 class Meta: 21 db_table = 'imu_storage_assets' 22 verbose_name = '存储设备资产表' 23 verbose_name_plural = verbose_name
软件资产表:SoftAssets
1 class SoftAssets(models.Model): 2 """软件类资产表""" 3 4 soft_type_choice = ( 5 (0, '操作系统'), 6 (1, '数据库授权'), 7 (2, '网络相关授权'), 8 (3, '办公/开发软件'), 9 (4, '业务软件'), 10 (5, '其他'), 11 ) 12 assets = models.OneToOneField('Assets', related_name='soft_assets', on_delete=models.CASCADE) 13 type = models.SmallIntegerField(choices=soft_type_choice, default=5, verbose_name='软件资产类型') 14 license_num = models.IntegerField(default=1, verbose_name='授权数量') 15 version = models.CharField(max_length=100, blank=True, null=True, help_text="例如:Cent 5.2.9-2kali1", 16 verbose_name='软件版本') 17 18 def __str__(self): 19 return '%s--%s--%s' % (self.assets.assets_name, self.get_type_display(), self.version) 20 21 class Meta: 22 db_table = 'imu_soft_assets' 23 verbose_name = '软件资产表' 24 verbose_name_plural = verbose_name
办公相关设备资产表:OfficeAssets
1 class OfficeAssets(models.Model):
2 """办公相关设备资产表"""
3
4 office_type_choice = (
5 (0, '台式电脑'),
6 (1, '笔记本'),
7 (2, '打印机'),
8 (3, '传真机'),
9 (4, 'PAD'),
10 (5, '手机'),
11 (6, '电话'),
12 (7, '其他'),
13 )
14 assets = models.OneToOneField('Assets', related_name='office_assets', on_delete=models.CASCADE)
15 type = models.SmallIntegerField(choices=office_type_choice, default=7, verbose_name='办公设备类型')
16 user = models.CharField(max_length=100, unique=True, editable=True, verbose_name='使用人')
17
18 def __str__(self):
19 return '%s--%s--%s' % (self.assets.assets_name, self.get_type_display(), self.user)
20
21 class Meta:
22 db_table = 'imu_office_assets'
23 verbose_name = '办公设备资产表'
24 verbose_name_plural = verbose_name
厂商信息表:ManufactureAssets
1 class ManufacturerAssets(models.Model): 2 """厂商信息表""" 3 4 name = models.CharField(max_length=100, unique=True, verbose_name='厂商名称') 5 contacts = models.CharField(max_length=100, verbose_name='厂商联系人') 6 phone = models.CharField(max_length=11, null=True, unique=True, verbose_name="联系人电话") 7 email = models.EmailField(unique=True, null=True, verbose_name='联系邮箱') 8 memo = models.CharField(max_length=128, blank=True, null=True, verbose_name='备注') 9 10 def __str__(self): 11 return '%s--%s' % (self.name, self.contacts) 12 13 class Meta: 14 db_table = 'imu_manufacturer_assets' 15 verbose_name = '厂商信息表' 16 verbose_name_plural = verbose_name
业务线信息表:BusinessAssets
1 class BusinessAssets(models.Model): 2 """业务线""" 3 4 name = models.CharField(max_length=100, unique=True, verbose_name='业务线名称') 5 charge = models.ForeignKey('users.UserProfile', blank=True, verbose_name='业务负责人', 6 on_delete=models.PROTECT) 7 department = models.CharField(max_length=100, blank=True, verbose_name='所属部门') 8 9 def __str__(self): 10 return self.name + self.department 11 12 class Meta: 13 db_table = 'imu_business_assets' 14 verbose_name = '业务线资产表' 15 verbose_name_plural = verbose_name
资产标签表:TagAssets
1 class TagAssets(models.Model): 2 """资产标签表""" 3 4 name = models.CharField(max_length=32, unique=True, verbose_name='标签名') 5 c_day = models.DateField(auto_now_add=True, verbose_name='创建日期') 6 7 def __str__(self): 8 return self.name 9 10 class Meta: 11 db_table = 'imu_tag_assets' 12 verbose_name = "标签" 13 verbose_name_plural = verbose_name
机房信息表:IDCAssets
1 class IDCAssets(models.Model):
2 """机房资产表"""
3
4 name = models.CharField('机房名称', max_length=64, unique=True)
5 address = models.CharField('机房地址', max_length=100, unique=True)
6 contract = models.ForeignKey('ContractAssets', null=True, blank=True, on_delete=models.SET_NULL,
7 verbose_name='机房合同')
8 contact = models.CharField('机房联系人', max_length=32)
9 telephone = models.CharField('支持电话', max_length=11, blank=True, null=True)
10 memo = models.CharField('备注', max_length=100, blank=True, null=True)
11
12 def __str__(self):
13 return self.name
14
15 class Meta:
16 db_table = 'imu_idc_assets'
17 verbose_name = '机房表'
18 verbose_name_plural = verbose_name
机柜信息表:CabinetAssets
1 class CabinetAssets(models.Model):
2 """机柜资产表"""
3
4 idc = models.ForeignKey('IDCAssets', related_name='cabinet', on_delete=models.CASCADE)
5 name = models.CharField('机柜名称', max_length=64, unique=True)
6 U_num = models.CharField('机柜总U数', max_length=100, blank=True)
7 used = models.CharField('已使用空间', max_length=100, blank=True)
8 memo = models.CharField('备注', max_length=100, blank=True, null=True)
9
10 def __str__(self):
11 return self.name
12
13 class Meta:
14 db_table = 'imu_cabinet_assets'
15 verbose_name = '机柜表'
16 verbose_name_plural = verbose_name
合同信息表:ContactAssets
1 class ContractAssets(models.Model): 2 """合同资产表""" 3 4 sn = models.CharField('合同号', max_length=128, unique=True) 5 name = models.CharField('合同名称', max_length=100) 6 price = models.IntegerField('合同金额') 7 detail = models.TextField('详细合同', blank=True, null=True) 8 start_day = models.DateField('生效日期', blank=True, null=True) 9 end_day = models.DateField('失效日期', blank=True, null=True) 10 c_day = models.DateField('创建时间', auto_now_add=True) 11 m_day = models.DateField('修改日期', auto_now=True) 12 memo = models.TextField('备注', blank=True, null=True) 13 14 def __str__(self): 15 return self.name 16 17 class Meta: 18 db_table = 'imu_contract_assets' 19 verbose_name = "合同" 20 verbose_name_plural = verbose_name
网卡信息表:NICAssets
1 class NICAssets(models.Model): 2 """网卡组件""" 3 4 assets = models.ForeignKey('Assets', related_name='nic_assets', on_delete=models.CASCADE) # 注意要用外键 5 name = models.CharField('网卡名称', max_length=64, blank=True, null=True) 6 model = models.CharField('网卡型号', max_length=128) 7 mac = models.CharField('MAC地址', max_length=64) # 虚拟机有可能会出现同样的mac地址 8 ip_address = models.GenericIPAddressField('IP地址', blank=True, null=True) 9 net_mask = models.CharField('掩码', max_length=64, blank=True, null=True) 10 binding = models.CharField('绑定地址', max_length=64, blank=True, null=True) 11 12 def __str__(self): 13 return '%s: %s: %s' % (self.assets.assets_name, self.model, self.mac) 14 15 class Meta: 16 db_table = 'imu_nic_assets' 17 verbose_name = '网卡' 18 verbose_name_plural = verbose_name 19 unique_together = ('assets', 'model', 'mac') # 资产、型号、MAC地址必须联合唯一,避免虚拟化中特殊情况发生错误。
硬盘信息表:DiskAssets
1 class DiskAssets(models.Model):
2 """硬盘设备"""
3
4 disk_interface_type_choice = (
5 ('SATA', 'SATA'),
6 ('SAS', 'SAS'),
7 ('SCSI', 'SCSI'),
8 ('SAS', 'SAS'),
9 ('IDE', 'IDE'),
10 ('Fiber Channel', 'Fiber Channel'),
11 ('UNKNOWN', 'UNKNOWN'),
12 )
13
14 assets = models.ForeignKey('Assets', related_name='disk_assets', on_delete=models.CASCADE)
15 slot = models.CharField('所在插槽位', max_length=64, blank=True, null=True)
16 sn = models.CharField('硬盘SN号', max_length=128)
17 model = models.CharField('磁盘型号', max_length=128, blank=True, null=True)
18 brand = models.CharField('磁盘制造商', max_length=128, blank=True, null=True)
19 volume = models.FloatField('磁盘容量(GB)', blank=True, null=True)
20 interface_type = models.CharField('接口类型', max_length=16, choices=disk_interface_type_choice,
21 default='unknown')
22
23 def __str__(self):
24 return '%s: %s: %s: %sGB' % (self.assets.assets_name, self.model, self.slot, self.volume)
25
26 class Meta:
27 db_table = 'imu_disk_assets'
28 verbose_name = '硬盘'
29 verbose_name_plural = verbose_name
30 unique_together = ('assets', 'sn') # 同一资产下的硬盘,根据SN不同必须唯一。
内存信息表:RAMAssets
1 class RAMAssets(models.Model): 2 """内存组件""" 3 4 assets = models.ForeignKey('Assets', related_name='ram_assets', on_delete=models.CASCADE) 5 sn = models.CharField('SN号', max_length=128, blank=True, null=True) 6 model = models.CharField('内存型号', max_length=128, blank=True, null=True) 7 brand = models.CharField('内存制造商', max_length=128, blank=True, null=True) 8 slot = models.CharField('插槽', max_length=64) 9 volume = models.IntegerField('内存大小(GB)', blank=True, null=True) 10 11 def __str__(self): 12 return '%s: %s: %s: %s' % (self.assets.assets_name, self.model, self.slot, self.volume) 13 14 class Meta: 15 db_table = 'imu_ram_assets' 16 verbose_name = '内存' 17 verbose_name_plural = verbose_name 18 unique_together = ('assets', 'slot') # 同一资产下的内存,根据插槽不同必须唯一。
CPU信息表:CPUAssets
1 class CPUAssets(models.Model):
2 """CPU组件"""
3
4 assets = models.OneToOneField('Assets', related_name='cpu_assets', on_delete=models.CASCADE)
5 cpu_model = models.CharField('CPU型号', max_length=128, blank=True, null=True)
6 cpu_count = models.PositiveSmallIntegerField('物理CPU个数', default=1)
7 cpu_core_count = models.PositiveSmallIntegerField('CPU核数', default=1)
8
9 def __str__(self):
10 return self.assets.assets_name
11
12 class Meta:
13 db_table = 'imu_cpu_assets'
14 verbose_name = 'CPU'
15 verbose_name_plural = verbose_name
域名资产表:DomainAssets
1 class DomainAssets(models.Model): 2 """域名资产表""" 3 4 name = models.CharField('域名名称', max_length=64, blank=True, null=True) 5 address = models.URLField('域名地址', max_length=100, null=False) 6 int_ip = models.GenericIPAddressField('对内IP地址', blank=True, null=True) 7 out_ip = models.GenericIPAddressField('对外IP地址', blank=True, null=True) 8 department = models.CharField('所属部门', max_length=64, blank=True, null=True) 9 admin = models.ForeignKey('users.UserProfile', blank=True, related_name='domain_admin', 10 verbose_name='域名管理员', on_delete=models.PROTECT) 11 12 class Meta: 13 db_table = 'imu_domain_assets' 14 verbose_name = '域名表' 15 verbose_name_plural = verbose_name
供应商信息表:ProviderAssets
1 class ProviderAssets(models.Model): 2 """供应商信息表""" 3 4 name = models.CharField('供应商名称', max_length=64, unique=True) 5 contact = models.CharField('技术支持人员', max_length=32, blank=True, null=True) 6 phone = models.CharField('支持电话', max_length=11, blank=True, null=True) 7 email = models.EmailField('供应商邮箱', unique=True) 8 memo = models.CharField('备注', max_length=128, blank=True, null=True) 9 10 class Meta: 11 db_table = 'imu_provider_assets' 12 verbose_name = '供应商表' 13 verbose_name_plural = verbose_name
云平台资产表:CloudAssets
1 class CloudAssets(models.Model): 2 """云平台资产表""" 3 4 cloud_type_choice = ( 5 (0, '公有云'), 6 (1, '私有云'), 7 ) 8 type = models.SmallIntegerField('云类型', choices=cloud_type_choice, default=1) 9 brand = models.CharField('云厂商', max_length=100, blank=True, null=True) 10 admin = models.ForeignKey('users.UserProfile', blank=True, related_name='cloud_admin', 11 verbose_name='云平台管理员', on_delete=models.PROTECT) 12 13 class Meta: 14 db_table = 'imu_cloud_assets' 15 verbose_name = '云平台资产表' 16 verbose_name_plural = verbose_name
其他资产表:OtherAssets
1 class OtherAssets(models.Model): 2 """其他资产表""" 3 4 name = models.CharField('资产名称', max_length=64, unique=True) 5 sn = models.CharField('SN号', max_length=128, blank=True, null=True) 6 admin = models.ForeignKey('users.UserProfile', blank=True, related_name='other_admin', 7 verbose_name='资产管理员', on_delete=models.PROTECT) 8 9 def __str__(self): 10 return self.name 11 12 class Meta: 13 db_table = 'imu_other_assets' 14 verbose_name = '其他资产表' 15 verbose_name_plural = verbose_name
至此,前期工作量比较大的部分就告一段落了。models.py文件并未完成所有的编写,后期根据功能可能还会增加部分代码。
纠错:assets中的assets_u_time类型要改成“auto_now=True”,否则更新资产时,更新时间无法自动更新!