接上篇:[CloudStack VM运行状态的监控-Management]
本文继续讲解Hyperviser端的处理逻辑.

XenServer

XenServer的处理逻辑在CitrixResourceBase.java 中,该类封装了几乎全部的XenServer操作,主要调用XenServer API 和 XenServer RRD API来完成。

@Override
public Answer executeRequest(Command cmd) {
    Class<? extends Command> clazz = cmd.getClass();
    if (clazz == CreateCommand.class) {

    ......

    } else if (clazz == GetVmStatsCommand.class) {
        return execute((GetVmStatsCommand)cmd);
    }

    ......

}

该方法判断接收到的Command类型,并dispatch到相关处理方法。

protected GetVmStatsAnswer execute(GetVmStatsCommand cmd) {

    ......

        HashMap<String, VmStatsEntry> vmStatsUUIDMap = getVmStats(conn, cmd, vmUUIDs, cmd.getHostGuid());

        ......

        for (String vmUUID : vmStatsUUIDMap.keySet()) {
            vmStatsNameMap.put(vmNames.get(vmUUIDs.indexOf(vmUUID)), vmStatsUUIDMap.get(vmUUID));
        }

    ......

    return new GetVmStatsAnswer(cmd, vmStatsNameMap);

}

该方法调用getVmStats()方法,获取数据,并进行处理后返回。

protected HashMap<String, VmStatsEntry> getVmStats(Connection conn, GetVmStatsCommand cmd, List<String> vmUUIDs, String hostGuid) {
    HashMap<String, VmStatsEntry> vmResponseMap = new HashMap<String, VmStatsEntry>();

    ......

    Object[] rrdData = getRRDData(conn, 2); // call rrddata with 2 for vm

    ......//解析RRD Data

            if (param.contains("cpu")) {
                vmStatsAnswer.setNumCPUs(vmStatsAnswer.getNumCPUs() + 1);
                vmStatsAnswer.setCPUUtilization(((vmStatsAnswer.getCPUUtilization() + getDataAverage(dataNode, col, numRows))));
            } else if (param.matches("vif_\\d*_rx")) {
                vmStatsAnswer.setNetworkReadKBs(vmStatsAnswer.getNetworkReadKBs() + (getDataAverage(dataNode, col, numRows)/1000));
            } else if (param.matches("vif_\\d*_tx")) {
                vmStatsAnswer.setNetworkWriteKBs(vmStatsAnswer.getNetworkWriteKBs() + (getDataAverage(dataNode, col, numRows)/1000));
            } else if (param.matches("vbd_.*_read")) {
                vmStatsAnswer.setDiskReadKBs(vmStatsAnswer.getDiskReadKBs() + (getDataAverage(dataNode, col, numRows)/1000));
            } else if (param.matches("vbd_.*_write")) {
                vmStatsAnswer.setDiskWriteKBs(vmStatsAnswer.getDiskWriteKBs() + (getDataAverage(dataNode, col, numRows)/1000));
            }
        }
    }

    ......

    return vmResponseMap;
}

至此,全部逻辑处理完毕,XenServer 的 RRD 格式解析,日后会发文详解。 值得注意的是:getDataAverage(),看名字则可知其实逻辑是获取平均值。因为XenServer的RRD并不记录某段时间内的统计量,而是记录某一时刻的瞬时值。所以XenServer中,以此逻辑进行了处理,获取了该段时间内的全部瞬时值,然后进行处理得到近似这段时间内数据总量的统计值。

VMware

逻辑类似于XenServer,只看getVmStats() 方法

