CMDB 設計(二)實現host、ip儲存
接上篇博文
嘗試新增一張主機表,用於管理主機資訊
無非表的內容是host,那麼肯定是在schema中建立,直接寫描述即可
schema 填入id,id預設是1,name對應的是host
這裡虛擬表所對應的東西是1,描述的欄位還暫時沒有
如下所示:
MariaDB [cmdb]> insert into `schema`(name) values('hosts'); MariaDB [cmdb]> select * from `schema`; +----+-------+------+ | id | name | desc | +----+-------+------+ | 1 | hosts | NULL | +----+-------+------+ 1 row in set (0.00 sec)
描述欄位
通過filed表中新增兩個欄位 hostname,schema_id 這倆欄位與host與關,其實就是與id與關聯
這樣自增的話,會產生第一個索引,那如果再進行新增ip 的話,則id是2
MariaDB [cmdb]> insert into `field` (name,schema_id) value ('hostname',1); MariaDB [cmdb]> insert into `field` (name,schema_id) value ('ip',1); MariaDB [cmdb]> select * from field; +----+----------+------+-----------+ | id | name | meta | schema_id | +----+----------+------+-----------+ | 1 | hostname | NULL | 1 | | 5 | ip | NULL | 1 | +----+----------+------+-----------+ 2 rows in set (0.00 sec)
如上,這裡value對應的schema_id 都是1,也就是找到schema表中與id=1對應相關的表,也就是屬hosts所管轄
MariaDB [cmdb]> select * from `schema`; +----+-------+------+ | id | name | desc | +----+-------+------+ | 1 | hosts | NULL | +----+-------+------+
這樣建立假設自增的話,產生第一個主鍵
查詢
查出對應的虛擬表所有的欄位,只要指定schemaid就可以查出當前資訊
MariaDB [cmdb]> select * from `schema`,`field` where 1 = 1 and `field`.schema_id = `schema`.id and `schema`.id = 1; +----+-------+------+----+----------+------+-----------+ | id | name | desc | id | name | meta | schema_id | +----+-------+------+----+----------+------+-----------+ | 1 | hosts | NULL | 1 | hostname | NULL | 1 | | 1 | hosts | NULL | 5 | ip | NULL | 1 | +----+-------+------+----+----------+------+-----------+ 2 rows in set (0.00 sec)
查出它所有欄位,只要能知名schema id 相當於虛擬表已經被查出
記錄一個主機資訊,entity
MariaDB [cmdb]> insert into entity (id,`key`,schema_id) values(1,'0123123',1);
對value表填入資料
當前entity 的值是1,那麼對應entity_id = 1就可以了,欄位id是
MariaDB [cmdb]> insert into `value`(entity_id,filed_id,`value`) values(1,1,'webserver'); MariaDB [cmdb]> insert into `value`(entity_id,filed_id,`value`) values(1,2,'182.168.1.1'); MariaDB [cmdb]> select * from value; +----+-------------+----------+-----------+ | id | value | field_id | entity_id | +----+-------------+----------+-----------+ | 1 | webser | 1 | 1 | | 2 | 182.168.1.1 | 2 | 1 | +----+-------------+----------+-----------+ 2 rows in set (0.01 sec)
可以看到其field對應的表字段
MariaDB [cmdb]> select * from field; +----+----------+------+-----------+ | id | name | meta | schema_id | +----+----------+------+-----------+ | 1 | hostname | NULL | 1 | | 2 | ip | NULL | 1 | +----+----------+------+-----------+ 2 rows in set (0.00 sec)
插入第二個主機資訊:
通過唯一key來區別主機,以及還需要對entity 進行新增行進行id對應
MariaDB [cmdb]> insert into entity(`key`,schema_id) values ('0456456',1); Query OK, 1 row affected, 1 warning (0.00 sec) MariaDB [cmdb]> select * from entity; +----+---------+-----------+ | id | key | schema_id | +----+---------+-----------+ | 0 | 0456456 | 1 | | 1 | 0123123 | 1 | +----+---------+-----------+ 2 rows in set (0.00 sec)
插入資料 ,對應entity 第二個id
insert into `value`(entity_id,filed_id,`value`) VALUES (1,1,'DBser'); insert into `value`(entity_id,filed_id,`value`) VALUES (1,2,'127.0.0.1');
以上存入了兩個主機資訊
檢視
select entity.id as entity_id,entity.`key`, entity.schema_id, `schema`.`name`, field.id,field.`name` as fname, `value`.`value` FROM entity INNER JOIN `value` on `value`.entity_id = entity_id INNER JOIN `schema` on `value`.schema_id = `schema`.id INNER JOIN field on `value`.field_id = field_id
利與弊
好處:之前每個類都會生成一個表,現在無非是在scaehma新增一行記錄而已
壞處:關係複雜、表結構複雜,多長關係表組成的關係鏈,複雜的同時帶來了靈活性,ORM不識別這樣的表,只能自己封裝進行實現
可否在value 的value段用約束進行?
比如記錄ip,那麼ip不允許重複如何去寫?
這樣相當於所有表都互相干擾了,所以不允許加唯一鍵約束
可否建立一個ip 池的表,如果存放的主機資訊的,對於資產管理,將所有的服務配置,那麼就肯定涉及到ip
如果互不干擾的話,那麼可否通過sechma id進行判斷,需要考慮重複的時候判斷問題,那麼無非是插入的時候判斷
單獨寫型別非常有限,既然型別不合適,那麼ip地址肯定不合適,總需要方法來解決這些
那麼可否通過正則表示式,但是比較難掌握
通過meta進行限制,meta是text型別,是否可以使用json?
將json中的字串轉為python程式碼,通過反射動態載入執行,這樣實現的話需要約定好呼叫的介面
考慮的問題點:
1. 如何存放?如何描述?描述什麼?
2. 如果用到反射的話,那可否存放一個類名,一個模組名 直接載入它 直接呼叫這個方法
開發
設計一個類 ,通過反射來判斷這個值,進行欄位型別來驗證
儲存之前通過類轉為特殊型別轉為字串,來進行校驗,如果校驗成功則存入資料庫
建立約束型別,目錄結構如下:
建立基類,用於校驗,功能方法冗餘性全部在基類中實現,子類用於增強
校驗字符合法性
class BaseType: def stringify(self,value): raise NotImplementedError() def destringify(self,value): raise NotImplementedError() class Int(BaseType): def stringify(self,value): str(int(value)) def destringify(self,value): pass class Int(BaseType): def stringify(self,value): return str(int(value)) def destringify(self,value): pass
ip地址校驗
通過ipaddress 模組進行校驗ip地址合法性 import ipaddress class BaseType: def stringify(self,value): raise NotImplementedError() def destringify(self,value): raise NotImplementedError() class Int(BaseType): def stringify(self,value): return int(str(value)) def destringify(self,value): return value class IP(BaseType): def stringify(self,value): return str(ipaddress.ip_address(value)) def destringify(self,value): return value
反射
既然拿到了型別和值了,接下來如何操作?
通過getattr反射進行找到對應的方法
通過name返回object屬性值,當屬性不存在,將使用default返回,如果沒有預設,則丟擲AttributeError
通過getattr進行反射
當前模組還未匯入,而且字串還沒被分段
import json jsonstr = """ { "type":"cmdb.types.Int", "value":300 } """ obj = json.loads(jsonstr) print(obj) m,c = obj['type'].rsplit('.',maxsplit=1) print(m,c) # 返回如下 {'type': 'cmdb.types.Int', 'value': 300} types.Int cmdb
匯入模組importlib
通過獲取的值進行反射,這裡m對應的是cmdb.types, 正是init.py,將其匯入
我們看到看init是類方法,那麼需要扔一個值進去
進行例項化,傳入一個引數,那麼這個值是value
# 匯入cmdb.types mod = importlib.import_module(m) cls = getattr(mod,c) cls().stringify(obj['value']) # 返回如下 {'value': 300, 'type': 'cmdb.types.Int'} cmdb.types Int
校驗IP
一般要求分4段式
這裡用於測試 value 不應該寫在這裡,切記
抽象函式
一般抽象的時候都將其返回一個物件為止,所以需要改進如下:
import json import importlib jsonstr = """ { "type":"cmdb.types.IP", "value":"10.10.10.1" } """ obj = json.loads(jsonstr) def get_instance(type:str): # 這裡要約定好,不然無法判斷 m,c = type.rsplit('.',maxsplit=1) mod = importlib.import_module(m) cls = getattr(mod,c) return cls() print(get_instance(obj['type']).stringify(obj['value']))
給予一個類模組取出,將類載入起來之後返回類的例項
呼叫它的方法,將函式get_instance匯入到init中 import ipaddress import importlib def get_instance(type:str): m,c = type.rsplit('.',maxsplit=1) mod = importlib.import_module(m) cls = getattr(mod,c) return cls() class BaseType: def stringify(self,value): raise NotImplementedError() def destringify(self,value): raise NotImplementedError() class Int(BaseType): def stringify(self,value): return int(str(value)) def destringify(self,value): return value class IP(BaseType): def stringify(self,value): return str(ipaddress.ip_address(value)) def destringify(self,value): return value
這樣就可以完美解決資料型別的問題,通過傳遞進來的json串來找到對應方法並解析,從而進行判斷型別並return