设为首页 收藏本站
查看: 2734|回复: 0

[经验分享] openstack 扩展开发最佳实践之云主机监控查询

[复制链接]

尚未签到

发表于 2018-5-31 08:49:28 | 显示全部楼层 |阅读模式
  前言:此功能代码不代表笔者代码正常水平。。。真的只是为了实现而实现,很多硬代码加不优雅的地方。就让我自己先吐槽一下吧,首先是代码每次会去拿token而不是用horizon缓存的token,没有将js,css优雅的放进horizon,以及panel的名字都没改,最后就是前端布局不好看(这个真的是当下能力之外的了)。
  

  首先说说需求,在horizon有这么一个页面,可以查看本项目的虚拟机的一些基本的性能指标,比如cpu使用率,内存使用率,磁盘io,网络io等。

  怎么实现呢?

  数据获取:

  方案一,自己造轮子,写个agent到计算节点,然后起个服务作为服务端,然后horizon交互服务端。

  方案二,用zabbix,写个plugin,zabbix存,horizon与zabbix交互。

  方案三,horizon与ceilometer交互。

  页面数据展示:

  方案一:highcharts

  方案二:echarts

  方案三:horizon的js库。
  

  笔者的数据取自ceilometer,数据展示选择highcharts。
  注:ceilometer众所周知是有性能问题的,解决方案是gnocchi。
  主题内容如下:

  一:Ceilometer API

  二:Highcharts API

  三:组织代码

  

(一)Ceilometer API

  CPU使用率:/v2/meters/cpu_util
  内存使用量:/v2/meters/memory.resident
  磁盘io:/v2/meters/disk.read.bytes.rate,/v2/meters/disk.write.bytes.rate
  网络io:/network.incoming.bytes.rate, network.outgoing.bytes.rate
  

(二)Highcharts

<script language="JavaScript">
$(document).ready(function() {
   var chart = {
      zoomType: 'x'
   };
   var credits = {
            text: 'UMCloud',
            href: 'http://www.umcloud.com/'
   };
   var title = {
      text: "`hn`: {{ ret.0.title }}"
   };
   var xAxis = {
      type: 'datetime',
      labels: {
                step: 1,
                formatter: function () {
                    return Highcharts.dateFormat('%Y-%m-%d %H:%M', this.value );
                    }
             }};
   var yAxis = {
      title: {
         text: "单位: {{ ret.0.unit }}"
      },
      labels: {
        formatter: function () {
            return this.value;
        }
    }
   };
   var tooltip = {
      valueSuffix: '{{ ret.0.unit }}',
      headerFormat: '{point.x:%Y-%m-%d %H:%M}<br>',
      pointFormat: "{series.name}: {point.y:.3f}"
   }
   var legend = {
       enabled: false
      //layout: 'vertical',
      // align: 'right',
      //verticalAlign: 'middle',
      // borderWidth: 0
   };
   var exporting =  {
        csv: {
            dateFormat: '%Y-%m-%d %H:%M'
        }
    };
   var setOptions = {
        global:
          { useUTC: false }
        };
   var series = {{ ret.1 }} ;

   var json = {};
   json.title = title;
   json.chart = chart;
   json.xAxis = xAxis;
   json.yAxis = yAxis;
   json.tooltip = tooltip;
   json.legend = legend;
   json.series = series;
   json.credits = credits;
   json.setOptions = setOptions;
   $('#containerP').highcharts(json);
});
   $('#tableid').DataTable();
</script>  

  

(三)组织代码

#coding: utf-8
import sys
import requests
import time
import logging
import os
import subprocess as sp
from ConfigParser import ConfigParser
from pprint import pprint
import datetime
import json

confFile="/etc/unit.conf"
logfile = "/var/log/selfm.log"
debug=False
level = logging.WARNING if not debug else logging.DEBUG
logging.basicConfig(level=level,
                format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                datefmt='%a, %d %b %Y %H:%M:%S',
                filename=logfile,
                filemode='a')
