[TOC]
## **一、創建項目**
讓我們新建一個項目。
首先,通過Pycharm直接創建CMDB項目,安裝最新的Django3.2,生成一個app,名字就叫做assets,最后配置好settings中的語言和時區。這些基本過程以后就不再贅述了,不熟悉的請參考教程的前面部分。
創建成功后,初始狀態如下圖所示:

<br />
## **二、模型設計**
**說明:本項目采用SQLite數據庫**
模型設計是整個項目的重中之重,其它所有的內容其實都是圍繞它展開的。
而我們設計數據模型的原則和參考依據是前一節分析的項目需求和數據分類表。
我們的模型架構圖如下所示:

<br />
### **1.資產共有數據模型**
打開assets/models.py文件,首先我們要設計一張資產共有數據表:
<details>
<summary>assets/models.py</summary>
```
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Asset(models.Model):
""" 所有資產的共有數據表 """
asset_type_choice = (
('server', '服務器'),
('networkdevice', '網絡設備'),
('storagedevice', '存儲設備'),
('securitydevice', '安全設備'),
('software', '軟件資產'),
)
asset_status = (
(0, '在線'),
(1, '下線'),
(2, '未知'),
(3, '故障'),
(4, '備用'),
)
asset_type = models.CharField(choices=asset_type_choice, max_length=64, default='server', verbose_name="資產類型")
name = models.CharField(max_length=64, unique=True, verbose_name="資產名稱") # 不可重復
sn = models.CharField(max_length=128, unique=True, verbose_name="資產序列號") # 不可重復
business_unit = models.ForeignKey('BusinessUnit', null=True, blank=True, verbose_name='所屬業務線',
on_delete=models.SET_NULL)
status = models.SmallIntegerField(choices=asset_status, default=0, verbose_name='設備狀態')
manufacturer = models.ForeignKey('Manufacturer', null=True, blank=True, verbose_name='制造商',
on_delete=models.SET_NULL)
manage_ip = models.GenericIPAddressField(null=True, blank=True, verbose_name='管理IP')
tags = models.ManyToManyField('Tag', blank=True, verbose_name='標簽')
admin = models.ForeignKey(User, null=True, blank=True, verbose_name='資產管理員', related_name='admin',
on_delete=models.SET_NULL)
idc = models.ForeignKey('IDC', null=True, blank=True, verbose_name='所在機房', on_delete=models.SET_NULL)
contract = models.ForeignKey('Contract', null=True, blank=True, verbose_name='合同', on_delete=models.SET_NULL)
purchase_day = models.DateField(null=True, blank=True, verbose_name="購買日期")
expire_day = models.DateField(null=True, blank=True, verbose_name="過保日期")
price = models.FloatField(null=True, blank=True, verbose_name="價格")
approved_by = models.ForeignKey(User, null=True, blank=True, verbose_name='批準人', related_name='approved_by',
on_delete=models.SET_NULL)
memo = models.TextField(null=True, blank=True, verbose_name='備注')
c_time = models.DateTimeField(auto_now_add=True, verbose_name='批準日期')
m_time = models.DateTimeField(auto_now=True, verbose_name='更新日期')
def __str__(self):
return '<%s> %s' % (self.get_asset_type_display(), self.name)
class Meta:
verbose_name = '資產總表'
verbose_name_plural = "資產總表"
ordering = ['-c_time']
```
</details>
<br />
說明:
* 導入django.contrib.auto.models內置的User表,作為我們CMDB項目的用戶表,用于保存管理員和批準人員的信息;
* sn這個數據字段是所有資產都必須有,并且唯一不可重復的!通常來自自動收集的數據中;
* name和sn一樣,也是唯一的;
* asset\_type\_choice和asset\_status分別設計為兩個選擇類型
* adamin和approved\_by是分別是當前資產的管理員和將該資產上線的審批員,為了區分他們,設置了related\_name;
* Asset表中的很多字段內容都無法自動獲取,需要我們手動輸入,比如合同、備注。
* 最關鍵的是其中的一些外鍵字段,設置為`on_delete=models.SET_NULL`,這樣的話,當關聯的對象被刪除的時候,不會影響到資產數據表。
<br />
### **2.服務器模型**
服務器作為資產的一種,而且是最主要的管理對象,包含了一些重要的字段,其模型結構如下:
```
class Server(models.Model):
"""服務器設備"""
sub_asset_type_choice = (
(0, 'PC服務器'),
(1, '刀片機'),
(2, '小型機'),
)
created_by_choice = (
('auto', '自動添加'),
('manual', '手工錄入'),
)
asset = models.OneToOneField('Asset', on_delete=models.CASCADE) # 非常關鍵的一對一關聯!asset被刪除的時候一并刪除server
sub_asset_type = models.SmallIntegerField(choices=sub_asset_type_choice, default=0, verbose_name="服務器類型")
created_by = models.CharField(choices=created_by_choice, max_length=32, default='auto', verbose_name="添加方式")
hosted_on = models.ForeignKey('self', related_name='hosted_on_server',
blank=True, null=True, verbose_name="宿主機", on_delete=models.CASCADE) # 虛擬機專用字段
model = models.CharField(max_length=128, null=True, blank=True, verbose_name='服務器型號')
raid_type = models.CharField(max_length=512, blank=True, null=True, verbose_name='Raid類型')
os_type = models.CharField('操作系統類型', max_length=64, blank=True, null=True)
os_distribution = models.CharField('發行商', max_length=64, blank=True, null=True)
os_release = models.CharField('操作系統版本', max_length=64, blank=True, null=True)
def __str__(self):
return '%s--%s--%s <sn:%s>' % (self.asset.name, self.get_sub_asset_type_display(), self.model, self.asset.sn)
class Meta:
verbose_name = '服務器'
verbose_name_plural = "服務器"
```
說明:
* 每臺服務器都唯一關聯著一個資產對象,因此使用OneToOneField構建了一個一對一字段,這非常重要!
* 服務器又可分為幾種子類型,這里定義了三種;
* 服務器添加的方式可以分為手動和自動;
* 有些服務器是虛擬機或者docker生成的,沒有物理實體,存在于宿主機中,因此需要增加一個hosted\_on字段;這里認為,宿主機如果被刪除,虛擬機也就不存在了;
* 服務器有型號信息,如果硬件信息中不包含,那么指的就是主板型號;
* Raid類型在采用了Raid的時候才有,否則為空
* 操作系統相關信息包含類型、發行版本和具體版本。
<br />
### **3.安全、網絡、存儲設備和軟件資產的模型**
這部分內容不是項目的主要內容,而且數據大多數不能自動收集和報告,很多都需要手工錄入。我這里給出了范例,更多的數據字段,可以自行添加。
```
class SecurityDevice(models.Model):
"""安全設備"""
sub_asset_type_choice = (
(0, '防火墻'),
(1, '入侵檢測設備'),
(2, '互聯網網關'),
(4, '運維審計系統'),
)
asset = models.OneToOneField('Asset', on_delete=models.CASCADE)
sub_asset_type = models.SmallIntegerField(choices=sub_asset_type_choice, default=0, verbose_name="安全設備類型")
model = models.CharField(max_length=128, default='未知型號', verbose_name='安全設備型號')
def __str__(self):
return self.asset.name + "--" + self.get_sub_asset_type_display() + str(self.model) + " id:%s" % self.id
class Meta:
verbose_name = '安全設備'
verbose_name_plural = "安全設備"
class StorageDevice(models.Model):
"""存儲設備"""
sub_asset_type_choice = (
(0, '磁盤陣列'),
(1, '網絡存儲器'),
(2, '磁帶庫'),
(4, '磁帶機'),
)
asset = models.OneToOneField('Asset', on_delete=models.CASCADE)
sub_asset_type = models.SmallIntegerField(choices=sub_asset_type_choice, default=0, verbose_name="存儲設備類型")
model = models.CharField(max_length=128, default='未知型號', verbose_name='存儲設備型號')
def __str__(self):
return self.asset.name + "--" + self.get_sub_asset_type_display() + str(self.model) + " id:%s" % self.id
class Meta:
verbose_name = '存儲設備'
verbose_name_plural = "存儲設備"
class NetworkDevice(models.Model):
"""網絡設備"""
sub_asset_type_choice = (
(0, '路由器'),
(1, '交換機'),
(2, '負載均衡'),
(4, 'VPN設備'),
)
asset = models.OneToOneField('Asset', on_delete=models.CASCADE)
sub_asset_type = models.SmallIntegerField(choices=sub_asset_type_choice, default=0, verbose_name="網絡設備類型")
vlan_ip = models.GenericIPAddressField(blank=True, null=True, verbose_name="VLanIP")
intranet_ip = models.GenericIPAddressField(blank=True, null=True, verbose_name="內網IP")
model = models.CharField(max_length=128, default='未知型號', verbose_name="網絡設備型號")
firmware = models.CharField(max_length=128, blank=True, null=True, verbose_name="設備固件版本")
port_num = models.SmallIntegerField(null=True, blank=True, verbose_name="端口個數")
device_detail = models.TextField(null=True, blank=True, verbose_name="詳細配置")
def __str__(self):
return '%s--%s--%s <sn:%s>' % (self.asset.name, self.get_sub_asset_type_display(), self.model, self.asset.sn)
class Meta:
verbose_name = '網絡設備'
verbose_name_plural = "網絡設備"
class Software(models.Model):
"""
只保存付費購買的軟件
"""
sub_asset_type_choice = (
(0, '操作系統'),
(1, '辦公\開發軟件'),
(2, '業務軟件'),
)
sub_asset_type = models.SmallIntegerField(choices=sub_asset_type_choice, default=0, verbose_name="軟件類型")
license_num = models.IntegerField(default=1, verbose_name="授權數量")
version = models.CharField(max_length=64, unique=True, help_text='例如: RedHat release 7 (Final)',
verbose_name='軟件/系統版本')
def __str__(self):
return '%s--%s' % (self.get_sub_asset_type_display(), self.version)
class Meta:
verbose_name = '軟件/系統'
verbose_name_plural = "軟件/系統"
```
說明:
* 每臺安全、網絡、存儲設備都通過一對一的方式唯一關聯這一個資產對象。
* 通過sub\_asset\_type又細分設備的子類型
* 對于軟件,它沒有物理形體,因此無須關聯一個資產對象;
* 軟件只管理那些大型的收費軟件,關注點是授權數量和軟件版本。對于那些開源的或者免費的軟件,顯然不算公司的資產。
<br />
### **4.機房、制造商、業務線、合同、資產標簽等數據模型**
這一部分是CMDB中相關的內容,數據表建立后,可以通過手動添加。
~~~
class IDC(models.Model):
"""機房"""
name = models.CharField(max_length=64, unique=True, verbose_name="機房名稱")
memo = models.CharField(max_length=128, blank=True, null=True, verbose_name='備注')
def __str__(self):
return self.name
class Meta:
verbose_name = '機房'
verbose_name_plural = "機房"
class Manufacturer(models.Model):
"""廠商"""
name = models.CharField('廠商名稱', max_length=64, unique=True)
telephone = models.CharField('支持電話', max_length=30, blank=True, null=True)
memo = models.CharField('備注', max_length=128, blank=True, null=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '廠商'
verbose_name_plural = "廠商"
class BusinessUnit(models.Model):
"""業務線"""
parent_unit = models.ForeignKey('self', blank=True, null=True, related_name='parent_level', on_delete=models.SET_NULL)
name = models.CharField('業務線', max_length=64, unique=True)
memo = models.CharField('備注', max_length=64, blank=True, null=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '業務線'
verbose_name_plural = "業務線"
class Contract(models.Model):
"""合同"""
sn = models.CharField('合同號', max_length=128, unique=True)
name = models.CharField('合同名稱', max_length=64)
memo = models.TextField('備注', blank=True, null=True)
price = models.IntegerField('合同金額')
detail = models.TextField('合同詳細', blank=True, null=True)
start_day = models.DateField('開始日期', blank=True, null=True)
end_day = models.DateField('失效日期', blank=True, null=True)
license_num = models.IntegerField('license數量', blank=True, null=True)
c_day = models.DateField('創建日期', auto_now_add=True)
m_day = models.DateField('修改日期', auto_now=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '合同'
verbose_name_plural = "合同"
class Tag(models.Model):
"""標簽"""
name = models.CharField('標簽名', max_length=32, unique=True)
c_day = models.DateField('創建日期', auto_now_add=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '標簽'
verbose_name_plural = "標簽"
~~~
說明:
* 機房可以有很多其它字段,比如城市、樓號、樓層和未知等等,如有需要可自行添加;
* 業務線可以有子業務線,因此使用一個外鍵關聯自身模型;
* 合同模型主要存儲財務部門關心的數據;
* 資產標簽模型與資產是多對多的關系。
<br />
### **5.CPU模型**
通常一臺服務器中只能有一種CPU型號,所以這里使用OneToOneField唯一關聯一個資產對象,而不是外鍵關系。服務器上可以有多個物理CPU,它們的型號都是一樣的。每個物理CPU又可能包含多核。
~~~
class CPU(models.Model):
"""CPU組件"""
asset = models.OneToOneField('Asset', on_delete=models.CASCADE) # 設備上的cpu肯定都是一樣的,所以不需要建立多個cpu數據,一條就可以,因此使用一對一。
cpu_model = models.CharField('CPU型號', max_length=128, blank=True, null=True)
cpu_count = models.PositiveSmallIntegerField('物理CPU個數', default=1)
cpu_core_count = models.PositiveSmallIntegerField('CPU核數', default=1)
def __str__(self):
return self.asset.name + ": " + self.cpu_model
class Meta:
verbose_name = 'CPU'
verbose_name_plural = "CPU"
~~~
<br />
### **6.RAM模型**
某個資產中可能有多條內存,所以這里必須是外鍵關系。其次,內存的sn號可能無法獲得,就必須通過內存所在的插槽未知來唯一確定每條內存。因此,`unique_together = ('asset', 'slot')`這條設置非常關鍵,相當于內存的主鍵了,每條內存數據必須包含slot字段,否則就不合法。
~~~
class RAM(models.Model):
"""內存組件"""
asset = models.ForeignKey('Asset', on_delete=models.CASCADE)
sn = models.CharField('SN號', max_length=128, blank=True, null=True)
model = models.CharField('內存型號', max_length=128, blank=True, null=True)
manufacturer = models.CharField('內存制造商', max_length=128, blank=True, null=True)
slot = models.CharField('插槽', max_length=64)
capacity = models.IntegerField('內存大小(GB)', blank=True, null=True)
def __str__(self):
return '%s: %s: %s: %s' % (self.asset.name, self.model, self.slot, self.capacity)
class Meta:
verbose_name = '內存'
verbose_name_plural = "內存"
unique_together = ('asset', 'slot') # 同一資產下的內存,根據插槽的不同,必須唯一
~~~
<br />
### **7. 硬盤模型**
與內存相同的是,硬盤也可能有很多塊,所以也是外鍵關系。不同的是,硬盤通常都能獲取到sn號,使用sn作為唯一值比較合適,也就是`unique_together = ('asset', 'sn')`。硬盤有不同的接口,這里設置了4種以及unknown,可自行添加其它類別。
~~~
class Disk(models.Model):
"""硬盤設備"""
disk_interface_type_choice = (
('SATA', 'SATA'),
('SAS', 'SAS'),
('SCSI', 'SCSI'),
('SSD', 'SSD'),
('unknown', 'unknown'),
)
asset = models.ForeignKey('Asset', on_delete=models.CASCADE)
sn = models.CharField('硬盤SN號', max_length=128)
slot = models.CharField('所在插槽位', max_length=64, blank=True, null=True)
model = models.CharField('磁盤型號', max_length=128, blank=True, null=True)
manufacturer = models.CharField('磁盤制造商', max_length=128, blank=True, null=True)
capacity = models.FloatField('磁盤容量(GB)', blank=True, null=True)
interface_type = models.CharField('接口類型', max_length=16, choices=disk_interface_type_choice, default='unknown')
def __str__(self):
return '%s: %s: %s: %sGB' % (self.asset.name, self.model, self.slot, self.capacity)
class Meta:
verbose_name = '硬盤'
verbose_name_plural = "硬盤"
unique_together = ('asset', 'sn')
~~~
<br />
### **8.網卡模型**
一臺設備中可能有很多塊網卡,所以網卡與資產也是外鍵的關系。另外,由于虛擬機的存在,網卡的mac地址可能會發生重復,無法唯一確定某塊網卡,因此通過網卡型號加mac地址的方式來唯一確定網卡。
~~~
class NIC(models.Model):
"""網卡組件"""
asset = models.ForeignKey('Asset', on_delete=models.CASCADE) # 注意要用外鍵
name = models.CharField('網卡名稱', max_length=64, blank=True, null=True)
model = models.CharField('網卡型號', max_length=128)
mac = models.CharField('MAC地址', max_length=64) # 虛擬機有可能會出現同樣的mac地址
ip_address = models.GenericIPAddressField('IP地址', blank=True, null=True)
net_mask = models.CharField('掩碼', max_length=64, blank=True, null=True)
bonding = models.CharField('綁定地址', max_length=64, blank=True, null=True)
def __str__(self):
return '%s: %s: %s' % (self.asset.name, self.model, self.mac)
class Meta:
verbose_name = '網卡'
verbose_name_plural = "網卡"
unique_together = ('asset', 'model', 'mac') # 資產、型號和mac必須聯合唯一。防止虛擬機中的特殊情況發生錯誤。
~~~
<br />
### **9. 其它模型**
比如機房、廠商、標簽、業務線、合同等其它信息。
~~~
class IDC(models.Model):
"""機房"""
name = models.CharField(max_length=64, unique=True, verbose_name="機房名稱")
memo = models.CharField(max_length=128, blank=True, null=True, verbose_name='備注')
def __str__(self):
return self.name
class Meta:
verbose_name = '機房'
verbose_name_plural = "機房"
class Manufacturer(models.Model):
"""廠商"""
name = models.CharField('廠商名稱', max_length=64, unique=True)
telephone = models.CharField('支持電話', max_length=30, blank=True, null=True)
memo = models.CharField('備注', max_length=128, blank=True, null=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '廠商'
verbose_name_plural = "廠商"
class BusinessUnit(models.Model):
"""業務線"""
parent_unit = models.ForeignKey('self', blank=True, null=True, related_name='parent_level',
on_delete=models.CASCADE)
name = models.CharField('業務線名稱', max_length=64, unique=True)
memo = models.CharField('備注', max_length=64, blank=True, null=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '業務線'
verbose_name_plural = "業務線"
class Contract(models.Model):
"""合同"""
sn = models.CharField('合同號', max_length=128, unique=True)
name = models.CharField('合同名稱', max_length=64)
memo = models.TextField('備注', blank=True, null=True)
price = models.IntegerField('合同金額')
detail = models.TextField('合同詳細', blank=True, null=True)
start_day = models.DateField('開始日期', blank=True, null=True)
end_day = models.DateField('失效日期', blank=True, null=True)
license_num = models.IntegerField('license數量', blank=True, null=True)
c_day = models.DateField('創建日期', auto_now_add=True)
m_day = models.DateField('修改日期', auto_now=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '合同'
verbose_name_plural = "合同"
class Tag(models.Model):
"""標簽"""
name = models.CharField('標簽名', max_length=32, unique=True)
c_day = models.DateField('創建日期', auto_now_add=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '標簽'
verbose_name_plural = "標簽"
~~~
<br />
### **10.日志模型**
CMDB必須記錄各種日志,這是毫無疑問的!我們通常要記錄事件名稱、類型、關聯的資產、子事件、事件詳情、誰導致的、發生時間。這些都很重要!
尤其要注意的是,事件日志不能隨著關聯資產的刪除被一并刪除,也就是我們設置`on_delete=models.SET_NULL`的意義!
~~~
class EventLog(models.Model):
"""
日志.
在關聯對象被刪除的時候,不能一并刪除,需保留日志。
因此,on_delete=models.SET_NULL
"""
name = models.CharField('事件名稱', max_length=128)
event_type_choice = (
(0, '其它'),
(1, '硬件變更'),
(2, '新增配件'),
(3, '設備下線'),
(4, '設備上線'),
(5, '定期維護'),
(6, '業務上線\更新\變更'),
)
asset = models.ForeignKey('Asset', blank=True, null=True, on_delete=models.SET_NULL) # 當資產審批成功時有這項數據
new_asset = models.ForeignKey('NewAssetApprovalZone', blank=True, null=True, on_delete=models.SET_NULL) # 當資產審批失敗時有這項數據
event_type = models.SmallIntegerField('事件類型', choices=event_type_choice, default=4)
component = models.CharField('事件子項', max_length=256, blank=True, null=True)
detail = models.TextField('事件詳情')
date = models.DateTimeField('事件時間', auto_now_add=True)
user = models.ForeignKey(User, blank=True, null=True, verbose_name='事件執行人', on_delete=models.SET_NULL) # 自動更新資產數據時沒有執行人
memo = models.TextField('備注', blank=True, null=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '事件紀錄'
verbose_name_plural = "事件紀錄"
~~~
<br />
### **11.新資產待審批區模型**
新資產的到來,并不能直接加入CMDB數據庫中,而是要通過管理員審批后,才可以上線的。這就需要一個新資產的待審批區。在該區中,以資產的sn號作為唯一值,確定不同的資產。除了關鍵的包含資產所有信息的data字段,為了方便審批員查看信息,我們還設計了一些廠商、型號、內存大小、CPU類型等字段。同時,有可能出現資產還未審批,更新數據就已經發過來的情況,所以需要一個數據更新日期字段。
~~~
class NewAssetApprovalZone(models.Model):
"""新資產待審批區"""
sn = models.CharField('資產SN號', max_length=128, unique=True) # 此字段必填
asset_type_choice = (
('server', '服務器'),
('networkdevice', '網絡設備'),
('storagedevice', '存儲設備'),
('securitydevice', '安全設備'),
('software', '軟件資產'),
)
asset_type = models.CharField(choices=asset_type_choice, default='server', max_length=64, blank=True, null=True,
verbose_name='資產類型')
manufacturer = models.CharField(max_length=64, blank=True, null=True, verbose_name='生產廠商')
model = models.CharField(max_length=128, blank=True, null=True, verbose_name='型號')
ram_size = models.PositiveIntegerField(blank=True, null=True, verbose_name='內存大小')
cpu_model = models.CharField(max_length=128, blank=True, null=True, verbose_name='CPU型號')
cpu_count = models.PositiveSmallIntegerField('CPU物理數量', blank=True, null=True)
cpu_core_count = models.PositiveSmallIntegerField('CPU核心數量', blank=True, null=True)
os_distribution = models.CharField('發行商', max_length=64, blank=True, null=True)
os_type = models.CharField('系統類型', max_length=64, blank=True, null=True)
os_release = models.CharField('操作系統版本號', max_length=64, blank=True, null=True)
data = models.TextField('資產數據') # 此字段必填
c_time = models.DateTimeField('匯報日期', auto_now_add=True)
m_time = models.DateTimeField('數據更新日期', auto_now=True)
approved = models.BooleanField('是否批準', default=False)
def __str__(self):
return self.sn
class Meta:
verbose_name = '新上線待批準資產'
verbose_name_plural = "新上線待批準資產"
ordering = ['-c_time']
~~~
<br />
### **11.總結**
通過前面的內容,我們可以看出CMDB數據模型的設計非常復雜,我們這里還是省略了很多不太重要的部分,就這樣總共都有400多行代碼。其中每個模型需要保存什么字段、采用什么類型、什么關聯關系、定義哪些參數、數據是否可以為空,這些都是踩過各種坑后總結出來的,不是隨便就能定義的。所以,請務必詳細閱讀和揣摩這些模型的內容。
<br />
一切沒有問題之后,注冊app,然后makemigrations以及migrate!
最后附上整個models.py文件的代碼:
<br />
<details>
<summary>models.py</summary>
```
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Asset(models.Model):
""" 所有資產的共有數據表 """
asset_type_choice = (
('server', '服務器'),
('networkdevice', '網絡設備'),
('storagedevice', '存儲設備'),
('securitydevice', '安全設備'),
('software', '軟件資產'),
)
asset_status = (
(0, '在線'),
(1, '下線'),
(2, '未知'),
(3, '故障'),
(4, '備用'),
)
asset_type = models.CharField(choices=asset_type_choice, max_length=64, default='server', verbose_name="資產類型")
name = models.CharField(max_length=64, unique=True, verbose_name="資產名稱") # 不可重復
sn = models.CharField(max_length=128, unique=True, verbose_name="資產序列號") # 不可重復
business_unit = models.ForeignKey('BusinessUnit', null=True, blank=True, verbose_name='所屬業務線',
on_delete=models.SET_NULL)
status = models.SmallIntegerField(choices=asset_status, default=0, verbose_name='設備狀態')
manufacturer = models.ForeignKey('Manufacturer', null=True, blank=True, verbose_name='制造商',
on_delete=models.SET_NULL)
manage_ip = models.GenericIPAddressField(null=True, blank=True, verbose_name='管理IP')
tags = models.ManyToManyField('Tag', blank=True, verbose_name='標簽')
admin = models.ForeignKey(User, null=True, blank=True, verbose_name='資產管理員', related_name='admin',
on_delete=models.SET_NULL)
idc = models.ForeignKey('IDC', null=True, blank=True, verbose_name='所在機房', on_delete=models.SET_NULL)
contract = models.ForeignKey('Contract', null=True, blank=True, verbose_name='合同', on_delete=models.SET_NULL)
purchase_day = models.DateField(null=True, blank=True, verbose_name="購買日期")
expire_day = models.DateField(null=True, blank=True, verbose_name="過保日期")
price = models.FloatField(null=True, blank=True, verbose_name="價格")
approved_by = models.ForeignKey(User, null=True, blank=True, verbose_name='批準人', related_name='approved_by',
on_delete=models.SET_NULL)
memo = models.TextField(null=True, blank=True, verbose_name='備注')
c_time = models.DateTimeField(auto_now_add=True, verbose_name='批準日期')
m_time = models.DateTimeField(auto_now=True, verbose_name='更新日期')
def __str__(self):
return '<%s> %s' % (self.get_asset_type_display(), self.name)
class Meta:
verbose_name = '資產總表'
verbose_name_plural = "資產總表"
ordering = ['-c_time']
class Server(models.Model):
"""服務器設備"""
sub_asset_type_choice = (
(0, 'PC服務器'),
(1, '刀片機'),
(2, '小型機'),
)
created_by_choice = (
('auto', '自動添加'),
('manual', '手工錄入'),
)
asset = models.OneToOneField('Asset', on_delete=models.CASCADE) # 非常關鍵的一對一關聯!asset被刪除的時候一并刪除server
sub_asset_type = models.SmallIntegerField(choices=sub_asset_type_choice, default=0, verbose_name="服務器類型")
created_by = models.CharField(choices=created_by_choice, max_length=32, default='auto', verbose_name="添加方式")
hosted_on = models.ForeignKey('self', related_name='hosted_on_server',
blank=True, null=True, verbose_name="宿主機", on_delete=models.CASCADE) # 虛擬機專用字段
model = models.CharField(max_length=128, null=True, blank=True, verbose_name='服務器型號')
raid_type = models.CharField(max_length=512, blank=True, null=True, verbose_name='Raid類型')
os_type = models.CharField('操作系統類型', max_length=64, blank=True, null=True)
os_distribution = models.CharField('發行商', max_length=64, blank=True, null=True)
os_release = models.CharField('操作系統版本', max_length=64, blank=True, null=True)
def __str__(self):
return '%s--%s--%s <sn:%s>' % (self.asset.name, self.get_sub_asset_type_display(), self.model, self.asset.sn)
class Meta:
verbose_name = '服務器'
verbose_name_plural = "服務器"
class SecurityDevice(models.Model):
"""安全設備"""
sub_asset_type_choice = (
(0, '防火墻'),
(1, '入侵檢測設備'),
(2, '互聯網網關'),
(4, '運維審計系統'),
)
asset = models.OneToOneField('Asset', on_delete=models.CASCADE)
sub_asset_type = models.SmallIntegerField(choices=sub_asset_type_choice, default=0, verbose_name="安全設備類型")
model = models.CharField(max_length=128, default='未知型號', verbose_name='安全設備型號')
def __str__(self):
return self.asset.name + "--" + self.get_sub_asset_type_display() + str(self.model) + " id:%s" % self.id
class Meta:
verbose_name = '安全設備'
verbose_name_plural = "安全設備"
class StorageDevice(models.Model):
"""存儲設備"""
sub_asset_type_choice = (
(0, '磁盤陣列'),
(1, '網絡存儲器'),
(2, '磁帶庫'),
(4, '磁帶機'),
)
asset = models.OneToOneField('Asset', on_delete=models.CASCADE)
sub_asset_type = models.SmallIntegerField(choices=sub_asset_type_choice, default=0, verbose_name="存儲設備類型")
model = models.CharField(max_length=128, default='未知型號', verbose_name='存儲設備型號')
def __str__(self):
return self.asset.name + "--" + self.get_sub_asset_type_display() + str(self.model) + " id:%s" % self.id
class Meta:
verbose_name = '存儲設備'
verbose_name_plural = "存儲設備"
class NetworkDevice(models.Model):
"""網絡設備"""
sub_asset_type_choice = (
(0, '路由器'),
(1, '交換機'),
(2, '負載均衡'),
(4, 'VPN設備'),
)
asset = models.OneToOneField('Asset', on_delete=models.CASCADE)
sub_asset_type = models.SmallIntegerField(choices=sub_asset_type_choice, default=0, verbose_name="網絡設備類型")
vlan_ip = models.GenericIPAddressField(blank=True, null=True, verbose_name="VLanIP")
intranet_ip = models.GenericIPAddressField(blank=True, null=True, verbose_name="內網IP")
model = models.CharField(max_length=128, default='未知型號', verbose_name="網絡設備型號")
firmware = models.CharField(max_length=128, blank=True, null=True, verbose_name="設備固件版本")
port_num = models.SmallIntegerField(null=True, blank=True, verbose_name="端口個數")
device_detail = models.TextField(null=True, blank=True, verbose_name="詳細配置")
def __str__(self):
return '%s--%s--%s <sn:%s>' % (self.asset.name, self.get_sub_asset_type_display(), self.model, self.asset.sn)
class Meta:
verbose_name = '網絡設備'
verbose_name_plural = "網絡設備"
class Software(models.Model):
"""
只保存付費購買的軟件
"""
sub_asset_type_choice = (
(0, '操作系統'),
(1, '辦公\開發軟件'),
(2, '業務軟件'),
)
sub_asset_type = models.SmallIntegerField(choices=sub_asset_type_choice, default=0, verbose_name="軟件類型")
license_num = models.IntegerField(default=1, verbose_name="授權數量")
version = models.CharField(max_length=64, unique=True, help_text='例如: RedHat release 7 (Final)',
verbose_name='軟件/系統版本')
def __str__(self):
return '%s--%s' % (self.get_sub_asset_type_display(), self.version)
class Meta:
verbose_name = '軟件/系統'
verbose_name_plural = "軟件/系統"
class CPU(models.Model):
"""CPU組件"""
asset = models.OneToOneField('Asset', on_delete=models.CASCADE) # 設備上的cpu肯定都是一樣的,所以不需要建立多個cpu數據,一條就可以,因此使用一對一。
cpu_model = models.CharField('CPU型號', max_length=128, blank=True, null=True)
cpu_count = models.PositiveSmallIntegerField('物理CPU個數', default=1)
cpu_core_count = models.PositiveSmallIntegerField('CPU核數', default=1)
def __str__(self):
return self.asset.name + ": " + self.cpu_model
class Meta:
verbose_name = 'CPU'
verbose_name_plural = "CPU"
class RAM(models.Model):
"""內存組件"""
asset = models.ForeignKey('Asset', on_delete=models.CASCADE)
sn = models.CharField('SN號', max_length=128, blank=True, null=True)
model = models.CharField('內存型號', max_length=128, blank=True, null=True)
manufacturer = models.CharField('內存制造商', max_length=128, blank=True, null=True)
slot = models.CharField('插槽', max_length=64)
capacity = models.IntegerField('內存大小(GB)', blank=True, null=True)
def __str__(self):
return '%s: %s: %s: %s' % (self.asset.name, self.model, self.slot, self.capacity)
class Meta:
verbose_name = '內存'
verbose_name_plural = "內存"
unique_together = ('asset', 'slot') # 同一資產下的內存,根據插槽的不同,必須唯一
class Disk(models.Model):
"""硬盤設備"""
disk_interface_type_choice = (
('SATA', 'SATA'),
('SAS', 'SAS'),
('SCSI', 'SCSI'),
('SSD', 'SSD'),
('unknown', 'unknown'),
)
asset = models.ForeignKey('Asset', on_delete=models.CASCADE)
sn = models.CharField('硬盤SN號', max_length=128)
slot = models.CharField('所在插槽位', max_length=64, blank=True, null=True)
model = models.CharField('磁盤型號', max_length=128, blank=True, null=True)
manufacturer = models.CharField('磁盤制造商', max_length=128, blank=True, null=True)
capacity = models.FloatField('磁盤容量(GB)', blank=True, null=True)
interface_type = models.CharField('接口類型', max_length=16, choices=disk_interface_type_choice, default='unknown')
def __str__(self):
return '%s: %s: %s: %sGB' % (self.asset.name, self.model, self.slot, self.capacity)
class Meta:
verbose_name = '硬盤'
verbose_name_plural = "硬盤"
unique_together = ('asset', 'sn')
class NIC(models.Model):
"""網卡組件"""
asset = models.ForeignKey('Asset', on_delete=models.CASCADE) # 注意要用外鍵
name = models.CharField('網卡名稱', max_length=64, blank=True, null=True)
model = models.CharField('網卡型號', max_length=128)
mac = models.CharField('MAC地址', max_length=64) # 虛擬機有可能會出現同樣的mac地址
ip_address = models.GenericIPAddressField('IP地址', blank=True, null=True)
net_mask = models.CharField('掩碼', max_length=64, blank=True, null=True)
bonding = models.CharField('綁定地址', max_length=64, blank=True, null=True)
def __str__(self):
return '%s: %s: %s' % (self.asset.name, self.model, self.mac)
class Meta:
verbose_name = '網卡'
verbose_name_plural = "網卡"
unique_together = ('asset', 'model', 'mac') # 資產、型號和mac必須聯合唯一。防止虛擬機中的特殊情況發生錯誤。
class IDC(models.Model):
"""機房"""
name = models.CharField(max_length=64, unique=True, verbose_name="機房名稱")
memo = models.CharField(max_length=128, blank=True, null=True, verbose_name='備注')
def __str__(self):
return self.name
class Meta:
verbose_name = '機房'
verbose_name_plural = "機房"
class Manufacturer(models.Model):
"""廠商"""
name = models.CharField('廠商名稱', max_length=64, unique=True)
telephone = models.CharField('支持電話', max_length=30, blank=True, null=True)
memo = models.CharField('備注', max_length=128, blank=True, null=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '廠商'
verbose_name_plural = "廠商"
class BusinessUnit(models.Model):
"""業務線"""
parent_unit = models.ForeignKey('self', blank=True, null=True, related_name='parent_level',
on_delete=models.CASCADE)
name = models.CharField('業務線名稱', max_length=64, unique=True)
memo = models.CharField('備注', max_length=64, blank=True, null=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '業務線'
verbose_name_plural = "業務線"
class Contract(models.Model):
"""合同"""
sn = models.CharField('合同號', max_length=128, unique=True)
name = models.CharField('合同名稱', max_length=64)
memo = models.TextField('備注', blank=True, null=True)
price = models.IntegerField('合同金額')
detail = models.TextField('合同詳細', blank=True, null=True)
start_day = models.DateField('開始日期', blank=True, null=True)
end_day = models.DateField('失效日期', blank=True, null=True)
license_num = models.IntegerField('license數量', blank=True, null=True)
c_day = models.DateField('創建日期', auto_now_add=True)
m_day = models.DateField('修改日期', auto_now=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '合同'
verbose_name_plural = "合同"
class Tag(models.Model):
"""標簽"""
name = models.CharField('標簽名', max_length=32, unique=True)
c_day = models.DateField('創建日期', auto_now_add=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '標簽'
verbose_name_plural = "標簽"
class EventLog(models.Model):
"""
日志.
在關聯對象被刪除的時候,不能一并刪除,需保留日志。
因此,on_delete=models.SET_NULL
"""
name = models.CharField('事件名稱', max_length=128)
event_type_choice = (
(0, '其它'),
(1, '硬件變更'),
(2, '新增配件'),
(3, '設備下線'),
(4, '設備上線'),
(5, '定期維護'),
(6, '業務上線\更新\變更'),
)
asset = models.ForeignKey('Asset', blank=True, null=True, on_delete=models.SET_NULL) # 當資產審批成功時有這項數據
new_asset = models.ForeignKey('NewAssetApprovalZone', blank=True, null=True, on_delete=models.SET_NULL) # 當資產審批失敗時有這項數據
event_type = models.SmallIntegerField('事件類型', choices=event_type_choice, default=4)
component = models.CharField('事件子項', max_length=256, blank=True, null=True)
detail = models.TextField('事件詳情')
date = models.DateTimeField('事件時間', auto_now_add=True)
user = models.ForeignKey(User, blank=True, null=True, verbose_name='事件執行人', on_delete=models.SET_NULL) # 自動更新資產數據時沒有執行人
memo = models.TextField('備注', blank=True, null=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '事件紀錄'
verbose_name_plural = "事件紀錄"
class NewAssetApprovalZone(models.Model):
"""新資產待審批區"""
sn = models.CharField('資產SN號', max_length=128, unique=True) # 此字段必填
asset_type_choice = (
('server', '服務器'),
('networkdevice', '網絡設備'),
('storagedevice', '存儲設備'),
('securitydevice', '安全設備'),
('software', '軟件資產'),
)
asset_type = models.CharField(choices=asset_type_choice, default='server', max_length=64, blank=True, null=True,
verbose_name='資產類型')
manufacturer = models.CharField(max_length=64, blank=True, null=True, verbose_name='生產廠商')
model = models.CharField(max_length=128, blank=True, null=True, verbose_name='型號')
ram_size = models.PositiveIntegerField(blank=True, null=True, verbose_name='內存大小')
cpu_model = models.CharField(max_length=128, blank=True, null=True, verbose_name='CPU型號')
cpu_count = models.PositiveSmallIntegerField('CPU物理數量', blank=True, null=True)
cpu_core_count = models.PositiveSmallIntegerField('CPU核心數量', blank=True, null=True)
os_distribution = models.CharField('發行商', max_length=64, blank=True, null=True)
os_type = models.CharField('系統類型', max_length=64, blank=True, null=True)
os_release = models.CharField('操作系統版本號', max_length=64, blank=True, null=True)
data = models.TextField('資產數據') # 此字段必填
c_time = models.DateTimeField('匯報日期', auto_now_add=True)
m_time = models.DateTimeField('數據更新日期', auto_now=True)
approved = models.BooleanField('是否批準', default=False)
def __str__(self):
return self.sn
class Meta:
verbose_name = '新上線待批準資產'
verbose_name_plural = "新上線待批準資產"
ordering = ['-c_time']
```
</details>
- Linux
- Linux 文件權限概念
- 重點總結
- Linux 文件與目錄管理
- 2.1 文件與目錄管理
- 2.2 文件內容查閱
- 文件與文件系統的壓縮,打包與備份
- 3.1 Linux 系統常見的壓縮指令
- 3.2 打包指令: tar
- vi/vim 程序編輯器
- 4.1 vi 的使用
- 4.2 vim編輯器刪除一行或者多行內容
- 進程管理
- 5.1 常用命令使用技巧
- 5.2 進程管理
- 系統服務 (daemons)
- 6.1 通過 systemctl 管理服務
- Linux 系統目錄結構
- Linux yum命令
- linux系統查看、修改、更新系統時間(自動同步網絡時間)
- top linux下的任務管理器
- Linux基本配置
- CentOS7開啟防火墻
- CentOS 使用yum安裝 pip
- strace 命令
- Linux下設置固定IP地址
- 查看Linux磁盤及內存占用情況
- Mysql
- 關系數據庫概述
- 數據庫技術
- 數據庫基礎語句
- 查詢語句(--重點--)
- 約束
- 嵌套查詢(子查詢)
- 表emp
- MySQL數據庫練習
- 01.MySQL數據庫練習數據
- 02.MySQL數據庫練習題目
- 03.MySQL數據庫練習-答案
- Mysql遠程連接數據庫
- Python
- python基礎
- Python3中字符串、列表、數組的轉換方法
- python字符串
- python安裝、pip基本用法、變量、輸入輸出、流程控制、循環
- 運算符及優先級、數據類型及常用操作、深淺拷貝
- 虛擬環境(virtualenv)
- 網絡編程
- TCP/IP簡介
- TCP編程
- UDP編程
- 進程和線程
- 訪問數據庫
- 使用SQLite
- 使用MySQL
- Web開發
- HTML簡介
- Python之日志處理(logging模塊)
- 函數式編程
- 高階函數
- python報錯解決
- 啟動Python時報“ImportError: No module named site”錯誤
- python實例
- 01- 用python解決數學題
- 02- 冒泡排序
- 03- 郵件發送(smtplib)
- Django
- 01 Web應用
- Django3.2 教程
- Django簡介
- Django環境安裝
- 第一個Django應用
- Part 1:請求與響應
- Part 2:模型與后臺
- Part 3:視圖和模板
- Part 4:表單和類視圖
- Part 5:測試
- Part 6:靜態文件
- Part 7:自定義admin
- 第一章:模型層
- 實戰一:基于Django3.2可重用登錄與注冊系統
- 1. 搭建項目環境
- 2. 設計數據模型
- 3. admin后臺
- 4. url路由和視圖
- 5. 前端頁面設計
- 6. 登錄視圖
- 7. Django表單
- 8. 圖片驗證碼
- 9. session會話
- 10. 注冊視圖
- 實戰二:Django3.2之CMDB資產管理系統
- 1.項目需求分析
- 2.模型設計
- 3.數據收集客戶端
- 4.收集Windows數據
- 5.Linux下收集數據
- 6.新資產待審批區
- 7.審批新資產
- django 快速搭建blog
- imooc-Django全棧項目開發實戰
- redis
- 1.1 Redis簡介
- 1.2 安裝
- 1.3 配置
- 1.4 服務端和客戶端命令
- 1.5 Redis命令
- 1.5.1 Redis命令
- 1.5.2 鍵(Key)
- 1.5.3 字符串(string)
- 1.5.4 哈希(Hash)
- 1.5.5 列表(list)
- 1.5.6 集合(set)
- 1.5.7 有序集合(sorted set)
- Windows
- Win10安裝Ubuntu子系統
- win10遠程桌面身份驗證錯誤,要求的函數不受支持
- hm軟件測試
- 02 linux基本命令
- Linux終端命令格式
- Linux基本命令(一)
- Linux基本命令(二)
- 02 數據庫
- 數據庫簡介
- 基本概念
- Navicat使用
- SQL語言
- 高級
- 03 深入了解軟件測試
- day01
- 04 python基礎
- 語言基礎
- 程序中的變量
- 程序的輸出
- 程序中的運算符
- 數據類型基礎
- 數據序列
- 數據類型分類
- 字符串
- 列表
- 元組
- 字典
- 列表與元組的區別詳解
- 函數
- 案例綜合應用
- 列表推導式
- 名片管理系統
- 文件操作
- 面向對象基礎(一)
- 面向對象基礎(二)
- 異常、模塊
- 05 web自動化測試
- Day01
- Day02
- Day03
- Day04
- Day05
- Day06
- Day07
- Day08
- 06 接口自動化測試
- 軟件測試面試大全2020
- 第一章 測試理論
- 軟件測試面試
- 一、軟件基礎知識
- 二、網絡基礎知識
- 三、數據庫
- SQL學生表 — 1
- SQL學生表 — 2
- SQL查詢 — 3
- SQL經典面試題 — 4
- 四、linux
- a. linux常用命令
- 五、自動化測試
- 自動化測試
- python 筆試題
- selenium面試題
- 如何判斷一個頁面上元素是否存在?
- 如何提高腳本的穩定性?
- 如何定位動態元素?
- 如何通過子元素定位父元素?
- 如果截取某一個元素的圖片,不要截取全部圖片
- 平常遇到過哪些問題?如何解決的
- 一個元素明明定位到了,點擊無效(也沒報錯),如果解決?
- selenium中隱藏元素如何定位?(hidden、display: none)
- 六、接口測試
- 接口測試常規面試題
- 接口自動化面試題
- json和字典dict的區別?
- 測試的數據你放在哪?
- 什么是數據驅動,如何參數化?
- 下個接口請求參數依賴上個接口的返回數據
- 依賴于登錄的接口如何處理?
- 依賴第三方的接口如何處理
- 不可逆的操作,如何處理,比如刪除一個訂單這種接口如何測試
- 接口產生的垃圾數據如何清理
- 一個訂單的幾種狀態如何全部測到,如:未處理,處理中,處理失敗,處理成功
- python如何連接數據庫操作?
- 七、App測試
- 什么是activity?
- Activity生命周期?
- Android四大組件
- app測試和web測試有什么區別?
- android和ios測試區別?
- app出現ANR,是什么原因導致的?
- App出現crash原因有哪些?
- app對于不穩定偶然出現anr和crash時候你是怎么處理的?
- app的日志如何抓取?
- logcat查看日志步驟
- 你平常會看日志嗎, 一般會出現哪些異常
- 抓包工具
- fiddler
- Wireshark
- 安全/滲透測試
- 安全性測試都包含哪些內容?
- 開放性思維題
- 面試題
- 字節測試面試
- 一、計算機網絡
- 二、操作系統
- 三、數據庫
- 四、數據結構與算法
- 五、Python
- 六、Linux
- 七、測試用例
- 八、智力/場景題
- 九、開放性問題
- python3_收集100+練習題(面試題)
- python3_100道題目答案
- 接口測試
- 接口測試實例_01
- python+requests接口自動化測試框架實例詳解
- 性能測試
- 性能測試流程
- 性能測試面試題
- 如何編寫性能測試場景用例
- 性能測試:TPS和QPS的區別
- jmeter
- jmeter安裝配置教程
- Jmeter性能測試 入門
- PyCharm
- 快捷工具
- 1-MeterSphere
- 一、安裝和升級
- 2- MobaXterm 教程
- 3-fiddler抓包
- 4-Xshell
- Xshell的安裝和使用
- Xshell遠程連接失敗怎么解決
- 5-Vmware
- Vmware提示以獨占方式鎖定此配置文件失敗
- Windows10徹底卸載VMWare虛擬機步驟
- VM ware無法關機,虛擬機繁忙
- VMware虛擬機下載與安裝
- 解決VM 與 Device/Credential Guard 不兼容。在禁用 Device/Credential Guard 后,可以運行 VM 的方法
- VMware虛擬機鏡像克隆與導入
- 6-WPS
- 1.WPS文檔里的批注怎么刪除
- 2.wps表格中設置圖表的坐標
- 3. wps快速繪制數學交集圖
- 7-MongoDB
- Win10安裝配置MongoDB
- Navicat 15.x for MongoDB安裝破解教程
- Apache
- apache層的賬戶權限控制,以及apache黑名單白名單過濾功能
- HTTP / HTTPS協議
- HTTP協議詳解
- 代理
- 狀態碼詳解
- HTTPS詳解
- Selenium3+python3
- (A) selenium
- selenium自動化環境搭建(Windows10)
- 火狐firebug和firepath插件安裝方法(最新)
- 元素定位工具和方法
- Selenium3+python3自動化
- 新手學習selenium路線圖---學前篇
- 1-操作瀏覽器基本方法
- 2-八種元素定位方法
- 3-CSS定位語法
- 4-登錄案例
- 5-定位一組元素find_elements
- 6-操作元素(鍵盤和鼠標事件)
- 7-多窗口、句柄(handle)
- 8-iframe
- 9-select下拉框
- 10-alert\confirm\prompt
- 11-JS處理滾動條
- 12-單選框和復選框(radiobox、checkbox)
- 13-js處理日歷控件(修改readonly屬性)
- 14-js處理內嵌div滾動條
- 15-table定位
- 16-js處理多窗口
- 17-文件上傳(send_keys)
- 18-獲取百度輸入聯想詞
- 19-處理瀏覽器彈窗
- 20-獲取元素屬性
- 21-判斷元素存在
- 22-爬頁面源碼(page_source)
- 23-顯式等待(WebDriverWait)
- 24-關于面試的題
- 25-cookie相關操作
- 26-判斷元素(expected_conditions)
- 27-判斷title(title_is)
- 28-元素定位參數化(find_element)
- 29-18種定位方法(find_elements)
- 30- js解決click失效問題
- 31- 判斷彈出框存在(alert_is_present)
- 32- 登錄方法(參數化)
- 33- 判斷文本(text_to_be_present_in_element)
- 34- unittest簡介
- 35- unittest執行順序
- 36- unittest之裝飾器(@classmethod)
- 37- unittest之斷言(assert)
- 38- 捕獲異常(NoSuchElementException)
- 39- 讀取Excel數據(xlrd)
- 40- 數據驅動(ddt)
- 41- 異常后截圖(screenshot)
- 42- jenkins持續集成環境搭建
- 43- Pycharm上python和unittest兩種運行方式
- 44- 定位的坑:class屬性有空格
- 45- 只截某個元素的圖
- 46- unittest多線程執行用例
- 47- unittest多線程生成報告(BeautifulReport)
- 48- 多線程啟動多個不同瀏覽器
- (B) python3+selenium3實現web UI功能自動化測試框架
- (C) selenium3常見報錯處理
- 書籍
- (D)Selenium3自動化測試實戰--基于Python語
- 第4章 WebDriver API
- 4.1 從定位元素開始
- 4.2 控制瀏覽器
- 4.3 WebDriver 中的常用方法
- 4.4 鼠標操作
- 4.5 鍵盤操作
- 4.6 獲得驗證信息
- 4.7 設置元素等待
- 4.8 定位一組元素
- 4.9 多表單切換
- 4.10 多窗口切換
- 4.11 警告框處理
- 4.12 下拉框處理
- 4.13 上傳文件
- 4.14 下載文件
- 4.15 操作cookie
- 4.16 調用JavaScript
- 4.17 處理HTML5視頻播放
- 4.18 滑動解鎖
- 4.19 窗口截圖
- 第5章 自動化測試模型
- 5.3 模塊化與參數化
- 5.4 讀取數據文件
- 第6章 unittest單元測試框架
- 6.1 認識unittest