CMDB 设计(二)实现host、ip存储


接上篇博文html


尝试添加一张主机表,用于管理主机信息java

无非表的内容是host,那么确定是在schema中创建,直接写描述便可python

schema 填入id,id默认是1,name对应的是hostweb

这里虚拟表所对应的东西是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)


描述字段json

经过filed表中添加两个字段 hostname,schema_id 这俩字段与host与关,其实就是与id与关联ide

这样自增的话,会产生第一个索引,那若是再进行添加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

相关文章
相关标签/搜索