def tons(dateStr):
    """convert the isodateformate str to unix timestamp"""
    try:
        d = datetime.datetime.strptime(dateStr,'%Y-%m-%dT%H:%M:%S.%f')
    except Exception as e:
        d = datetime.datetime.strptime(dateStr,'%Y-%m-%dT%H:%M:%S')
    d = d + datetime.timedelta(hours=8)
    ret = d.strftime("%s000")
    return int(ret)
class baseInfo(object):
    """init the info of API, and get the token for access the api"""
    def __init__(self, token=None):
        headers = {}
        headers["Content-Type"] = "application/json"
        self.headers = headers
        self.cf = ConfigParser()
        self.cf.read(confFile)
        self.conf = self.getConf()
        self.catalog, self.token = self.getToken()
        self.url = [url for url in self.catalog if url["name"] == "ceilometer"]
        self.url = self.url[0]["endpoints"][0]["publicURL"]
    def getConf(self):
        try:
            conf = {
                "url": self.cf.get("ser","OS_AUTH_URL"),
                "uname" : self.cf.get("ser","OS_USERNAME"),
                "passwd" : self.cf.get("ser","OS_PASSWORD"),
                "tname" : self.cf.get("ser","OS_TENANT_NAME")}
        except Exception as e:
                logging.critical("加载配置文件失败")
                logging.critical(e)
        return conf
    def getToken(self):
        headers = self.headers
        url = self.conf["url"] + "/tokens"
        data = '{"auth": {"tenantName": "%s", "passwordCredentials": {"username": "%s", "password": "%s"}}}'
        data = data % (self.conf["tname"], self.conf["uname"], self.conf["passwd"])
        try:
            logging.debug("开始获取Token")
            ret = requests.post(url, data=data, headers=headers)
            #print ret.url
            logging.debug("request url:%s" % ret.url)
            ret = ret.json()
        except Exception as e:
            msg = "获取Token失败 data:%s headers:%s" % (data, headers)
            logging.critical(msg)
            logging.critical(e)
        catalog = ret["access"]["serviceCatalog"]
        token = ret["access"]["token"]["id"]
        return catalog, token
    def getCResp(self, suffix, method, data=None, headers=None, params=None, isjson=True):
        """return the result of ceilometer response"""
        url = self.url + suffix
        if headers == None:
            headers = self.headers.copy()
        headers["X-Auth-Token"] = self.token
        req = getattr(requests, method)
        try:
            ret = req(url, data=data, headers=headers, params=params, verify=False)
            #print ret.url
            logging.debug("request url:%s" % ret.url)
        except Exception as e:
            msg = "%s访问%s失败 data:%s headers:%s" % (method, suffix, data, headers)
            logging.critical(msg)
            logging.critical(e)
            sys.exit(1)
        if ret.status_code == 401:
            self.catalog, self.token = self.getToken()
            headers["X-Auth-Token"] = self.token
            ret = req(url, data=data, headers=headers)
        if isjson:
            ret = ret.json()
        return ret
