一个简单的puppet report页面

这个月就这么结束了,感到很惭愧,本来计划好的是月底翻译一篇关于python socket编程的文章,结果到现在连个影都没有,对自己的自控力感到失望。想了想,总不能这个月,博客就这么长草吧,于是就把之前写的一个小东西拿出来充数了。

说下我们使用puppet的方式:crontab定时每五分钟执行一次puppet。每次配置更新,就是在puppet master上改下相应的配置,然后等五分钟默认puppet agent都更新过了。这样明显是有问题的,特别是对于强迫症患者,我怎么知道哪些设备配置更新了,哪些又没有呢?

想了下,本来想测试下一些puppet的控制台Puppet DashBoard、Foreman什么的,结果一看介绍这么多功能,这么麻烦,而且又不是我所需要的,就不搞了,自己写一个简单的report页面不就完事了,只需要一个功能就是记录puppet agent的更新时间,对配置没有更新的设备红色显示。

梳理下思路,如下:

要干什么:写一个简单的页面,告诉我那些设备执行过puppet,哪些没有?
解决方法:1.puppet agent端执行puppet成功时,向puppet master返回一个时间戳
        2.puppet master端页面显示每一台puppet设备的更新时间,对没有更新的,红色显示

一:于是数据库设计如下:

mysql> desc idc;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| idc_id     | int(11)      | NO   | PRI | NULL    | auto_increment |
| idc_name   | varchar(255) | NO   |     | -       |                |
| idc_desc   | varchar(255) | NO   |     | -       |                |
| puppet_lid | varchar(255) | NO   |     | lid     |                |
+------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql> desc machine;
+---------------------+--------------+------+-----+---------+----------------+
| Field               | Type         | Null | Key | Default | Extra          |
+---------------------+--------------+------+-----+---------+----------------+
| machine_id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| idc_id              | int(11)      | NO   |     | NULL    |                |
| role_id             | int(11)      | NO   |     | NULL    |                |
| machine_intranet_ip | varchar(255) | NO   |     | -       |                |
| machine_extranet_ip | varchar(255) | NO   |     | -       |                |
| machine_card_ip     | varchar(255) | NO   |     | -       |                |
| machine_name        | varchar(255) | NO   |     | -       |                |
| machine_disk        | varchar(255) | NO   |     | -       |                |
| machine_bandwidth   | varchar(255) | NO   |     | -       |                |
| machine_memory      | varchar(255) | NO   |     | -       |                |
| machine_cpu         | varchar(255) | NO   |     | -       |                |
| machine_inventory   | varchar(255) | NO   |     | -       |                |
| is_puppet           | varchar(255) | YES  |     | true    |                |
| update_puppet       | int(11)      | YES  |     | 28800   |                |
| comment             | varchar(255) | YES  |     | NULL    |                |
+---------------------+--------------+------+-----+---------+----------------+
15 rows in set (0.00 sec)

mysql> desc machine_role;
+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| role_id   | int(11)      | NO   | PRI | NULL    | auto_increment |
| role_name | varchar(255) | NO   |     | -       |                |
| role_desc | varchar(255) | NO   |     | -       |                |
+-----------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

mysql> desc user;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| name     | varchar(255) | NO   |     | -       |                |
| password | varchar(255) | NO   |     | -       |                |
+----------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

解释下各个表的作用:

表名 描述
idc 机房信息表
machine 服务器信息表
machine_role 服务器角色信息表
user 系统用户表

实现一个简单的puppet汇报功能,上面用了四张表和那么多无关的字段,明显复杂了,那是因为还有一个简单的cmdb系统在使用这些表。跟本篇文章相关的主要是下面这张表的两个字段:

表名 字段
machine is_puppet 是否安装puppet
update_puppet puppet agent配置更新时间

二:代码,puppet master服务端用的是tornado框架,具体代码如下:

a. puppet agent端,每次puppet更新后,向puppet master服务器返回一个时间戳

#!/bin/bash
timestamp=`date +%s`
puppet agent --server puppet.domain.com --test && curl "http://puppet_master_ip?key=DBZS83EkAd&timestamp=$timestamp"

b. puppet master端,接收puppet agent curl过来时间戳,存到数据库,并将此条信息记录到日志文件(出错时有日志可查)

class PuppetHandler(BaseHandler):

    def write_log(self,m):
        today = time.strftime('%Y%m%d',time.localtime(time.time()))
        update_log = os.path.join('/data1/logs/tornado/', "%s_update.log" % today)

        with open(update_log,'a') as fh:
            fh.write('%s\n' % m)

    def get(self):
        client_ip = self.request.headers['X-Forwarded-For']
        timestamp = self.get_argument('timestamp')
        key = self.get_argument('key')
        now = time.strftime('%Y-%m-%d_%H:%M:%S',time.localtime(time.time()))
        message = ''.join([str(now)," update `machine` set `update_puppet`=",str(timestamp)," where `machine_extranet_ip`= ",str(client_ip)])
        if key != 'DBZS83EkAd':
            return
        else:
            self.db.execute("update `machine` set `update_puppet`=%s where `machine_extranet_ip`='%s'" % (int(timestamp),str(client_ip)))
            message = ''.join([message,' sucess'])
            self.write_log(message)
        self.write('ok')

c. puppet master端,页面渲染进行展示

