skypaladin 发表于 2018-5-31 08:49:28

openstack 扩展开发最佳实践之云主机监控查询

  前言:此功能代码不代表笔者代码正常水平。。。真的只是为了实现而实现,很多硬代码加不优雅的地方。就让我自己先吐槽一下吧,首先是代码每次会去拿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 %(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 = == "ceilometer"]
      self.url = self.url["endpoints"]["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
      self.qlt = "&q.field=timestamp&q.op=ge&q.value=%s" % self.timeRange
      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()
            #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 = [), i["counter_volume"] / 1024] for i in resp ]
                  unit = "KB/s"
                  name = s.split("/")[-1]
                else:
                  volumes = [), i["counter_volume"]] for i in resp ]
                  unit = resp["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 =
      return ret
    def cpu(self, plotype="line", title="cpu_util"):
      """return the data series of highchart need
      ret =
            options["unit"] = "None"
            options["title"] = "No Data"
            data = [{type:plottype,name:name,data:}]
      """
      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, self.timeRange)
                }
            #print d
            resp = self.getCResp(suffix, "post", data=json.dumps(d))
            if resp:
                volumes = [), i["volume"] / 1024 ] for i in resp ]
                unit = "KB/s"
                name = m
                seq = {"type": plotype,
                     "name": name,
                     "data": volumes}
                data.append(seq)
            else:
                unit = "None"
                title = "No Data"
      options["unit"] = unit
      options["title"] = "netio"
      ret =
      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这个应用。

  

  附图:





  

  

  相关链接:
  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]
查看完整版本: openstack 扩展开发最佳实践之云主机监控查询