[Ovirt 노트] 계기판의 실현 분석과 정리

21195 단어
설명
우리 중의 한 사람으로서 끊임없는 학습이 필요합니다. 저는 업무의 여가 시간에 분석과 정리, 학습 노트를 블로그로 작성하여 여러분과 함께 교류하고 이런 방식으로 자신의 학습 여행을 기록하고 싶습니다.
본고는 학습 교류에만 사용되기 때문에 권리 침해는 반드시 삭제해야 한다.상업 목적에 사용하지 않으니 전재는 출처를 밝혀 주십시오.
분석 및 정리된 버전은 Ovirt 4.2.3 버전입니다.
  • ovirt-engine의 계기판은 플러그인을 통해 관리 인터페이스에 불러옵니다.구체적인 실현 방식은 Plugin의 실현 분석과 정리를 참고할 수 있다.
  • 3.4.5의 플러그인은 AngularJS를 통해 구현됩니다.
  • 4.2.3의 플러그인은React를 통해 실현된다.소스 다운로드 주소(https://github.com/oVirt/ovirt-engine-dashboard)
  • 플러그인을 관리 인터페이스에 불러오는 것은 모두 같은 메커니즘을 사용했는데 그 중에서 JSNI가 자바와 자바스크립트의 상호 방법을 호출하는 데 사용되었다.

  • PluginDefinitions클래스는 플러그인 프로필의 정보 읽기(계기판의 프로필 위치/usr/share/ovirt-engine/ui-plugins/dashboard.json)를 실현했다.
  • [root@localhost ui-plugins]# cat dashboard.json 
    {
      "name": "dashboard",
      "url": "plugin/dashboard/plugin.html",
      "resourcePath": "dashboard-resources",
      "lazyLoad": false
    }
    

    등록 정보
    설명
    name
    플러그인 이름
    url
    플러그인 포털 주소
    resourcePath
    자원 경로
    lazyLoad
    로드 지연 여부
  • PluginManager클래스는 플러그인 데이터 정보를 읽는 것을 실현했고 exposePluginApi방법을 통해Javascript를 호출하여pluginapi 대상을 만들었다.
  • 자바 방법과 자바스크립트 방법이 서로 호출되는 다리.


  • 1. 호출 실현
  • 대시보드 플러그인 항목에 HtmlWebpackPlugin이 사용되어 지정된 템플릿에 따라 html을 생성할 수 있습니다.

  • 속성 구성
    설명
    title
    html 파일의 제목을 생성합니다.
    filename
    html 파일의 파일 이름입니다. 기본값은 index입니다.html.
    template
    템플릿 이름, 템플릿 유형은 html,jade,ejs 등이 가능합니다.
    inject
    true(기본값, script 라벨은 html 파일의 바디 밑에 있음), 바디(script 라벨은 html 파일의 바디 밑에 있음), 헤드(script 라벨은 html 파일의 헤드에 있음), false(생성된 js 파일을 삽입하지 않음)
    favicon
    favicon을 생성합니다. 값은 경로입니다.
    minify
    미니fy를 사용하여 생성된 html 파일을 압축합니다.기본값은 false입니다.
    cache
    기본true, 내용이 바뀔 때 새 파일을 생성합니다.
    showErrors
    웹 패키지가 잘못 보고되었을 때, 오류 정보를 pre에 감싸십시오. 기본값은true입니다.
    chunks
    여러 개의 입구 파일이 있고, 컴파일된 후에 여러 개의 포장된 파일을 생성하면,chunks는 어떤 js 파일을 사용할 것인지 선택할 수 있습니다.기본적으로 모두 표시됩니다.
    excludeChunks
    일부 js를 배제합니다.
    xhtml
    xhtml 모드 인용 파일을 호환할지 여부입니다.기본값은 false입니다.
    chunksSortMode
    script의 순서,none,auto,dependency,{function}
  • plugin을 사용합니다.template.ejs 템플릿 파일 생성plugin.html 입구 파일.
  • 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 
    
    
    
      
    
    
    
    
    
  • plugin을 로드합니다.js 파일.
  • 등록은 대시보드 탭의 내용을 로드합니다.
  • priority는 옵션 카드에 순서를 표시하고 -1은 맨 위에 있습니다.

  • 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'
          })
      }
    
    })
    
  • getPluginApiplugin-api.js 파일에 정의되어 있습니다.
  • pluginapi 대상은 PluginManager류의 exposePluginApi 방법에서 이미 실현되었다.

  • 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;
    
  • exposePluginApi방법에서 ready방법을 실현했고 실제PluginManager류의 pluginReady방법을 호출했다.
  • 최종적으로 집행된 것은 getPluginApiUiInit 방법이다.

  • // Indicates that the plugin is ready for use
    ready: function() {
          [email protected]::pluginReady(Ljava/lang/String;)(this.pluginName);
    },
    
    // Initialize the plugin once it's ready
    initPlugin(plugin);
    
    if (invokePlugin(plugin, "UiInit", null)) { //$NON-NLS-1$
    
  • UiInit 방법에main-tab을 불러왔습니다.html 페이지, 인터페이스에main-tab이 추가되었습니다.jsx.
  • main-tab.jsx 파일에 로드됨
  • Dashboard DataProvider 데이터 소스 파일
  • Global Dashboard 인터페이스에서 파일을 보여줍니다.


  • import DashboardDataProvider from './components/DashboardDataProvider'
    import GlobalDashboard from './components/GlobalDashboard'
    

    1.1 파일 설명
    1.1.1 DashboardDataProvider
  • 데이터 원본 설정 대상을 포함한다.
  • servlet을 호출하여 전시 데이터를 가져옵니다.

  • _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 })
        })
      }
    
    
            dashboardData
            org.ovirt.engine.ui.frontend.server.dashboard.DashboardDataServlet
            100
    
    
            dashboardData
            /dashboard_data
    
    
  • DashboardDataServlet은 데이터 획득 개체를 보여줍니다.
  • 두 개의 스케줄링을 생성하고 데이터 캐시를 정시에 업데이트합니다.

  • /*
             * 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 데이터 소스 연결 가져오기
    @Resource(mappedName = "java:/DWHDataSource")
    private DataSource dwhDataSource;
    
    @Resource(mappedName = "java:/ENGINEDataSource")
    private DataSource engineDataSource;
    
  • 리소스 파일 구성에 따른 SQL 쿼리
  • 자원 파일 이름
    설명
    ClusterDwhDAO.properties
    dwh 라이브러리의 클러스터 관련 통계 SQL.
    ClusterEngineDAO.properties
    engin e라이브러리 클러스터 관련 통계 SQL.
    GlusterVolumeEngineDAO.properties
    engin e라이브러리의 볼륨 관련 통계 SQL.
    HostDwhDAO.properties
    dwh 라이브러리의 호스트 관련 통계 SQL.
    HostEngineDAO.properties
    engine 라이브러리의 호스트 관련 통계 SQL.
    StorageDomainDwhDAO.properties
    dwh 라이브러리의 저장소 도메인 관련 통계 SQL입니다.
    StorageDomainEngineDAO.properties
    engin e라이브러리의 스토리지 도메인 관련 통계 SQL
    VmDwhDAO.properties
    dwh 라이브러리의 VM 관련 통계 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.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();
    }
    
  • VM 인벤토리
  • 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.3 글로벌 활용도
    1.1.3.1 CPU 및 메모리 정보
    HourlySummaryHelper.getCpuMemSummary(utilization, dwhDataSource);
    
  • 총 CPU 및 메모리
  • 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);
    }
    
  • CPU 및 메모리 시간 사용률
  • 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 cpuHistory = new ArrayList<>();
            List memHistory = new ArrayList<>();
            HostDwhDao dao = new HostDwhDao(dataSource);
            List 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);
    }
    
  • 총 가상 CPU 및 메모리
  • 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.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 getHourlyStorageHistory(DataSource dwhDataSource) throws DashboardDataException {
            List history = new ArrayList<>();
            StorageDomainDwhDao dao = new StorageDomainDwhDao(dwhDataSource);
            List usageList = dao.getHourlyStorageHistory();
            for (ResourceUsage usage: usageList) {
                //Transform GB to TB.
                history.add(new HistoryNode(usage.getEpoch(), usage.getStorageValue() / 1024));
            }
            return history;
    }
    
  • 스토리지 마지막 5분 사용 평균
  • 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.4 클러스터 활용도
  • CPU 및 메모리 최종 24시간 사용률
  • 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 averages = dao.getClusterCpuAndMemoryAverage();
            List cpu = new ArrayList<>();
            List 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 스토리지 활용도
  • 스토리지 최종 24시간 사용률
  • private static final String STORAGE_LAST24_AVERAGE = "storage.last24hours_average"; //$NON-NLS-1$
    
    public static List getStorage(DataSource dwhDataSource) throws DashboardDataException {
            List 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'
    
    {/* refresh buttons and last updated information label */}
    {/* inventory cards - match height of all of the card's titles and body */}
    { applySearch(webadminPlaces.dc, searchPrefixes.dc) }} onStatusCountClick={(statusItem) => { applySearch(webadminPlaces.dc, searchPrefixes.dc, [{ name: searchFields.status, values: statusItem.statusValues }]) }} />
    ......

    좋은 웹페이지 즐겨찾기