class MachineIndexHandler2(BaseHandler):
    def most_tuple_value(self,d):
        most = 0
        frequency = 0
        values_list = [v[1] for v in d]
        values_set = set(values_list)
        for value in values_set:
            if values_list.count(value) > frequency:
                frequency = values_list.count(value)
                most = value
        return  most

    def same_tuple_value(self,d,v):
        l = []
        for key in d:
            if v == key[1]:
                l.append(key[0])
        return l

    def remove_bad_person(self,l):
        d = []
        for i in range(len(l)):
            sum = 0
            for j in range(len(l)):
                if 0 <= abs(l[i] - l[j]) <= 480:
                    sum += 1
                else:
                    sum -= 1
            d.append((l[i],sum))
        most_value = self.most_tuple_value(d)
        same_value = self.same_tuple_value(d,most_value)
        return same_value

    @tornado.web.authenticated
    def get(self):
        idc = self.db.query("select `idc_name` from idc")
        idc_name = [i.idc_name for i in idc]
        all_machine = []
        for name in idc_name:
            machine = self.db.query("SELECT `idc_name`,`role_name`,`machine_extranet_ip`,`comment`,`is_puppet`,`update_puppet` FROM machine INNER JOIN idc ON machine.idc_id=idc.idc_id INNER JOIN machine_role ON mach
ine.role_id=machine_role.role_id where `idc_name`='%s'" % name)
            l = [i['update_puppet'] for i in machine]
            same_value = self.remove_bad_person(l)
            r = machine,same_value
            all_machine.append(r)
        #self.write(json.dumps(all_machine))
        self.render("machine2.html",result=all_machine)

三:最后页面呈现的效果如下:

看着像是结束语:

这里puppet master端只有update_puppet这个字段记录了puppet agent最后的更新时间,然后页面显示时使用了一个自己写的小算法,把看着像没更新的设备红色显示出来,这么做其实是复杂了。例如简单点可以搞两个字段,last_update表示上次puppet agent更新的时间,current_update表示当前puppet更新的时间戳,两个时间一比较就知道puppet agent是否更新了。其实,我写这篇博客的目的并不是想写这个puppet report的小页面,而是想好好的写下这个自己无意中想出来的小算法。因此,故事才刚刚开始,请继续往下看。


真正的故事才刚刚开始,谢谢!!!

想想这个小题目:

a.1 2 3 4 5 100 
b.1 1 1 1 1 3
a b 两组数据一看就感觉 100 和 3 出现的很突兀,问怎么把他挑出来呢?

上面显示未更新的puppet设备也是用的这个算法,试想下如果一个节点的puppet都更新了,那么他们的时间戳相差不大会太大(差不多在300s左右),如果有某个的时间戳跟其他的比起来差距特别大(600s)时我就默认他没有更新。

按照a组数据,看下这个小算法的执行过程:

a组数据: 1 2 3 4 5 100
循环:
a. 1分别对1,2,3,4,5,100 问我是一个好人吗(相差的绝对值小于等于5就是)?
|1-1=0|<5     是 +1  
|1-2=-1|<5    是 +1
|1-3=-2|<5    是 +1
|1-4=-3|<5    是 +1
|1-5=-4|<5    是 +1
|1-100=-99|>5 否 -1
将判断结果相加,得出一个我自己命名的”好人度"1+1+1+1+1-1=4
b.按a的方法通样对2,3,4,5,100进行处理,最终得到的结果为
好人度 4 4 4 4 4 -4
      1 2 3 4 5 100
c.根据好人度将100剔除

程序 remove_bad_person.py 如下:

#!/usr/bin/python
#coding:utf-8

L1 = [1,2,3,4,5,100]

def most_tuple_value(d):
    most = 0
    frequency = 0
    values_list = [v[1] for v in d]
    values_set = set(values_list)
    for value in values_set:
        if values_list.count(value) > frequency:
            frequency = values_list.count(value)
            most = value
    return  most

def same_tuple_value(d,v):
    l = []
    for key in d:
        if v == key[1]:
            l.append(key[0])
    return l

def remove_bad_person(l):
    d = []
    for i in range(len(l)):
        sum = 0
        for j in range(len(l)):
            if 0 <= abs(l[i] - l[j]) <= 5:
                sum += 1
            else:
                sum -= 1
        d.append((l[i],sum))
    print d
    most_value = most_tuple_value(d)
    print most_value
    same_value = same_tuple_value(d,most_value)
    print same_value

def main():
    print L1
    remove_bad_person(L1)

if __name__ == '__main__':
    main()

执行下:

./remove_bad_person.py
[1, 2, 3, 4, 5, 100]
[(1, 4), (2, 4), (3, 4), (4, 4), (5, 4), (100, -4)]
4
[1, 2, 3, 4, 5]

那时写这个puppet report页面时,想着puppet agent传过来只有时间戳,怎么判断哪台更新了,哪台没有呢?后来想了半天,无意中想到这个小算法,挑出一堆数据里与其他数据相差最大的那个,想到这个可以用于判断puppet是否更新的,一堆时间戳中与其他时间戳相差最大的一个或者几个肯定是没有更新过,于是就有了本篇文章。现在,要是让我再做的话,我宁愿像上面说过的多建一个字段last_update,每次更新时比较下last_update、current_update不就知道了,用不着这么麻烦。本想把这篇文章写的更清晰易懂些,这个小算法还有些值得写的东西,但是时间不允许了,大家就自己看代码了,中秋节到了,我要回家去了,见谅。