程序员Ovirt虚拟化技术

【Ovirt 笔记】仪表板的实现分析与整理

2018-08-22  本文已影响1人  58bc06151329

文前说明

作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。

本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。

分析整理的版本为 Ovirt 4.2.3 版本。

[root@localhost ui-plugins]# cat dashboard.json 
{
  "name": "dashboard",
  "url": "plugin/dashboard/plugin.html",
  "resourcePath": "dashboard-resources",
  "lazyLoad": false
}
属性 说明
name 插件名称
url 插件入口地址
resourcePath 资源路径
lazyLoad 是否延迟加载

1. 调用实现

配置属性 说明
title 生成 html 文件的标题。
filename html 文件的文件名,默认是 index.html。
template 模板名称,模板类型可以是 html、jade、ejs 等。
inject true(默认值,script 标签在 html 文件的 body 底部)、body( script 标签在 html 文件的 body 底部)、head(script 标签在 html 文件的 head 中)、false(不插入生成的 js 文件)
favicon 生成一个 favicon ,值是一个路径。
minify 使用 minify 对生成的 html 文件进行压缩。默认是 false。
cache 默认 true,内容变化的时候生成一个新的文件。
showErrors webpack 报错时,把错误信息包裹在一个 pre 中,默认是 true。
chunks 当有多个入口文件,编译后生成多个打包后的文件,那么 chunks 能选择使用哪些 js 文件。默认全部显示。
excludeChunks 排除掉一些 js。
xhtml 是否兼容 xhtml 模式引用文件。默认值是 false。
chunksSortMode script 的顺序,none、auto、dependency、 {function}
new HtmlWebpackPlugin({
      filename: 'main-tab.html',
      template: 'static/html/main-tab.template.ejs',
      inject: true,
      chunks: ['vendor', 'main-tab']
}),
new HtmlWebpackPlugin({
      filename: 'plugin.html',
      template: 'static/html/plugin.template.ejs',
      inject: true,
      chunks: ['vendor', 'plugin']
}),
[root@localhost dashboard-resources]# cat plugin.html 
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
<script type="text/javascript" src="/ovirt-engine/webadmin/plugin/dashboard/js/plugin.db9ef782.js"></script></body>
</html>
getPluginApi().register({

      UiInit () {
      // add Dashboard main tab
      getPluginApi().addPrimaryMenuPlace(msg.mainTabTitle(), dashboardPlaceToken, `${pluginBasePath}/main-tab.html`, {
      // position this tab before any standard ones
      priority: -1,
      // customize the prefix displayed in search bar
      searchPrefix: 'Dashboard',
      defaultPlace: true,
      icon: 'fa-tachometer'
      })
  }

})
const getPluginApi = () => {
  api = api || getWebAdminWindow().pluginApi(pluginName)
  return api
}
pluginApi.fn = pluginApi.prototype = {

     pluginName: null, // Initialized in constructor function

     // Constructor function
     init: function(pluginName) {
        this.pluginName = pluginName;
        return this;
      },
......

// Give init function the pluginApi prototype for later instantiation
pluginApi.fn.init.prototype = pluginApi.fn;
// Indicates that the plugin is ready for use
ready: function() {
      ctx.@org.ovirt.engine.ui.webadmin.plugin.PluginManager::pluginReady(Ljava/lang/String;)(this.pluginName);
},
// Initialize the plugin once it's ready
initPlugin(plugin);
if (invokePlugin(plugin, "UiInit", null)) { //$NON-NLS-1$
import DashboardDataProvider from './components/DashboardDataProvider'
import GlobalDashboard from './components/GlobalDashboard'

1.1 文件说明

1.1.1 DashboardDataProvider

_fetchData () {
    const request = this._jqXHR = $.ajax({
      method: 'GET',
      url: `${getPluginApi().engineBaseUrl()}webadmin/dashboard_data`,
      dataType: 'json',
      headers: {
        'Accept': 'application/json'
        // For testing purposes you can uncomment either of these.
        // 'Prefer': 'fake_data' // returns randomly generated data
        // 'Prefer': 'error'     // triggers HTTP error response
      }
    })

    request.done((data) => {
      this._updateData({ data: this._transformData({ data }) })
    })

    request.fail(() => {
      console.error('Request failed', request)
      this._updateData({ data: DATA_ERROR })
    })
  }
<servlet>
        <servlet-name>dashboardData</servlet-name>
        <servlet-class>org.ovirt.engine.ui.frontend.server.dashboard.DashboardDataServlet</servlet-class>
        <load-on-startup>100</load-on-startup>
</servlet>
<servlet-mapping>
        <servlet-name>dashboardData</servlet-name>
        <url-pattern>/dashboard_data</url-pattern>
</servlet-mapping>
/*
         * Update the utilization cache now and every 5 minutes (by default) thereafter, but never run 2 updates simultaneously.
         */
        try {
            UTILIZATION_CACHE_UPDATE_INTERVAL = config.getLong(UTILIZATION_CACHE_UPDATE_INTERVAL_KEY);
        } catch (IllegalArgumentException e) {
            log.error("Missing/Invalid key \"{}\", using default value of 300", UTILIZATION_CACHE_UPDATE_INTERVAL_KEY, e); //$NON-NLS-1$
            UTILIZATION_CACHE_UPDATE_INTERVAL = 300;
        }
        utilizationCacheUpdate = scheduledExecutor.scheduleWithFixedDelay(new Runnable() {
            Logger log = LoggerFactory.getLogger(DashboardDataServlet.class.getName() + ".CacheUpdate.Utilization"); //$NON-NLS-1$

            @Override
            public void run() {
                log.trace("Attempting to update the Utilization cache"); //$NON-NLS-1$
                try {
                    populateUtilizationCache();
                } catch (DashboardDataException e) {
                    log.error("Could not update the Utilization Cache: {}", e.getMessage(), e); //$NON-NLS-1$
                }
            }
        }, 0, UTILIZATION_CACHE_UPDATE_INTERVAL, TimeUnit.SECONDS);
        log.info("Dashboard utilization cache updater initialized (update interval {}s)", UTILIZATION_CACHE_UPDATE_INTERVAL); //$NON-NLS-1$
try {
            INVENTORY_CACHE_UPDATE_INTERVAL = config.getLong(INVENTORY_CACHE_UPDATE_INTERVAL_KEY);
        } catch (IllegalArgumentException e) {
            log.error("Missing/Invalid key \"{}\", using default value of 60", INVENTORY_CACHE_UPDATE_INTERVAL_KEY, e); //$NON-NLS-1$
            INVENTORY_CACHE_UPDATE_INTERVAL = 60;
        }
        inventoryCacheUpdate = scheduledExecutor.scheduleWithFixedDelay(new Runnable() {
            Logger log = LoggerFactory.getLogger(DashboardDataServlet.class.getName() + ".CacheUpdate.Inventory"); //$NON-NLS-1$

            @Override
            public void run() {
                log.trace("Attempting to update the Inventory cache"); //$NON-NLS-1$
                try {
                    populateInventoryCache();
                } catch (DashboardDataException e) {
                    log.error("Could not update the Inventory Cache: {}", e.getMessage(), e); //$NON-NLS-1$
                }

            }
        }, 0, INVENTORY_CACHE_UPDATE_INTERVAL, TimeUnit.SECONDS);
        log.info("Dashboard inventory cache updater initialized (update interval {}s)", INVENTORY_CACHE_UPDATE_INTERVAL); //$NON-NLS-1$

1.1.1.1 获取数据源连接

@Resource(mappedName = "java:/DWHDataSource")
private DataSource dwhDataSource;

@Resource(mappedName = "java:/ENGINEDataSource")
private DataSource engineDataSource;
资源文件名称 说明
ClusterDwhDAO.properties dwh 库中集群相关统计 SQL。
ClusterEngineDAO.properties engine 库中集群相关统计 SQL。
GlusterVolumeEngineDAO.properties engine 库中卷相关统计 SQL。
HostDwhDAO.properties dwh 库中主机相关统计 SQL。
HostEngineDAO.properties engine 库中主机相关统计 SQL。
StorageDomainDwhDAO.properties dwh 库中存储域相关统计 SQL。
StorageDomainEngineDAO.properties engine 库中存储域相关统计 SQL。
VmDwhDAO.properties dwh 库中虚拟机相关统计 SQL。
VmEngineDAO.properties engine 库中虚拟机相关统计 SQL。
host.hourly_cpu_mem_history=SELECT \
    the_datetime AS the_date, \
    SUM(a.cpu_usage_per_host) / SUM(a.total_host_cpu_cores) AS cpu_avg, \
    SUM(a.memory_usage_per_host) / SUM(a.total_host_mem_avg) AS mem_avg \
FROM \
    ( \
      SELECT \
          date_trunc('hour',hourly.history_datetime) AS the_date, \
          hosts.host_id, \
          AVG(COALESCE(hourly.cpu_usage_percent, 0) * number_of_cores  ) AS cpu_usage_per_host, \
          AVG(COALESCE(hourly.memory_usage_percent, 0) * memory_size_mb  ) AS memory_usage_per_host , \
          AVG(COALESCE (hosts.number_of_cores , 0 )) AS total_host_cpu_cores, \
          AVG(COALESCE (hosts.memory_size_mb , 0 ) )AS total_host_mem_avg \
      FROM \
          v4_2_statistics_hosts_resources_usage_hourly hourly \
      INNER JOIN \
          v4_2_configuration_history_hosts hosts \
      ON \
          hosts.host_id = hourly.host_id \
      WHERE \
          /*Here we filter by active hosts only*/ \
          hourly.host_status = 1 AND \
          /*Here we join the configrations of the hosts with the statistics*/ \
          hourly.host_configuration_version = hosts.history_id AND \
          /*Here we filter by the last 24 hours period*/ \
          history_datetime >= date_trunc('hour',CURRENT_TIMESTAMP) - INTERVAL '24 hours' AND \
          history_datetime <= date_trunc('hour',CURRENT_TIMESTAMP) + INTERVAL '2 hours' \
      GROUP BY \
              hourly.history_datetime, hosts.host_id \
......

1.1.1.2 库存信息

private static final String DC_INVENTORY = "datacenter.inventory"; //$NON-NLS-1$

public static InventoryStatus getDcInventoryStatus(DataSource engineDataSource) throws DashboardDataException {
        DataCenterDao dao = new DataCenterDao(engineDataSource);
        return dao.getDcInventoryStatus();
}
private static final String CLUSTER_INVENTORY = "cluster.inventory"; //$NON-NLS-1$

public static InventoryStatus getClusterInventoryStatus(DataSource engineDataSource) throws DashboardDataException {
        ClusterEngineDao dao = new ClusterEngineDao(engineDataSource);
        return dao.getClusterInventorySummary();
}
private static final String HOST_INVENTORY = "host.inventory"; //$NON-NLS-1$

public static InventoryStatus getHostInventoryStatus(DataSource engineDataSource) throws DashboardDataException {
        HostEngineDao dao = new HostEngineDao(engineDataSource);
        return dao.getHostInventoryStatus();
}
private static final String STORAGE_INVENTORY = "storage.inventory"; //$NON-NLS-1$

public static InventoryStatus getStorageInventoryStatus(DataSource engineDataSource) throws DashboardDataException {
        StorageDomainEngineDao dao = new StorageDomainEngineDao(engineDataSource);
        return dao.getStorageInventoryStatus();
}
private static final String VM_INVENTORY = "vm.inventory"; //$NON-NLS-1$

public static InventoryStatus getVmInventorySummary(DataSource engineDataSource) throws DashboardDataException {
        VmEngineDao dao = new VmEngineDao(engineDataSource);
        return dao.getVmInventoryStatus();
}
private static final String GLUSTER_VOLUME_INVENTORY = "glusterVolume.inventory"; //$NON-NLS-1$

public static InventoryStatus getGlusterVolumeInventorySummary(DataSource engineDataSource)
            throws DashboardDataException {
        GlusterVolumeEngineDao dao = new GlusterVolumeEngineDao(engineDataSource);
        return dao.getVolumeInventoryStatus();
}

1.1.1.3 全局利用率

1.1.1.3.1 CPU 和内存信息
HourlySummaryHelper.getCpuMemSummary(utilization, dwhDataSource);
private static final String TOTAL_CPU_MEMORY_COUNT = "host.total_cpu_memory_count"; //$NON-NLS-1$

private static void getTotalCpuMemCount(GlobalUtilizationResourceSummary cpuSummary,
            GlobalUtilizationResourceSummary memSummary, DataSource dwhDataSource) throws DashboardDataException {
        HostDwhDao dao = new HostDwhDao(dwhDataSource);
        ResourcesTotal total = dao.getTotalCpuMemCount();
        cpuSummary.setPhysicalTotal(total.getCpuTotal());
        //Transform MB to GB.
        memSummary.setPhysicalTotal(total.getMemTotal() / 1024);
}
private static final String HOURLY_CPU_MEM_HISTORY = "host.hourly_cpu_mem_history"; //$NON-NLS-1$

private static void getHourlyCpuMemUsage(GlobalUtilizationResourceSummary cpuSummary,
            GlobalUtilizationResourceSummary memSummary, DataSource dataSource) throws DashboardDataException {
        List<HistoryNode> cpuHistory = new ArrayList<>();
        List<HistoryNode> memHistory = new ArrayList<>();
        HostDwhDao dao = new HostDwhDao(dataSource);
        List<ResourceUsage> history = dao.getHourlyCpuMemUsage();
        for (ResourceUsage item: history) {
            cpuHistory.add(new HistoryNode(item.getEpoch(), item.getCpuValue()));
            memHistory.add(new HistoryNode(item.getEpoch(), item.getMemValue() * memSummary.getTotal() / 100));
        }
        ResourceUsage last5minUsage = dao.getLast5MinCpuMemUsage();
        cpuSummary.setUsed(last5minUsage.getCpuValue());
        memSummary.setUsed(last5minUsage.getMemValue() * memSummary.getTotal() / 100);
        cpuSummary.setHistory(cpuHistory);
        memSummary.setHistory(memHistory);
}
private static final String VIRTUAL_CPU_MEMORY_COUNT = "vm.virtual_cpu_memory_count"; //$NON-NLS-1$

private static void getVirtualCpuMemCount(GlobalUtilizationResourceSummary cpuSummary,
            GlobalUtilizationResourceSummary memSummary, DataSource dwhDataSource) throws DashboardDataException {
        VmDwhDao dao = new VmDwhDao(dwhDataSource);
        ResourcesTotal resourcesTotal = dao.getVirtualCpuMemCount();
        cpuSummary.setVirtualTotal(resourcesTotal.getCpuTotal());
        cpuSummary.setVirtualUsed(resourcesTotal.getCpuUsed());
        memSummary.setVirtualTotal(resourcesTotal.getMemTotal());
        memSummary.setVirtualUsed(resourcesTotal.getMemUsed());
}
1.1.1.3.2 存储信息
utilization.setStorage(HourlySummaryHelper.getStorageSummary(dwhDataSource));
private static final String TOTAL_STORAGE_COUNT = "storage.total_count"; //$NON-NLS-1$

private static Double getTotalStorageCount(DataSource dwhDataSource) throws DashboardDataException {
        StorageDomainDwhDao dao = new StorageDomainDwhDao(dwhDataSource);
        //Transform GB to TB.
        return dao.getTotalStorageCount() / 1024;
}
private static final String HOURLY_STORAGE_HISTORY = "storage.hourly_history"; //$NON-NLS-1$

private static List<HistoryNode> getHourlyStorageHistory(DataSource dwhDataSource) throws DashboardDataException {
        List<HistoryNode> history = new ArrayList<>();
        StorageDomainDwhDao dao = new StorageDomainDwhDao(dwhDataSource);
        List<ResourceUsage> usageList = dao.getHourlyStorageHistory();
        for (ResourceUsage usage: usageList) {
            //Transform GB to TB.
            history.add(new HistoryNode(usage.getEpoch(), usage.getStorageValue() / 1024));
        }
        return history;
}
private static final String LAST5_MIN_STORAGE_AVERAGE = "storage.last5_minutes_average"; //$NON-NLS-1$

private static double getLast5MinutesStorageAverage(DataSource dwhDataSource) throws DashboardDataException {
        StorageDomainDwhDao dao = new StorageDomainDwhDao(dwhDataSource);
        //Transform GB to TB.
        return dao.getLast5MinutesStorageAverage() / 1024;
}

1.1.1.4 集群利用率

private static final String CLUSTER_LAST_24_AVERAGE = "cluster.last24hours"; //$NON-NLS-1$

public static void getCpuAndMemory(HeatMapData utilization, DataSource dataSource) throws DashboardDataException {
        ClusterDwhDao dao = new ClusterDwhDao(dataSource);
        List<ClusterResourceAverage> averages = dao.getClusterCpuAndMemoryAverage();
        List<HeatMapBlock> cpu = new ArrayList<>();
        List<HeatMapBlock> memory = new ArrayList<>();
        for (ClusterResourceAverage data: averages) {
            cpu.add(new HeatMapBlock(data.getName(), data.getCpuAverage()));
            memory.add(new HeatMapBlock(data.getName(), data.getMemoryAverage()));
        }
        utilization.setCpu(cpu);
        utilization.setMemory(memory);
}

1.1.1.5 存储利用率

private static final String STORAGE_LAST24_AVERAGE = "storage.last24hours_average"; //$NON-NLS-1$

public static List<HeatMapBlock> getStorage(DataSource dwhDataSource) throws DashboardDataException {
        List<HeatMapBlock> nodes = new ArrayList<>();
        StorageDomainDwhDao dao = new StorageDomainDwhDao(dwhDataSource);
        for (StorageDomainAverage data: dao.getStorageAverage()) {
            nodes.add(new HeatMapBlock(data.getName(), data.getValue()));
        }
        return nodes;
}

1.1.2 GlobalDashboard

import RefreshDataControl from './RefreshDataControl'
import LastUpdatedLabel from './LastUpdatedLabel'
import AggregateStatusCard from './AggregateStatusCard'
import UtilizationTrendCard from './UtilizationTrendCard'
import HeatMapLegend from './patternfly/HeatMapLegend'
import HeightMatching from './helper/HeightMatching'
<div className='container-fluid container-tiles-pf containers-dashboard'>

      {/* refresh buttons and last updated information label */}
      <div className='row row-tile-pf'>
        <div className='col-xs-12 global-dashboard-update-column'>
          <RefreshDataControl onRefresh={onRefreshData} />

          <div style={{ marginLeft: 10 }}>
            <LastUpdatedLabel date={lastUpdated} />
          </div>
        </div>
      </div>

      {/* inventory cards - match height of all of the card's titles and body */}
      <HeightMatching
        className={classNames('row', 'row-tile-pf', {'seven-cols': showGlusterCard})}
        selector={[
          '.card-pf-aggregate-status .card-pf-title',
          '.card-pf-aggregate-status .card-pf-body' ]}>

        <div className={statusCardClass}>
          <AggregateStatusCard
            data={inventory.dc}
            title={msg.statusCardDataCenterTitle()}
            mainIconClass='fa fa-building-o'
            onTotalCountClick={() => {
              applySearch(webadminPlaces.dc, searchPrefixes.dc)
            }}
            onStatusCountClick={(statusItem) => {
              applySearch(webadminPlaces.dc, searchPrefixes.dc, [{
                name: searchFields.status,
                values: statusItem.statusValues
              }])
            }} />
        </div>
......
上一篇下一篇

猜你喜欢

热点阅读