private HashMap<String, VmStatsEntry> getVmStats(List<String> vmNames) throws Exception {

    ......

    List<PerfCounterInfo> cInfo = getServiceContext().getVimClient().getDynamicProperty(perfMgr, "perfCounter");

    ......

    int key = ((HostMO)hyperHost).getCustomFieldKey("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME);

    ......

    String instanceNameCustomField = "value[" + key + "]";
    ObjectContent[] ocs =
            hyperHost.getVmPropertiesOnHyperHost(new String[] {"name", "summary.config.numCpu", "summary.quickStats.overallCpuUsage", instanceNameCustomField});
    if (ocs != null && ocs.length > 0) {
        for (ObjectContent oc : ocs) {
            List<DynamicProperty> objProps = oc.getPropSet();
            if (objProps != null) {
                ......

                for (DynamicProperty objProp : objProps) {
                    if (objProp.getName().equals("name")) {
                        vmNameOnVcenter = objProp.getVal().toString();
                    } else if (objProp.getName().contains(instanceNameCustomField)) {
                        if (objProp.getVal() != null)
                            vmInternalCSName = ((CustomFieldStringValue)objProp.getVal()).getValue();
                    } else if (objProp.getName().equals("summary.config.numCpu")) {
                        numberCPUs = objProp.getVal().toString();
                    } else if (objProp.getName().equals("summary.quickStats.overallCpuUsage")) {
                        maxCpuUsage = objProp.getVal().toString();
                    }
                }
                new VirtualMachineMO(hyperHost.getContext(), oc.getObj());

                ......

                ManagedObjectReference vmMor = hyperHost.findVmOnHyperHost(name).getMor();

                ......

                if (vmNetworkMetrics.size() != 0) {

                    ......

                    for (int i = 0; i < values.size(); ++i) {
                        List<PerfSampleInfo> infos = ((PerfEntityMetric)values.get(i)).getSampleInfo();
                        if (infos != null && infos.size() > 0) {

                            ......

                            List<PerfMetricSeries> vals = ((PerfEntityMetric)values.get(i)).getValue();
                            for (int vi = 0; ((vals != null) && (vi < vals.size())); ++vi) {
                                if (vals.get(vi) instanceof PerfMetricIntSeries) {
                                    PerfMetricIntSeries val = (PerfMetricIntSeries)vals.get(vi);
                                    List<Long> perfValues = val.getValue();
                                    Long sumRate = 0L;
                                    for (int j = 0; j < infos.size(); j++) { // Size of the array matches the size as the PerfSampleInfo
                                        sumRate += perfValues.get(j);
                                    }

                                    Long averageRate = sumRate / infos.size();
                                    if (vals.get(vi).getId().getCounterId() == rxPerfCounterInfo.getKey()) {
                                        networkReadKBs = sampleDuration * averageRate; //get the average RX rate multiplied by sampled duration
                                    }

                                    if (vals.get(vi).getId().getCounterId() == txPerfCounterInfo.getKey()) {
                                        networkWriteKBs = sampleDuration * averageRate;//get the average TX rate multiplied by sampled duration
                                    }
                  ......

    return vmResponseMap;
}

由逻辑可见,Vmware也是获取平均值,然后乘相关时间,得到近似这段时间内数据总量的统计值。

KVM

逻辑类似于XenServer,只看getVmStats() 方法

VmStatsEntry getVmStat(Connect conn, String vmName) throws LibvirtException {

    ......

        if (oldStats != null) {
            elapsedTime = now.getTimeInMillis() - oldStats._timestamp.getTimeInMillis();
            double utilization = (info.cpuTime - oldStats._usedTime) / ((double)elapsedTime * 1000000);

            utilization = utilization / node.cpus; //获取CPU使用值的平均值

            ......

        }

        .....

        for (InterfaceDef vif : vifs) {
            DomainInterfaceStats ifStats = dm.interfaceStats(vif.getDevName());
            rx += ifStats.rx_bytes;
            tx += ifStats.tx_bytes;
        }

        if (oldStats != null) {
            double deltarx = rx - oldStats._rx;

            ......

            double deltatx = tx - oldStats._tx;

            ......

        }

        /* get disk stats */
        ......

        for (DiskDef disk : disks) {
            DomainBlockStats blockStats = dm.blockStats(disk.getDiskLabel());
            io_rd += blockStats.rd_req;
            io_wr += blockStats.wr_req;
            bytes_rd += blockStats.rd_bytes;
            bytes_wr += blockStats.wr_bytes;
        }

        if (oldStats != null) {
            long deltaiord = io_rd - oldStats._ioRead;
            ......
            long deltaiowr = io_wr - oldStats._ioWrote;
            ......
            double deltabytesrd = bytes_rd - oldStats._bytesRead;
            ......
            double deltabyteswr = bytes_wr - oldStats._bytesWrote;
            ......
        }
        ......

        return stats;

    ......
}

看代码逻辑,则可以得知,KVM得到的是准确的当前时间段内的磁盘和网络IO量。