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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章