class ceil(baseInfo):
    """the class for grab ceilometer metric"""
    def __init__(self, vm, timeRange):
        super(ceil, self).__init__()
        self.timeRange = timeRange
        self.vm = vm
        self.qge = "&q.field=timestamp&q.op=lt&q.value=%s" % self.timeRange[0]
        self.qlt = "&q.field=timestamp&q.op=ge&q.value=%s" % self.timeRange[1]
        self.qr = "?q.field=resource_id&q.op=eq&q.value=%s" % self.vm
    def getData(self, suffix, plotype, title):
        data = []
        options = {}
        for s in suffix:
            s2 = "".join([s, self.qr, self.qge, self.qlt, '&limit=10000'])
            #print s2
            resp = self.getCResp(s2, "get")
            if resp:
                if s in ["/v2/meters/disk.read.bytes.rate", "/v2/meters/disk.write.bytes.rate"]:
                    volumes = [[tons(i["recorded_at"]), i["counter_volume"] / 1024  ] for i in resp ]
                    unit = "KB/s"
                    name = s.split("/")[-1]
                else:
                    volumes = [[tons(i["recorded_at"]), i["counter_volume"]] for i in resp ]
                    unit = resp[1]["counter_unit"]
                    name = s.split("/")[-1]

                seq = {"type": plotype,
                       "name": name,
                       "data": volumes}
                data.append(seq)
            else:
                unit = "None"
                title = "No Data"
            options["unit"] = unit
            options["title"] = title
            ret = [options, data]
        return ret
    def cpu(self, plotype="line", title="cpu_util"):
        """return the data series of highchart need
        ret = [options, data]
            options["unit"] = "None"
            options["title"] = "No Data"
            data = [{type:plottype,name:name,data:[nstime, datapoint]}]
        """
        suffix = ["/v2/meters/cpu_util"]
        ret = self.getData(suffix, plotype, title)
        return ret
    def disk(self, plotype="line", title="diskio"):
        suffix = ["/v2/meters/disk.read.bytes.rate", "/v2/meters/disk.write.bytes.rate"]
        ret = self.getData(suffix, plotype, title)
        return ret
    def mem(self, plotype="line", title="memory_usage"):
        suffix = ["/v2/meters/memory.resident"]
        #suffix = ["/v2/meters/memory.resident", "/v2/meters/memory"]
        ret = self.getData(suffix, plotype, title)
        return ret
    def net(self, plotype="line"):
        suffix = "/v2/query/samples"
        meters = ["network.incoming.bytes.rate", "network.outgoing.bytes.rate"]
        options = {}
        data = []
        kb = 1024
        mb = 1024 * 1024
        for m in meters:
            d = {
                "filter": "{\"and\": [{\"=\": {\"counter_name\": \"%s\"}}, {\"=~\": {\"resource_id\":\".*%s.*\"}}, {\"<\": {\"timestamp\": \"%s\"}}, {\">\": {\"timestamp\": \"%s\"}}]}" % (m, self.vm, self.timeRange[0], self.timeRange[1])
                }
            #print d
            resp = self.getCResp(suffix, "post", data=json.dumps(d))
            if resp:
                volumes = [[tons(i["recorded_at"]), i["volume"] / 1024 ] for i in resp ]
                unit = "KB/s"
                name = m
                seq = {"type": plotype,
                       "name": name[8:16],
                       "data": volumes}
                data.append(seq)
            else:
                unit = "None"
                title = "No Data"
        options["unit"] = unit
        options["title"] = "netio"
        ret = [options, data]
        return ret
def test():
    now = datetime.datetime.utcnow()
    yesterday = now + datetime.timedelta(hours=-2)
    now = now.isoformat()
    print now
    yesterday = yesterday.isoformat()
    c = ceil("8fc2a76e-90c5-491e-b207-28e90d5a5fab", (now, yesterday))
    pprint(c.net())
    #pprint(c.net())
if __name__ == "__main__":
    test()  

总结

  因为没有很技巧的部分,所以写着并不是有多少激情,只是实现了功能而已。需要花点时间的就是查询API,以及horizon的一些框架结构。
  注:调试rest API超级推荐postman这个应用。

  

  附图:
DSC0000.png

DSC0001.png

DSC0002.png

DSC0003.png

DSC0004.png

  

  

  相关链接:
  http://docs.openstack.org/admin-guide/telemetry-measurements.html
  https://docs.openstack.org/developer/ceilometer/webapi/v2.html
  http://api.highcharts.com/highcharts
  

  代码地址:https://github.com/youerning/UserPyScript

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-483279-1-1.html 上篇帖子: openstack 扩展开发最佳实践之计算节点高可用 下篇帖子: 安装HPE Helion Openstack
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表