zoukankan      html  css  js  c++  java
  • 03.主机资源采集反序列化

    一.models

    class Manufacturer(models.Model):
        vendor_name = models.CharField("厂商名称", max_length=32, db_index=True, unique=True, help_text="厂商名称")
        tel = models.CharField("联系电话", null=True, max_length=15, help_text="联系电话")
        mail = models.CharField("联系邮件", null=True, max_length=32, help_text="联系邮件")
        remark = models.CharField("备注", max_length=300, null=True, help_text="备注")
    
        def __str__(self):
            return self.vendor_name
    
        class Meta:
            db_table = "resources_manufacturer"
            ordering = ["id"]
    
    
    class ProductModel(models.Model):
        model_name = models.CharField("型号名称", max_length=20, help_text="型号名称")
        vendor = models.ForeignKey(Manufacturer, verbose_name="所属制造商", help_text="所属制造商",on_delete=models.CASCADE)
    
        def __str__(self):
            return self.model_name
    
        class Meta:
            db_table = "resources_productmodel"
            ordering = ["id"]
    
    
    class Server(models.Model):
        ip = models.CharField("管理ip", max_length=15, db_index=True, unique=True, help_text="管理ip")
        hostname = models.CharField("主机名", max_length=20, db_index=True, unique=True, help_text="主机名")
        cpu = models.CharField("CPU", max_length=50, help_text="CPU")
        mem = models.CharField("内存", max_length=32, help_text="内存")
        disk = models.CharField("磁盘", max_length=200, help_text="磁盘")
        os = models.CharField("OS", max_length=50, help_text="OS")
        sn = models.CharField("SN", max_length=50, db_index=True, help_text="SN")
        manufacturer = models.ForeignKey(Manufacturer, verbose_name="制造商", help_text="制造商",on_delete=models.CASCADE)
        model_name = models.ForeignKey(ProductModel, verbose_name="服务型号", help_text="服务器型号",on_delete=models.CASCADE)
        rmt_card_ip = models.CharField("管理管理卡IP", max_length=15, db_index=True, unique=True, help_text="管理管理卡IP")
        idc = models.ForeignKey(Idc, null=True, verbose_name="所在机房", help_text="所在机房",on_delete=models.CASCADE)
        cabinet = models.ForeignKey(Cabinet, null=True, verbose_name="所在机柜", help_text="所在机柜",on_delete=models.CASCADE)
        cabinet_position = models.CharField("机柜内位置", null=True, max_length=20, help_text="机柜内位置")
        uuid = models.CharField("UUID", db_index=True, unique=True, max_length=50, help_text="UUID")
        last_check = models.DateTimeField("检测时间", db_index=True, auto_now=True, help_text="检测时间")
        remark = models.CharField("备注", max_length=200, help_text="备注", null=True)
    
        def __str__(self):
            return self.ip
    
        class Meta:
            db_table = "resources_server"
            ordering = ["id"]
            # permissions = (
            #     ("view_server", "can view server"),
            # )
    
    
    class NetworkDevice(models.Model):
        """
        网卡模型
        """
        name = models.CharField("网卡设备名", max_length=20, help_text="网卡设备名")
        mac_address = models.CharField("MAC地址", max_length=30, help_text="MAC地址")
        host = models.ForeignKey(Server, verbose_name="所在服务器", help_text="所在服务器",on_delete=models.CASCADE)
        remark = models.CharField("备注", max_length=200, help_text="备注", null=True)
    
        def __str__(self):
            return self.name
    
        class Meta:
            db_table = "resources_network_device"
            ordering = ["id"]
    
    
    class IP(models.Model):
        """
        IP模型
        """
        ip_addr = models.CharField("ip地址", max_length=15, db_index=True, unique=True, help_text="ip地址")
        netmask = models.CharField("子网掩码", max_length=15, help_text="子网掩码")
        device = models.ForeignKey(NetworkDevice, verbose_name="所在网卡", help_text="所在网卡",on_delete=models.CASCADE)
        remark = models.CharField("备注", max_length=200, help_text="备注", null=True)
    
        def __str__(self):
            return self.ip_addr
    
        class Meta:
            db_table = "resources_ip"
            ordering = ["id"]
    
    
    说明
    • 1.厂家表Manufacturer,可直接反序列化
    • 2.厂家设备表ProductModel,存在外键,反序列化时需要传入外键表的实例化对象
    • 3.服务器表Server,存在多个外键字段,其中idccabinet允许为空,manufacturermodel_name不允许为空,反序列化时要先取这两个字段的实例
    • 4.反序列化ProductModel表时,需要先反序列化Manufacturer获取实例化对象

    二.来个最简单的,先反序列化创建一个主机,数据如下

    {
        "ip": "192.168.1.1",
        "hostname": "yz_cabinet_row5_01",
        "cpu": "32",
        "mem": "100G",
        "disk": "1000G",
        "os": "Centos 7.5",
        "sn": "54443398-17a9-4b4e-b536-7edac0a7c8e7",
        "manufacturer": "Oracle Corporation",
        "model_name": "VirtualBox",
        "uuid": "54443398-17a9-4b4e-b536-7edac0a7c8e7"
    }
    
    • 2.1 serializer 数据
    class ServerAutoReportSerializer(serializers.Serializer):
        """
        服务器同步序列化类
        """
        ip = serializers.IPAddressField(required=True)
        hostname = serializers.CharField(required=True, max_length=20)
        cpu = serializers.CharField(required=True, max_length=50)
        mem = serializers.CharField(required=True, max_length=20)
        disk = serializers.CharField(required=True, max_length=200)
        os = serializers.CharField(required=True, max_length=50)
        sn = serializers.CharField(required=True, max_length=50)
        manufacturer = serializers.CharField(required=True)
        model_name = serializers.CharField(required=True)
        uuid = serializers.CharField(required=True, max_length=50)
    
    • 2.2 重写 create 方法
        def create(self, validated_data):
            Server.objects.create(**validated_data) 
    

    发现需要传入 manufacturer model_name 实例化对象才能完成 ORM的 save 操作

    • 2.3 在校验单个字段时,完成manufacturer字段实例传入
    def validate_manufacturer(self,manufacturer):
            try:
                manufacturer_inst = Manufacturer.objects.get(vendor_name=manufacturer)
            except Manufacturer.DoesNotExist:
                manufacturer_inst = Manufacturer.objects.create(vendor_name=manufacturer)
            # print(manufacturer_inst,type(manufacturer_inst))
            return manufacturer_inst
    
    • 2.4 在总校验时,完成model_name字段实例的传入
        def validate(self, attrs):
            model_name = attrs['model_name']
            manfacturer_inst = attrs['manufacturer']
            try:
                produce_inst = manfacturer_inst.productmodel_set.get(model_name=model_name)
                # 这里需要注意,需要通过父级实例利用model_set反查子实例是否存在数据,反查返回的是被反查的对象实例
            except ProductModel.DoesNotExist:
                produce_inst = ProductModel.objects.create(vendor=manfacturer_inst,model_name=model_name)
            attrs['model_name']=produce_inst
            print(attrs)
            return attrs
    
    • 2.4 打印发现attrs已经是这两个字段的实例对象
    OrderedDict([('ip', '192.168.1.1'), ('hostname', 'yz_cabinet_row5_01'), ('cpu', '32'), ('mem', '100G'), ('disk', '1000G'), ('os', 'Centos 7.5'), ('sn', '54443398-17a9-4b4e-b536-7edac0a7c8e7'), ('manufacturer', <Manufacturer: Oracle Corporation>), ('model_name', <ProductModel: VirtualBox>), ('uuid', '54443398-17a9-4b4e-b536-7edac0a7c8e7')])
    
    • 2.5 此时直接在create中完成主机的创建
    Server.objects.create(**validated_data) 
    

    三. 自动采集的话,都是post数据,可以在create方法中同时完成更新动作

    第一次采集,写本地文件的方式确定唯一更可取,判断的话可能需要枚举所有虚拟环境的uuid和sn情况

        def create(self, validated_data):
            uuid = validated_data['uuid'].lower()
            sn = validated_data['sn'].lower()
            try:
                if sn == uuid or sn =='' or sn.startswith('vmware'):
                    server_inst = Server.objects.get(uuid=uuid)
                    # 虚拟机
                else:
                    # 物理机
                    server_inst = Server.objects.get(sn=sn)
            except Server.DoesNotExist:
                return self.create_server(validated_data)
            else:
                return self.update_server(server_inst, validated_data)
    

    四.反序列化增加不在model中的字段,serializer如下

        ip = serializers.IPAddressField(required=True)
        hostname = serializers.CharField(required=True, max_length=20)
        cpu = serializers.CharField(required=True, max_length=50)
        mem = serializers.CharField(required=True, max_length=20)
        disk = serializers.CharField(required=True, max_length=200)
        os = serializers.CharField(required=True, max_length=50)
        sn = serializers.CharField(required=True, max_length=50)
        manufacturer = serializers.CharField(required=True)
        model_name = serializers.CharField(required=True)
        uuid = serializers.CharField(required=True, max_length=50)
        network = serializers.JSONField()  # 这个是额外增加的
    

    增加一个json字段,携带network信息,多网卡,一张网卡多IP,post 数据如下

    {
        "ip": "192.168.1.1",
        "hostname": "yz_cabinet_row5_01",
        "cpu": "32",
        "mem": "100G",
        "disk": "1000G",
        "os": "Centos 7.5",
        "sn": "54443398-17a9-4b4e-b536-7edac0a7c8e7",
        "manufacturer": "Oracle Corporation",
        "model_name": "VirtualBox",
        "uuid": "54443398-17a9-4b4e-b536-7edac0a7c8e7",
        "network": [
    		{
    			"name":"eth0",
    			"ips":[
    				{"ip_addr":"192.168.1.1","netmask":"255.255.255.0"}
    			],
    			"mac":"08:00:27:9b:99:bc"
    		},
    				{
    			"name":"eth1",
    			"ips":[
    				{"ip_addr":"192.168.1.2","netmask":"255.255.255.0"},
    				{"ip_addr":"192.168.1.3","netmask":"255.255.255.0"}
    			],
    			"mac":"08:00:27:9b:99:b1"
    		}
    	]
    }
    
    思路:在真正创建 server 后,拿着 server 的实例去创建网卡,拿着网卡的实例,去创建IP数据对象
    • 4.1 在create中增加网卡的处理
        def create_server(self,validated_data):
            network = validated_data.pop('network')
            server_inst = Server.objects.create(**validated_data)
            self.check_network_device(server_inst,network)
            return server_inst
    
        def check_network_device(self,server_inst,network):
            print('处理network:',server_inst,type(server_inst),network)
    
    • 4.2 post 之后已经能获取到network的反序列化数据
    处理network: 192.168.1.1 <class 'servers.models.Server'> [{'name': 'eth0', 'ips': [{'ip_addr': '192.168.1.1', 'netmask': '255.255.255.0'}], 'mac': '08:00:27:9b:99:bc'}, {'name': 'eth1', 'ips': [{'ip_addr': '192.168.1.1', 'netmask': '255.255.255.0'}], 'mac': '08:00:27:9b:99:b1'}, {'name': 'eth2', 'ips': [{'ip_addr': '192.168.1.2', 'netmask': '255.255.255.0'}], 'mac': '08:00:27:9b:99:b2'}]
    
    • 4.3 但是会报错,因为序列化的时候这个network根本不存在模型中
    AttributeError: 'Server' object has no attribute 'network'
    
    • 4.4 post 之后 drf 会调用to_representation函数,重写之
        def to_representation(self, instance):
            ret = {
                "hostname": instance.hostname,
                "ip": instance.ip
            }
            return ret
    
    • 4.5 此时返回
    {"hostname":"yz_cabinet_row5_01","ip":"192.168.1.1"}
    

    image

    五.遍历network变量,创建网卡

        def check_network_device(self,server_inst,network):
            print('处理network:',server_inst,type(server_inst),network)
            # 创建网卡
            for device in network:
                ips = device.pop('ips')
                try:
                    device_inst = server_inst.networkdevice_set.get(name=device['mac'])
                except NetworkDevice.DoesNotExist:
                    device['host'] = server_inst
                    device['mac_address'] = device.pop('mac')
                    device_inst  = NetworkDevice.objects.create(**device)
                # 创建完网卡,需要创建IP
                self.check_ip(device_inst,ips)
    
        def check_ip(self,device_inst,ips):
            print(device_inst,ips)
    

    六. 遍历 ip,创建IP

        def check_ip(self,device_inst,ips):
            print(device_inst,ips)
            for ip in ips:
                try:
                    ip_inst = device_inst.ip_set.get(ip_addr=ip['ip_addr'])
                except IP.DoesNotExist:
                    ip['device'] = device_inst
                    ip_inst = IP.objects.create(**ip)
    
    
    此时 主机、网卡、IP 都可以创建
    {"hostname":"yz_cabinet_row5_01","ip":"192.168.1.1"}
    

    image
    image
    image

    七. 数据存在时候更新数据

    7.1 第一种情况,IP更改了
    • 7.1.1 使用集合,删除多余的IP,提交输入如下,一张网卡少了1个IP
    {
        "ip": "192.168.1.1",
        "hostname": "yz_cabinet_row5_01",
        "cpu": "32",
        "mem": "100G",
        "disk": "1000G",
        "os": "Centos 7.5",
        "sn": "54443398-17a9-4b4e-b536-7edac0a7c8e7",
        "manufacturer": "Oracle Corporation",
        "model_name": "VirtualBox",
        "uuid": "54443398-17a9-4b4e-b536-7edac0a7c8e7",
        "network": [
    		{
    			"name":"eth0",
    			"ips":[
    				{"ip_addr":"192.168.1.1","netmask":"255.255.255.0"}
    			],
    			"mac":"08:00:27:9b:99:bc"
    		},
    				{
    			"name":"eth1",
    			"ips":[
    				{"ip_addr":"192.168.1.2","netmask":"255.255.255.0"}
    			],
    			"mac":"08:00:27:9b:99:b1"
    		}
    	]
    }
    
    • 7.1.2 保存数据库的IP集合,与当前采集的IP集合做差集
        def check_ip(self,device_inst,ips):
            # 不管三七二一,先把数据库的IP集合临存
            last_ip_qs= device_inst.ip_set.all()
            # 用个列表把当前采集的IP集合起来
            current_ip = []
            for ip in ips:
                try:
                    ip_inst = last_ip_qs.get(ip_addr=ip['ip_addr'])
    
                except IP.DoesNotExist:
                    ip['device'] = device_inst
                    ip_inst = IP.objects.create(**ip)
                current_ip.append(ip_inst)
            for ip_inst in set(last_ip_qs) - set(current_ip):
                print(ip_inst)
                # ip_inst.delete()
    

    输出如下:

    <class 'django.db.models.query.QuerySet'> <class 'list'>
    192.168.1.3
    

    queryset 也是个列表,特殊的列表

    image

    7.2 第二种情况,删除一个网卡
    {
        "ip": "192.168.1.1",
        "hostname": "yz_cabinet_row5_01",
        "cpu": "32",
        "mem": "100G",
        "disk": "1000G",
        "os": "Centos 7.5",
        "sn": "54443398-17a9-4b4e-b536-7edac0a7c8e7",
        "manufacturer": "Oracle Corporation",
        "model_name": "VirtualBox",
        "uuid": "54443398-17a9-4b4e-b536-7edac0a7c8e7",
        "network": [
    		{
    			"name":"eth0",
    			"ips":[
    				{"ip_addr":"192.168.1.1","netmask":"255.255.255.0"}
    			],
    			"mac":"08:00:27:9b:99:bc"
    		}
    	]
    }
    
    • 代码如下
        def check_network_device(self,server_inst,network):
            last_device_inst_qs = server_inst.networkdevice_set.all()
            current_device = []
            for device in network:
                ips = device.pop('ips')
                try:
                    device_inst = last_device_inst_qs.get(mac_address=device['mac'])
    
                except NetworkDevice.DoesNotExist:
                    device['host'] = server_inst
                    device['mac_address'] = device.pop('mac')
                    device_inst  = NetworkDevice.objects.create(**device)
                # 创建完网卡,需要创建IP
                self.check_ip(device_inst,ips)
                current_device.append(device_inst)
            for device_inst in set(last_device_inst_qs) - set(current_device):
                print(device_inst)
                device_inst.delete()
    

    image

    last_device_inst_qs: <QuerySet [<NetworkDevice: eth0>, <NetworkDevice: eth1>]>
    eth1
    

    八. 增加网卡 增加IP都可以

    image
    image

    九.存在问题

    • 1.管理卡IP采集不到,唯一键
    • 2.如果数据都没有变化,根本不需要提交数据更改
    • 3.变更的数据前后记录没有记录下来
  • 相关阅读:
    让Android模拟器速度飞起来_Eclipse+BlueStacks调试Android应用【2012-10-30】
    开源镜像站-Android镜像
    字符编码的几篇文章
    [C/C++]_[Unicode转Utf8,Ansi转Unicode,Ansi文件转Utf8文件]
    MSVC下快速Unicode I/O
    edltplus使用正则表达式替换多余空行
    修改CMD的编码
    windows 安裝 gcc 編譯器
    CF369 C(递归 + 回溯)
    VIM支持系统剪切板
  • 原文地址:https://www.cnblogs.com/jenvid/p/12927247.html
Copyright © 2011-2022 走看看