mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Monitoring] Monitoring eui (#27064)
* [Monitoring] Cluster alerts table to EUI (#26031) * Convert cluster alerts page to use EUI tables. Also adds baseline support for all monitoring tables * Fix tests * Remove these two files * Keep the original table but offer a new one so existing UIs still work * Use different base table controller for the EUI table * Use EUI specific asc and desc constants * [Monitoring] Elasticsearch monitoring to EUI (#26217) * Convert cluster alerts page to use EUI tables. Also adds baseline support for all monitoring tables * Fix tests * Remove these two files * Keep the original table but offer a new one so existing UIs still work * Use different base table controller for the EUI table * Use EUI specific asc and desc constants * Update summary status * ES nodes * Indices page * ML job listing * Fix tests up * Node listing page * Advanced node page * Advanced index * Fix tests * Fix onBrush * Cluster listing page * Update snapshots * Fix functional tests * Fix more tests * Remove commented out code * Update token API calls in elaticsearch.js (#26650) * make selection border 1px (#26739) * Reporting phantom and chromium tests should run in parallel. (#26566) * Reporting phantom and chromium tests should run in parallel. * Chromium tests should be done in group 9. * Attempting to use group 2 in ci for chromium tests. * X-pack CI jobs should have 7 groups. * Phantom tests should be in group 7. * [es-management/watcher] implement k7Breadcrumbs (#26719) * [timelion] implement k7Breadcrumbs (#26729) * [timelion] implement k7Breadcrumbs * [timelion] show "Create" breadcrumb by default * [es-management] implement k7Breadcrumbs (#26711) * [es-manaagement] implement k7Breadcrumbs * fix i18n ids * Fixes i18n issue in space nav selector (#26742) ## Summary Wraps the Spaces `NavControlPopover` in `<I18nProvider>`. Fixes #26736 * [Docs] Add beta flag to central management docs (#26558) * Feature/translate ml-jobs-jobs_list(part_1) (#25466) Translate ml -> jobsList(part_1) * Corrected wrong calls from .on to .off (#24575) Closing memory leak * Fix saved objects client _processBatchQueue function to handle errors (#26763) * Fix saved objects client _processBatchQueue function to handle errors * Fix error thrown in try/catch * chore(.gitignore): ignore sublime workspace files (#26516) * Map inspector requests by id so single requests can be reset at a time (#26770) * [ML] Implement k7 breadcrumbs for ML routes (#26774) * [ML] Implement k7 breadcrumbs for ML routes * [ML] Remove duplicate nouns from jobs and settings breadcrumbs * [Reporting] Re-enabled Chromium API tests (#26789) * [Reporting] Test logging * chromium api tests fixed * whitespace * Fix Elasticsearch typo on connection error screen (#26815) `Elastiscearch` -> `Elasticsearch`. * Reporting/reveal document bytes (#26667) * Adding a `size` property to all job-reporting meta-data and showing in reporting details pane * Enable heartbeat telemetry (#25886) This commit allows heartbeat telemetry data to be sent through kibana. The change to beats was introduced in https://github.com/elastic/beats/pull/8621 * Change 'Disenroll' text to be consistent with menu option 'Unenroll'. (#26816) * Upgrading sshpk (#26834) sshpk is an implicit dependency of request@2.88.0 * Re-enable the chromium functional tests (#26822) * Logging when max-bytes is larger than what's set in ES (#26482) * Simple check if ES body-size is smaller than KBN report size * [BeatsCM] Cleanup and refactor (#26636) * Refactor BeatsCM * update deps * update more deps * update for new EUI definitions * update import * Revert "update deps" This reverts commit759a14561d
. * use _source_includes * remove _source_includes * work-around due to watcher UI tests * Keep all xpack checks safe because we cant trust its there in tests for some reason * VALIDATION. This commit is to ensure the errors in CI are coming from beats * remove validation that this is a beats CM issue * More try/catch to try and find where this error is * testing another call * revert back to dangerouslyGetActiveInjector * ensure expire always is a number * fix swallowed error * Update x-pack/plugins/beats_management/public/lib/compose/kibana.ts Co-Authored-By: mattapperson <me@mattapperson.com> * Update x-pack/plugins/beats_management/public/utils/page_loader.test.ts Co-Authored-By: mattapperson <me@mattapperson.com> * Update x-pack/plugins/beats_management/public/utils/page_loader.ts Co-Authored-By: mattapperson <me@mattapperson.com> * fix for new webpack import * Fix translation map * fix URL path * fix other link * removing tag from beats via tag details screen now uses container * remove debug text * added comment/readme about routing on the client side * enrolled beat UI now works on overview screen * newly enrolled beat now reloads the beats table * fix TS errors * [APM] Transaction group agg size config (#26683) * [APM] Fixes #24204 by adding default configs to kibana.yml * [APM] fixes #25940 by adding APM config to control top transation group agg size * Revert the default configs added to kibana.yml and define joi validations for `xpack.apm.ui.transactionGroupBucketSize` * fix broken test for incorrect config * [APM] add docs entry for `xpack.apm.ui.transactionGroupBucketSize` * [APM] fixes #26784 by updateing import from a default to a named import (#26785) * allow disabling gpu in tests (#26684) * skipping failing tests (#26877) * Feature/translate ml - jobs(part 2) (#25528) Translate ml -> jobs - jobs-list(part_2) * use canvas pipeline in visualize (#25996) * Upgrade to NodeJS 10 (#25157) * feat(NA): upgrade node js version on file configs. * chore(NA): migrate configs and 3rd party dependencies to work on node js 10.x * fix(NA): add missing async function declaration. * chore(NA): updated elastic/good package to work with node10 * chore(NA): update lockfiles. * fix(NA): add missing dep. * fix(NA): types for node 10. * test(NA): fix error return type for node10. * fix(NA): kbn-pm webpack config to unlazy a require using lazy-cache. fix(NA): build to work with node 10. * test(NA): jest integration test for kbn-pluin-helpers. * test(NA): fix jest tests for kbn-es. * fix(NA): use ostmpdir instead of a tmp folder inside the fixtures. * fix(NA): change afterEach on kbn es decompress test. * fix(NA): change afterEach on kbn es decompress test. * fix(NA): readd mock-fs for the tests that still use it on kbn-es and that works on node10. * fix(NA): readd mock-fs for the tests that still use it on kbn-es and that works on node10. * refact(NA): rewrite tests using mock-fs and completely remove this dependency. * fix(NA): failing test implementation using jest mock in order to replace mock-fs. * fix(NA): update jest snapshots to match new ones generated one node 10. * fix(NA): cli/cluster mock to spyOn off method instead off spyOn removeListener as this was changed on Node 10. * fix(NA): tests for cluster_manager to also spyOn off and on instead of addListener and removeListener * test(NA): fix management advance settings image field test flow. * fix(NA): apply missing types for src/core/server/plugins/discovery/plugins_discovery.ts. * test(NA): updated 2 missing snapshots for KuiCodeEditor on kbn-ui-framework. * refact(NA): fix eslint errors. * refact(NA): fix ts code with tslint fix. chore(NA): update jest snapshots. * chore(NA): migrate kbn config schema peer dependency to last used joi version to avoid warning on bootstrap. * fix(NA): tslint errors. * chore(NA): upgrade types node to the last version. * fix(NA): missing utf8 input format encoding when reading a file. * chore(NA): upgrade to node 10.14.1 * fix(NA): Buffer api usage to avoid deprecation warnings. * fixing a11y errors so we can add a11y rules for tslint (#26902) * [DOCS] Deprecate X-Pack-centric watcher endpoints (#26899) * Index lifecycle (#25553) * [WIP] Index lifecycle (#25071) * Index lifecycle management wizard * Adding index lifecycle management files * Updates * Fix errors and add flyout for node details * New diff tool * Scroll to change for review diff * Some feedback on copy * Updating copy, moving components around and fixing bugs with the diff view * Add logic to auto enable a phase when something is set * redesign ilm * Adding server api tests * Removing debug and some tweaks from dave's work * Conditionally show this message * Policy selection cleanup * Updates for better UX * [COPYEDIT | ILM] Copyedit of text in index lifecycle management UI * Use better default text * Remove debug * Adding readme and comments * Update readme * Do not need this anymore * Remove debug or commented out code * Remove these - they are in the tests PR * Toggle system indices * Aliases are not defined here anymore * Handle rollover better in warm phase and remove from cold,delete * adding learn more link component and switching over to using that * fixing UI issue when no policies exist * various fixes * some cleanup * moving number_of_replicas due to API change * modifying some messaging * fixing typo * fixing some diff issues and not adding sattr_name if none chosen * making write alias required for template step and making necessary API changes * removing alias definition from template patching as it needs to be per index * some copy edits for clarity * fixing issue with editing existing policy when rollover starts the warm phase * addressing PR feedback on server side code * addressing PR feedback * removing additional spaces from findMatchingNodes call * changing template to index template in one more place * fixing issue with error message showing when bootstrap is successful * fixing node options for warm and cold phase * adding seconds to duration fields to match what ES supports * changing icon for enabled steps so it does not look like an error indicator * adjusting icon color for enabled lifecycle steps * fixing issue with editing an existing policy with warm phase on rollover enabled * fixing issue with default unit for age dropdowns * fixing issues with shrink action serialization and deserialization * fixing issue with deserialization of ES policy for shrink * removing shrink option from UI when primary shard count is not greater than 1 for hot phase * going straight to create policy when no policies exist * improving lifecycle policy selection * adding active badge instead of checkmark for active lifecycle policy phases * some cleanup of unneeded properties and only showing save as new when it is appropriate * removing stray fullWidth attribute * adding missing minimum for replica count for warm phase * adding scroll to top for review step * fixing issue with start warm phase after rollover introduced by time representaiton change from ES * making shrink options not show for primary shard count of 1 as you can't shrink in that situation * fixing issue with editing existing policy and saveAsNew * bare bones policy table implementation * implementing delete policy behavior * fixing sorting and paging * fixing policy table title * rudimentary navigation flow * fixing delete * Index lifecycle management wizard (#21925) * Index lifecycle management wizard * Adding index lifecycle management files * Updates * Fix errors and add flyout for node details * New diff tool * Scroll to change for review diff * Some feedback on copy * Updating copy, moving components around and fixing bugs with the diff view * Add logic to auto enable a phase when something is set * redesign ilm * Adding server api tests * Removing debug and some tweaks from dave's work * Conditionally show this message * Policy selection cleanup * Updates for better UX * [COPYEDIT | ILM] Copyedit of text in index lifecycle management UI * Use better default text * Remove debug * Adding readme and comments * Update readme * Do not need this anymore * Remove debug or commented out code * Remove these - they are in the tests PR * Toggle system indices * Aliases are not defined here anymore * Handle rollover better in warm phase and remove from cold,delete * adding learn more link component and switching over to using that * fixing UI issue when no policies exist * various fixes * some cleanup * moving number_of_replicas due to API change * modifying some messaging * fixing typo * fixing some diff issues and not adding sattr_name if none chosen * making write alias required for template step and making necessary API changes * removing alias definition from template patching as it needs to be per index * some copy edits for clarity * fixing issue with editing existing policy when rollover starts the warm phase * addressing PR feedback on server side code * addressing PR feedback * removing additional spaces from findMatchingNodes call * changing template to index template in one more place * fixing issue with error message showing when bootstrap is successful * fixing node options for warm and cold phase * adding seconds to duration fields to match what ES supports * changing icon for enabled steps so it does not look like an error indicator * adjusting icon color for enabled lifecycle steps * fixing issue with editing an existing policy with warm phase on rollover enabled * fixing issue with default unit for age dropdowns * fixing issues with shrink action serialization and deserialization * fixing issue with deserialization of ES policy for shrink * removing shrink option from UI when primary shard count is not greater than 1 for hot phase * going straight to create policy when no policies exist * improving lifecycle policy selection * adding active badge instead of checkmark for active lifecycle policy phases * some cleanup of unneeded properties and only showing save as new when it is appropriate * removing stray fullWidth attribute * adding missing minimum for replica count for warm phase * adding scroll to top for review step * fixing issue with start warm phase after rollover introduced by time representaiton change from ES * making shrink options not show for primary shard count of 1 as you can't shrink in that situation * fixing issue with editing existing policy and saveAsNew * adjusting to changes in ES API * adding version and modified date to policies table * implementing new CRUD approach * simplified delete * cleanup edit_policy * removed wizard code * fixing issue with edit policy * fixing issue with closing delete confirmation modal * making max age and max size not mutually exclusive * removing names of covered indices from policy table * changing minimum_age to min_age * first pass at index lifecycle extensions * adding retry button for ilm covered index that is in error * first pass at index lifecycle banner * i18n work * more i18n * fixing issue with node attributes * removing console.log statements * fixing issue with deserializing number_of_shards for edit policy * defaulting shrink to false and fixing ui spacing issue * removing hot phase shard count from warm phase * scrolling to first error when user submits form for edit policy * disabling UI for index management when enabled is false in kibana.yml * disabling index lifecycle management when enabled is false in kibana.yml * extending index management filter to allow for searching fields * add support for filtering to indices with errors for index lifecycle management banner * i18n work * fixing error wrappers * fixing tests * adding view JSON for index lifecycle policy on edit screen * fixing label for i18n on policy JSON flyout * removing console.log statements * fixing tests * removing console.log statements * adding key for banner extensions * fixing bad import for FormattedMessage * add link to edit index lifecycle policy from index management index summary * adding key for map of summary extensions * adding proper icon for retry lifecycle action * factoring out common min age component * factoring out common NodeAllocation component * add copy to clipboard for json policy flyout * adding validation rules for policy names to match ES rules * fixing issues with policy names with non-alphabetic characters like & ^ % ? * moving create policy button to top right and adding fill * adding better empty state for policy list * moving shrink and force merge titles and descriptions to the left * moving show JSON button to right and limiting width of JSON flyout * fixing warning about lifecycle prop type for JSON flyout * fixing issue with warning on prop isShowingErrors * removing outdated README * simplifying constants * moving components to more logical places * moving lib and api files to services * renaming provider to enricher * factoring out common data enricher behavior * consolidating index management extension code * removing unnecessary colon * Revert "removing unnecessary colon" This reverts commit19712807bb
. * removing unnecessary colon * adding callout to ilm summary for errors * better formatting for banner extensions * Revert "disabling UI for index management when enabled is false in kibana.yml" This reverts commit45d4e8c51d
. * removing unnecessary code for detecting disabled plugin * adding config prefix of xpack.index_lifecycle_management * making policy link use href and consolidating link encoding logic * removed outdated comment * removed outdated comment * better solution for policy name in URI * fixing issue with index management being disabled and index_lifecycle_management not * adding link to index management list filtered to policy name from index_lifecycle_management policy list * fixing tests * adding popover for stack trace for ilm errors * adding stack trace and phase definition popovers to ilm summary in index management * adding to blacklist for node attributes and not showing node allocation section when there are not any node attributes to choose from * not showing create policy button in upper right for empty state * moving policy name form field to right to be consistent with the rest of the form layout * moving save button to left and using secondary color * added copied to clipboard toast message * moving activate/deactivate buttons to left on edit policy page * deleting unnecessary less file * using spacer instead of style * translating success message for edit policy save * fixing missing props warnings for EuiDescribedFormGroup usages * better error handling borrowed from rollups * disabling delete when a policy is attached to indices * adding remove lifecycle policy action * fixing issue with remove ilm policy showing for non-managed indices` * adding add lifecycle policy action to index management extensions in index lifecycle management * adding confirm modal for remove lifecycle policy * fixing validation * fixing issue with back button and edit policy retaining old policy * removing console.log * making no policy modal for add lifecycle policy make more sense (no add button) * Calling reloadIndices when a lifecycle policy gets added or removed * fixing logic issue with spinner showing * refactoring confirm modal on policy list page * adding an add to index template button on policy list page * fixing console warning about select value being null * fixing issue with modal not opening from index management table manage menu * changing app title and adding i18n for it * more naming changes and adding beta badge * adding filter extension to index management and using it to add filters for index lifecycle management * fixing broken jest test * fixing issue with banners appearing/disappearing based on filters * adding xpack.ilm.ui.enabled to allow cloud to disable the ui * add ability to configure list of node attributes to ignore in kibana.yml for cloud * filtering out reserved system templates from fetch route list * adding warning when user tries to add a policy to a template that already has a policy * fixing a11y issues on edit policy form * incorporating docs team feedback on copy * adding learn more link to add policy to index template modal * fixing app order for management * fixing breadcrumb issue by adding redirect for BASE_PATH (and adding memory leak fix) * making version and covered indices column smaller and adding horizontal scroll and min width * right align actions and better width solution for columns * bigger spacer under callout for no node attributes * restricting width of edit page * fixing typo * removing unnecessary store code for index templates * fixing react warning about boolean type * moving beta badge in line with title on policies screen * better UI for show JSON for edit policy * commenting on the memory leak fix for React Router redirects * fixing fatal error with node allocation flyout * fixing issue with banner not showing * moving unmanaged/managed filter to filter group to make it clearer what they mean * removing unused code * copy changes * adding context menu for policy table instead of icon buttons * adding fix errors badge for phases * removing unnecessary close button in flyout footer * adding spinner when nodeOptions are not present * moving view a list button below input * adding more explanation to add policy to index template modal * adding documentation link for main ILM docs in ES * only showing view nodes link when node attributes are selected * removing colon from flyout title * fixing layout for view nodes button for attributes * making loading spinner larger for node attributes select * fixing issue with button going off end of table * removing title from empty prompt for policy table * fixing max width for edit policy page * copy edits * don't show pager when number of policies is less than minimum page size * making number of replicas optional and adding optional label * fixing sort for policy table * fixing flicker for node allocation * removing redundant message for index policies defined * fixing spacing/alignment issues on error display for summary * fixing issues with pager not showing and controls disappearing when filters applied * adding tests for policy table * more test additions * making search bar incremental for index management * making JSON policy flyout show ES request JSON not internal representation * adding error message when user tries to submit add policy to index without selecting policy * adding validation for missing template on add policy to index template modal * adding tests for ilm index management extensions * adding tests for edit policy * removing learn about node attributes link until docs come * fixing prop type warning * adding missing translations * better tests for edit policy * adding tests for node attribute inputs * better tests for node attributes * fixing policy table test * fixing bad i18n id * updating snapshot * [Telemetry] Pull local Kibana usage stats (#26496) * add kibana stats * fix tests * format the stats for telemetry * fix the os/platform stats * add version to locally-source kibana telemetry stats * use callWithInternalUser * better get_kibana module unit test verification * separate handleKibanaStats * variable rename * fix comment * fix functional test * keep the return object literal from handleLocalStats * validate the payload fields * add warning log if no kibana stats returned * add missing apm-server response error monitor (#26570) * [DOCS] Deprecate /_xpack/security in favor of /_security (#26897) * fix ems hotlink (#26868) * Initialize authorization mode for reporting jobs (#26762) * wrap non error in a try/catch (#26898) * fix(NA): change kbn pm webpack config to generate dist files in mode=none. (#26847) * Fail out of auth flow on first provider failure (#26648) In practical terms, the flexibility afforded by providers being able to recover from the failures of previously configured providers isn't compelling, but the ambiguity is not ideal. * fixing a11y errors so we can add a11y rules to tslint (#26895) * EUI 5.6.0 (#26839) * eui 5.6.0 * Import IconType in infra/types/eui.d.ts * fixing interpreter socket error (#26870) * fixes split chart with no data (#26872) * fixing tooltips for line chart (#26881) * Make space selector a button (#26889) * [ML] Adds isRequired where applicable to timeseries_chart props. (#26880) Adds isRequired to timeseries_chart props to match the minimum required props necessary to render the component without errors reflected in the Minimal initialization test. * Translate share (#26802) * Only show change password form when a password change is possible (#26779) * only show change password form when a password change is possible * cleanup * remove test code * improved message * [Beats CM] Add basic license type (#26935) * Improve wording when creating a space (#26915) * copy tweaks * update save space toast * adjust save toast * fixing issue with multiple execution in console (#26933) * fixing a11y error (#26906) * [i18n] Translate ML - File Datavisualizer (Part 1) (#25641) translate file_datavisualizer folder of Machine Learning (Part 1) * Use new _graph endpoints (#26956) * [ML] Do not pass datafeed query to Discover in custom URL (#26957) * quick ILM fixes (#26966) * fix fatal IE 11 error with undefined TextEncoder * fixing validation issue with hot phase * fixing double scroll bar on IE11 : * Fixing a11y errors in querybar and suggestion_component (#26892) * correcting a11y errors so that we can add a11y rules to tslint * updating the jest snapshot * updating the jest snapshot * do not pass 'sortOrder' to EuiContextMenuItem in share context menu (#26890) * do not pass 'sortOrder' to EuiContextMenuItem in share context menu * add unit test for sortOrder * avoid using lodash * fix merge conflicts with internationization PR * Feat: Workpad Templates (#23966) * Added workpad manager which contains workpad_loader and workpad_templates * Fixed term filter in workpad_templates * design changes * Removed console logs Closes workpad manager modal after cloning template Fixed filtering workpad templates Removed console log Added sample templates Added more templates to test with Removed cloneDeep * case insensitive template search * Case insensitive tag order in popover * added descriptions and tags to sample data workpads * refine list of initial templates * remove sample data templates, make buttons bigger * Added template and tag registries * Fixed workpad loader resizing issue on home page * Moved tags to ui folder * Fixed template class * Fixed properties in templates to match workpad * fix lint errors (#26985) * Fix: Support columns with dots (#26659) Closes https://github.com/elastic/kibana/issues/26405 Upgrades tinymath so that columns with dots now work correctly. --- To test, follow the details in #26405. 1. Get some data that has fields with dots in them. Beats data would work, or you can create a test index with just 2 documents as follows: ``` POST test/test { "string":"abc", "with.dot":"abc" } POST test/test { "string":"abcd", "with.dot":"abcd" } ``` 2. Create a pie chart, splitting labels on one of the available fields. Previously, given the POST info above, only `string` would work correctly. Now both (and both of their `.keyword` variants) work correctly.  * upgrade resize-observer-polyfill version (#26990) * Fixing issues with the url.search being null in Node 10 (#26992) Node 10 uses `null` to denote the non-existence of a querystring parameter when `url.parse(urlString, true)` is used, the following changes fix our usages within the security plugin. * Hide logs from deleteAll on task: clean client modules into dll (#26884) * refact(NA): deleteAll function in order to allow it to not log anything out. * fix(NA): add missing no op debug and verbose functions. * refact(NA): wrap log calls into if calls. * [APM] Fix for library frames not collapsing (#26827) * [APM] fixes #26525 - simplified the stackframe grouping algorithm - add support for `stackframe.exclude_from_grouping` - made the rendering more tolerant of edge cases * Made improvements to code readability and added more meaningful test cases * [i18n] Translate untranslated labels (#26416) * Translate some missing translations * Fix issues * Add topNavMenu translations * Fix issues * Fix topNav * Fix issues * Fix issues * Fix kbnTopNav test and parametrs description * [ML] Fix word break in anomalies and jobs tables (#26978) * fixes other bucket request (#26874) * [I18n] Register translations before plugins init (#26078) * Register translations before plugins init * Fix i18n engine initialization * Fix translationPath$ RxJS pipeline * Move translations registration to mixin * Fix arrays concatenation * Use prettier * Fix translations relative paths * Use globby instead of glob * Update docs * Move globby to dependencies * Get rid of translation directories config * Update globby patterns * Search only for current locale translation files * [Infra UI] Fix styling after breaking EUI changes (#27021) This restores the Infrastructure and Logs UIs after upstream changes: * The theme json import now behaves like a ES6 module. * The `<EuiHeaderSection>` now requires the `grow` prop to be `true` in order to grow horizontally. * [i18n] Optimize translation labels for Roles page (#26945) * [i18n] Optimize translation labels * Usage of ng-if instead of ng-show * Update APM readme * [APM] Convert errors API to typescript (#26801) * [I18n] Allow i18n filter usage outside of interpolation expressions (#26803) * [I18n] Allow i18n filter usage outside of interpolation expressions * Remove redundant quotes from translation * Update tests * Resolve comments * Fix wrong filter usage * Introduce `recordOf` schema. Remove redundant declarations. (#26952) * [Infra UI] Fix graphql type generation after package upgrades (#26991) This fixes the infrastructure UI graphql type generation after relevant packages have been upgraded in #25157. * [Monitoring] APM monitoring to EUI (#26344) * Convert cluster alerts page to use EUI tables. Also adds baseline support for all monitoring tables * Fix tests * Remove these two files * Keep the original table but offer a new one so existing UIs still work * Use different base table controller for the EUI table * Use EUI specific asc and desc constants * Update summary status * ES nodes * Indices page * ML job listing * Fix tests up * Node listing page * Advanced node page * Advanced index * Fix tests * Fix onBrush * Cluster listing page * Overview page * Update snapshots * Fix functional tests * Beats instances * Fix more tests * Update cluster tests * Logstash UIs * Logstash tests * APM pages * [Monitoring] Beats monitoring to EUI (#26287) * Convert cluster alerts page to use EUI tables. Also adds baseline support for all monitoring tables * Fix tests * Remove these two files * Keep the original table but offer a new one so existing UIs still work * Use different base table controller for the EUI table * Use EUI specific asc and desc constants * Update summary status * ES nodes * Indices page * ML job listing * Fix tests up * Node listing page * Advanced node page * Advanced index * Fix tests * Fix onBrush * Cluster listing page * Overview page * Update snapshots * Fix functional tests * Beats instances * Fix more tests * Update cluster tests * [Monitoring] Kibana monitoring to EUI (#26361) * Convert cluster alerts page to use EUI tables. Also adds baseline support for all monitoring tables * Fix tests * Remove these two files * Keep the original table but offer a new one so existing UIs still work * Use different base table controller for the EUI table * Use EUI specific asc and desc constants * Update summary status * ES nodes * Indices page * ML job listing * Fix tests up * Node listing page * Advanced node page * Advanced index * Fix tests * Fix onBrush * Cluster listing page * Overview page * Update snapshots * Fix functional tests * Beats instances * Fix more tests * Update cluster tests * Logstash UIs * Logstash tests * APM pages * Kibana pages * [Monitoring] Logstash monitoring to EUI (#26298) * Convert cluster alerts page to use EUI tables. Also adds baseline support for all monitoring tables * Fix tests * Remove these two files * Keep the original table but offer a new one so existing UIs still work * Use different base table controller for the EUI table * Use EUI specific asc and desc constants * Update summary status * ES nodes * Indices page * ML job listing * Fix tests up * Node listing page * Advanced node page * Advanced index * Fix tests * Fix onBrush * Cluster listing page * Overview page * Update snapshots * Fix functional tests * Beats instances * Fix more tests * Update cluster tests * Logstash UIs * Logstash tests * Add this translation back in * PR feedback
This commit is contained in:
parent
4d10fae8c7
commit
96338a7ea2
123 changed files with 3596 additions and 2659 deletions
|
@ -51,6 +51,8 @@ export const NORMALIZED_DERIVATIVE_UNIT = '1s';
|
|||
* Values for column sorting in table options
|
||||
* @type {number} 1 or -1
|
||||
*/
|
||||
export const EUI_SORT_ASCENDING = 'asc';
|
||||
export const EUI_SORT_DESCENDING = 'desc';
|
||||
export const SORT_ASCENDING = 1;
|
||||
export const SORT_DESCENDING = -1;
|
||||
|
||||
|
|
133
x-pack/plugins/monitoring/public/components/alerts/alerts.js
Normal file
133
x-pack/plugins/monitoring/public/components/alerts/alerts.js
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { capitalize } from 'lodash';
|
||||
import { formatDateTimeLocal } from '../../../common/formatting';
|
||||
import { formatTimestampToDuration } from '../../../common';
|
||||
import { CALCULATE_DURATION_SINCE, EUI_SORT_DESCENDING } from '../../../common/constants';
|
||||
import { mapSeverity } from './map_severity';
|
||||
import { Tooltip } from 'plugins/monitoring/components/tooltip';
|
||||
import { FormattedAlert } from 'plugins/monitoring/components/alerts/formatted_alert';
|
||||
import { EuiMonitoringTable } from 'plugins/monitoring/components/table';
|
||||
import { EuiHealth, EuiIcon } from '@elastic/eui';
|
||||
import { injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
const linkToCategories = {
|
||||
'elasticsearch/nodes': 'Elasticsearch Nodes',
|
||||
'elasticsearch/indices': 'Elasticsearch Indices',
|
||||
'kibana/instances': 'Kibana Instances',
|
||||
'logstash/instances': 'Logstash Nodes',
|
||||
};
|
||||
const getColumns = (kbnUrl, scope) => ([
|
||||
{
|
||||
name: 'Status',
|
||||
field: 'metadata.severity',
|
||||
sortable: true,
|
||||
render: severity => {
|
||||
const severityIcon = mapSeverity(severity);
|
||||
|
||||
return (
|
||||
<Tooltip text={severityIcon.title} placement="bottom" trigger="hover">
|
||||
<EuiHealth color={severityIcon.color} data-test-subj="alertIcon" aria-label={severityIcon.title}>
|
||||
{ capitalize(severityIcon.value) }
|
||||
</EuiHealth>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Resolved',
|
||||
field: 'resolved_timestamp',
|
||||
sortable: true,
|
||||
render: (resolvedTimestamp) => {
|
||||
const resolution = {
|
||||
icon: null,
|
||||
text: 'Not Resolved'
|
||||
};
|
||||
|
||||
if (resolvedTimestamp) {
|
||||
resolution.text = `${formatTimestampToDuration(resolvedTimestamp, CALCULATE_DURATION_SINCE)} ago`;
|
||||
} else {
|
||||
resolution.icon = <EuiIcon type="alert" size="m" aria-label="Not Resolved" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
{ resolution.icon } { resolution.text }
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Message',
|
||||
field: 'message',
|
||||
sortable: true,
|
||||
render: (message, alert) => (
|
||||
<FormattedAlert
|
||||
prefix={alert.prefix}
|
||||
suffix={alert.suffix}
|
||||
message={message}
|
||||
metadata={alert.metadata}
|
||||
changeUrl={target => {
|
||||
scope.$evalAsync(() => {
|
||||
kbnUrl.changePath(target);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'Category',
|
||||
field: 'metadata.link',
|
||||
sortable: true,
|
||||
render: link => linkToCategories[link] ? linkToCategories[link] : 'General'
|
||||
},
|
||||
{
|
||||
name: 'Last Checked',
|
||||
field: 'update_timestamp',
|
||||
sortable: true,
|
||||
render: timestamp => formatDateTimeLocal(timestamp)
|
||||
},
|
||||
{
|
||||
name: 'Triggered',
|
||||
field: 'timestamp',
|
||||
sortable: true,
|
||||
render: timestamp => formatTimestampToDuration(timestamp, CALCULATE_DURATION_SINCE) + ' ago'
|
||||
},
|
||||
]);
|
||||
|
||||
const AlertsUI = ({ alerts, angular, sorting, pagination, onTableChange, intl }) => {
|
||||
return (
|
||||
<EuiMonitoringTable
|
||||
className="alertsTable"
|
||||
rows={alerts}
|
||||
columns={getColumns(angular.kbnUrl, angular.scope)}
|
||||
sorting={{
|
||||
...sorting,
|
||||
sort: {
|
||||
...sorting.sort,
|
||||
field: 'metadata.severity',
|
||||
direction: EUI_SORT_DESCENDING,
|
||||
}
|
||||
}}
|
||||
pagination={pagination}
|
||||
search={{
|
||||
box: {
|
||||
incremental: true,
|
||||
placeholder: intl.formatMessage({
|
||||
id: 'xpack.monitoring.alerts.filterAlertsPlaceholder',
|
||||
defaultMessage: 'Filter Alerts…'
|
||||
})
|
||||
|
||||
},
|
||||
}}
|
||||
onTableChange={onTableChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Alerts = injectI18n(AlertsUI);
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { Alerts } from './alerts';
|
|
@ -12,7 +12,8 @@ import {
|
|||
EuiSpacer,
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiFlexGroup
|
||||
EuiFlexGroup,
|
||||
EuiPageContent
|
||||
} from '@elastic/eui';
|
||||
import { Status } from './status';
|
||||
|
||||
|
@ -46,13 +47,15 @@ export function ApmServerInstance({ summary, metrics, ...props }) {
|
|||
));
|
||||
|
||||
return (
|
||||
<EuiPage style={{ backgroundColor: 'white' }}>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<Status stats={summary}/>
|
||||
<EuiSpacer size="s"/>
|
||||
<EuiFlexGroup wrap>
|
||||
{charts}
|
||||
</EuiFlexGroup>
|
||||
<EuiPageContent>
|
||||
<Status stats={summary}/>
|
||||
<EuiSpacer size="s"/>
|
||||
<EuiFlexGroup wrap>
|
||||
{charts}
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
|
|
|
@ -29,7 +29,7 @@ function StatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Name',
|
||||
}),
|
||||
value: name,
|
||||
dataTestSubj: 'name'
|
||||
'data-test-subj': 'name'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -37,7 +37,7 @@ function StatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Output',
|
||||
}),
|
||||
value: output,
|
||||
dataTestSubj: 'output'
|
||||
'data-test-subj': 'output'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -45,7 +45,7 @@ function StatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Version',
|
||||
}),
|
||||
value: version,
|
||||
dataTestSubj: 'version'
|
||||
'data-test-subj': 'version'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -53,7 +53,7 @@ function StatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Uptime',
|
||||
}),
|
||||
value: formatMetric(uptime, 'time_since'),
|
||||
dataTestSubj: 'uptime'
|
||||
'data-test-subj': 'uptime'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -65,7 +65,7 @@ function StatusUI({ stats, intl }) {
|
|||
defaultMessage: '{timeOfLastEvent} ago' }, {
|
||||
timeOfLastEvent: formatTimestampToDuration(+moment(timeOfLastEvent), CALCULATE_DURATION_SINCE)
|
||||
}),
|
||||
dataTestSubj: 'timeOfLastEvent',
|
||||
'data-test-subj': 'timeOfLastEvent',
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -6,150 +6,126 @@
|
|||
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { MonitoringTable } from '../../table';
|
||||
import {
|
||||
KuiTableRowCell,
|
||||
KuiTableRow
|
||||
} from '@kbn/ui-framework/components';
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { uniq } from 'lodash';
|
||||
import { EuiMonitoringTable } from '../../table';
|
||||
import { EuiLink, EuiPage, EuiPageBody, EuiPageContent, EuiSpacer } from '@elastic/eui';
|
||||
import { Status } from './status';
|
||||
import { SORT_ASCENDING, SORT_DESCENDING, TABLE_ACTION_UPDATE_FILTER } from '../../../../common/constants';
|
||||
import { formatMetric } from '../../../lib/format_number';
|
||||
import { formatTimestampToDuration } from '../../../../common';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
|
||||
const filterFields = [ 'name', 'type', 'version', 'output' ];
|
||||
const columns = [
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.apm.instances.nameTitle', {
|
||||
name: i18n.translate('xpack.monitoring.apm.instances.nameTitle', {
|
||||
defaultMessage: 'Name'
|
||||
}),
|
||||
sortKey: 'name',
|
||||
sortOrder: SORT_ASCENDING
|
||||
field: 'name',
|
||||
render: (name, instance) => (
|
||||
<EuiLink
|
||||
href={`#/apm/instances/${instance.uuid}`}
|
||||
data-test-subj={`apmLink-${name}`}
|
||||
>
|
||||
{name}
|
||||
</EuiLink>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.apm.instances.outputEnabledTitle', {
|
||||
name: i18n.translate('xpack.monitoring.apm.instances.outputEnabledTitle', {
|
||||
defaultMessage: 'Output Enabled'
|
||||
}),
|
||||
sortKey: 'output'
|
||||
field: 'output'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.apm.instances.totalEventsRateTitle', {
|
||||
name: i18n.translate('xpack.monitoring.apm.instances.totalEventsRateTitle', {
|
||||
defaultMessage: 'Total Events Rate'
|
||||
}),
|
||||
sortKey: 'total_events_rate',
|
||||
secondarySortOrder: SORT_DESCENDING
|
||||
field: 'total_events_rate',
|
||||
render: value => formatMetric(value, '', '/s')
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.apm.instances.bytesSentRateTitle', {
|
||||
name: i18n.translate('xpack.monitoring.apm.instances.bytesSentRateTitle', {
|
||||
defaultMessage: 'Bytes Sent Rate'
|
||||
}),
|
||||
sortKey: 'bytes_sent_rate'
|
||||
field: 'bytes_sent_rate',
|
||||
render: value => formatMetric(value, 'byte', '/s')
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.apm.instances.outputErrorsTitle', {
|
||||
name: i18n.translate('xpack.monitoring.apm.instances.outputErrorsTitle', {
|
||||
defaultMessage: 'Output Errors'
|
||||
}),
|
||||
sortKey: 'errors'
|
||||
field: 'errors',
|
||||
render: value => formatMetric(value, '0')
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.apm.instances.lastEventTitle', {
|
||||
name: i18n.translate('xpack.monitoring.apm.instances.lastEventTitle', {
|
||||
defaultMessage: 'Last Event'
|
||||
}),
|
||||
sortKey: 'time_of_last_event'
|
||||
field: 'time_of_last_event',
|
||||
render: value => formatTimestampToDuration(+moment(value), 'since') + ' ago'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.apm.instances.allocatedMemoryTitle', {
|
||||
name: i18n.translate('xpack.monitoring.apm.instances.allocatedMemoryTitle', {
|
||||
defaultMessage: 'Allocated Memory'
|
||||
}),
|
||||
sortKey: 'memory'
|
||||
field: 'memory',
|
||||
render: value => formatMetric(value, 'byte')
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.apm.instances.versionTitle', {
|
||||
name: i18n.translate('xpack.monitoring.apm.instances.versionTitle', {
|
||||
defaultMessage: 'Version'
|
||||
}),
|
||||
sortKey: 'version'
|
||||
field: 'version'
|
||||
},
|
||||
];
|
||||
const instanceRowFactory = () => {
|
||||
return function KibanaRow(props) {
|
||||
const applyFiltering = filterText => () => {
|
||||
props.dispatchTableAction(TABLE_ACTION_UPDATE_FILTER, filterText);
|
||||
};
|
||||
|
||||
return (
|
||||
<KuiTableRow>
|
||||
<KuiTableRowCell>
|
||||
<div className="monTableCell__name">
|
||||
<EuiLink
|
||||
href={`#/apm/instances/${props.uuid}`}
|
||||
data-test-subj={`apmLink-${props.name}`}
|
||||
>
|
||||
{props.name}
|
||||
</EuiLink>
|
||||
</div>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
{props.output}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
{formatMetric(props.total_events_rate, '', '/s')}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
{formatMetric(props.bytes_sent_rate, 'byte', '/s')}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
{formatMetric(props.errors, '0')}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
{formatTimestampToDuration(+moment(props.time_of_last_event), 'since') + ' ago'}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
{formatMetric(props.memory, 'byte')}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
<EuiLink
|
||||
onClick={applyFiltering(props.version)}
|
||||
>
|
||||
{props.version}
|
||||
</EuiLink>
|
||||
</KuiTableRowCell>
|
||||
</KuiTableRow>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
function ApmServerInstancesUI({ apms, intl }) {
|
||||
export function ApmServerInstancesUI({ apms, intl }) {
|
||||
const {
|
||||
pageIndex,
|
||||
filterText,
|
||||
sortKey,
|
||||
sortOrder,
|
||||
onNewState,
|
||||
pagination,
|
||||
sorting,
|
||||
onTableChange,
|
||||
data
|
||||
} = apms;
|
||||
|
||||
const versions = uniq(data.apms.map(item => item.version)).map(version => {
|
||||
return { value: version };
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Status stats={apms.data.stats}/>
|
||||
<MonitoringTable
|
||||
className="apmInstancesTable"
|
||||
rows={apms.data.apms}
|
||||
pageIndex={pageIndex}
|
||||
filterText={filterText}
|
||||
sortKey={sortKey}
|
||||
sortOrder={sortOrder}
|
||||
onNewState={onNewState}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'xpack.monitoring.apm.instances.filterInstancesPlaceholder',
|
||||
defaultMessage: 'Filter Instances…'
|
||||
})}
|
||||
filterFields={filterFields}
|
||||
columns={columns}
|
||||
rowComponent={instanceRowFactory()}
|
||||
/>
|
||||
</div>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<Status stats={data.stats} />
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiMonitoringTable
|
||||
className="apmInstancesTable"
|
||||
rows={data.apms}
|
||||
columns={columns}
|
||||
sorting={sorting}
|
||||
pagination={pagination}
|
||||
search={{
|
||||
box: {
|
||||
incremental: true,
|
||||
placeholder: intl.formatMessage({
|
||||
id: 'xpack.monitoring.apm.instances.filterInstancesPlaceholder',
|
||||
defaultMessage: 'Filter Instances…'
|
||||
})
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
type: 'field_value_selection',
|
||||
field: 'version',
|
||||
name: 'Version',
|
||||
options: versions,
|
||||
multiSelect: 'or',
|
||||
}
|
||||
]
|
||||
}}
|
||||
onTableChange={onTableChange}
|
||||
/>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ function StatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Servers',
|
||||
}),
|
||||
value: total,
|
||||
dataTestSubj: 'total'
|
||||
'data-test-subj': 'total'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -37,7 +37,7 @@ function StatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Total Events',
|
||||
}),
|
||||
value: formatMetric(totalEvents, '0.[0]a'),
|
||||
dataTestSubj: 'totalEvents'
|
||||
'data-test-subj': 'totalEvents'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -49,7 +49,7 @@ function StatusUI({ stats, intl }) {
|
|||
defaultMessage: '{timeOfLastEvent} ago' }, {
|
||||
timeOfLastEvent: formatTimestampToDuration(+moment(timeOfLastEvent), CALCULATE_DURATION_SINCE)
|
||||
}),
|
||||
dataTestSubj: 'timeOfLastEvent',
|
||||
'data-test-subj': 'timeOfLastEvent',
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -12,7 +12,8 @@ import {
|
|||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPageBody,
|
||||
EuiPanel
|
||||
EuiPanel,
|
||||
EuiPageContent
|
||||
} from '@elastic/eui';
|
||||
import { Status } from '../instances/status';
|
||||
|
||||
|
@ -50,13 +51,15 @@ export function ApmOverview({
|
|||
));
|
||||
|
||||
return (
|
||||
<EuiPage style={{ backgroundColor: 'white' }}>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<Status stats={stats}/>
|
||||
<EuiSpacer size="s"/>
|
||||
<EuiFlexGroup wrap>
|
||||
{charts}
|
||||
</EuiFlexGroup>
|
||||
<EuiPageContent>
|
||||
<Status stats={stats}/>
|
||||
<EuiSpacer size="s"/>
|
||||
<EuiFlexGroup wrap>
|
||||
{charts}
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
import React from 'react';
|
||||
import { MonitoringTimeseriesContainer } from '../../chart';
|
||||
import { formatMetric } from '../../../lib/format_number';
|
||||
import { EuiFlexItem, EuiFlexGroup, EuiPage, EuiPageBody, EuiFlexGrid, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiFlexItem, EuiPage, EuiPageBody, EuiFlexGrid, EuiSpacer, EuiPageContent } from '@elastic/eui';
|
||||
import { injectI18n } from '@kbn/i18n/react';
|
||||
import { SummaryStatus } from '../../summary_status';
|
||||
|
||||
function BeatUi({ summary, metrics, intl, ...props }) {
|
||||
|
||||
|
@ -23,58 +24,41 @@ function BeatUi({ summary, metrics, intl, ...props }) {
|
|||
metrics.beat_handles,
|
||||
];
|
||||
|
||||
const wrapChild = ({ label, value, dataTestSubj }, index) => (
|
||||
<EuiFlexItem
|
||||
key={`summary-status-item-${index}`}
|
||||
grow={false}
|
||||
data-test-subj={dataTestSubj}
|
||||
>
|
||||
<EuiFlexGroup responsive={false} gutterSize="xs" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
{label ? label + ': ' : null}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<strong>{value}</strong>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
|
||||
const summarytStatsTop = [
|
||||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.nameLabel', defaultMessage: 'Name' }),
|
||||
value: summary.name,
|
||||
dataTestSubj: 'name'
|
||||
'data-test-subj': 'name'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.hostLabel', defaultMessage: 'Host' }),
|
||||
value: summary.transportAddress,
|
||||
dataTestSubj: 'host'
|
||||
'data-test-subj': 'host'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.versionLabel', defaultMessage: 'Version' }),
|
||||
value: summary.version,
|
||||
dataTestSubj: 'version'
|
||||
'data-test-subj': 'version'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.typeLabel', defaultMessage: 'Type' }),
|
||||
value: summary.type,
|
||||
dataTestSubj: 'type'
|
||||
'data-test-subj': 'type'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.outputLabel', defaultMessage: 'Output' }),
|
||||
value: summary.output,
|
||||
dataTestSubj: 'output'
|
||||
'data-test-subj': 'output'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.configReloadsLabel', defaultMessage: 'Config reloads' }),
|
||||
value: formatMetric(summary.configReloads, 'int_commas'),
|
||||
dataTestSubj: 'configReloads'
|
||||
'data-test-subj': 'configReloads'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.uptimeLabel', defaultMessage: 'Uptime' }),
|
||||
value: formatMetric(summary.uptime, 'time_since'),
|
||||
dataTestSubj: 'uptime'
|
||||
'data-test-subj': 'uptime'
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -82,63 +66,48 @@ function BeatUi({ summary, metrics, intl, ...props }) {
|
|||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.eventsTotalLabel', defaultMessage: 'Events total' }),
|
||||
value: formatMetric(summary.eventsTotal, 'int_commas'),
|
||||
dataTestSubj: 'eventsTotal'
|
||||
'data-test-subj': 'eventsTotal'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.eventsEmittedLabel', defaultMessage: 'Events emitted' }),
|
||||
value: formatMetric(summary.eventsEmitted, 'int_commas'),
|
||||
dataTestSubj: 'eventsEmitted'
|
||||
'data-test-subj': 'eventsEmitted'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.eventsDroppedLabel', defaultMessage: 'Events dropped' }),
|
||||
value: formatMetric(summary.eventsDropped, 'int_commas'),
|
||||
dataTestSubj: 'eventsDropped'
|
||||
'data-test-subj': 'eventsDropped'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.bytesSentLabel', defaultMessage: 'Bytes sent' }),
|
||||
value: formatMetric(summary.bytesWritten, 'byte'),
|
||||
dataTestSubj: 'bytesWritten'
|
||||
'data-test-subj': 'bytesWritten'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.handlesLimitSoftLabel', defaultMessage: 'Handles limit (soft)' }),
|
||||
value: formatMetric(summary.handlesSoftLimit, 'byte'),
|
||||
dataTestSubj: 'handlesLimitSoft'
|
||||
'data-test-subj': 'handlesLimitSoft'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({ id: 'xpack.monitoring.beats.instance.handlesLimitHardLabel', defaultMessage: 'Handles limit (hard)' }),
|
||||
value: formatMetric(summary.handlesHardLimit, 'byte'),
|
||||
dataTestSubj: 'handlesLimitHard'
|
||||
'data-test-subj': 'handlesLimitHard'
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="monSummaryStatus" role="status">
|
||||
<div {...props}>
|
||||
<EuiFlexGroup gutterSize="none" alignItems="center" data-test-subj="beatSummaryStatus01">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup>
|
||||
{summarytStatsTop.map(wrapChild)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="monSummaryStatus" role="status">
|
||||
<div {...props}>
|
||||
<EuiFlexGroup gutterSize="none" alignItems="center" data-test-subj="beatSummaryStatus02">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup>
|
||||
{summarytStatsBot.map(wrapChild)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<EuiPage style={{ backgroundColor: 'white' }}>
|
||||
<EuiPageBody>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<SummaryStatus
|
||||
metrics={summarytStatsTop}
|
||||
data-test-subj="beatSummaryStatus01"
|
||||
/>
|
||||
<SummaryStatus
|
||||
metrics={summarytStatsBot}
|
||||
data-test-subj="beatSummaryStatus02"
|
||||
/>
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiFlexGrid columns={2} gutterSize="none">
|
||||
{metricsToShow.map((metric, index) => (
|
||||
<EuiFlexItem key={index} style={{ width: '50%' }}>
|
||||
|
@ -150,9 +119,9 @@ function BeatUi({ summary, metrics, intl, ...props }) {
|
|||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGrid>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</div>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { Listing } from './listing';
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { uniq } from 'lodash';
|
||||
import { EuiPage, EuiPageBody, EuiPageContent, EuiSpacer, EuiLink } from '@elastic/eui';
|
||||
import { Stats } from 'plugins/monitoring/components/beats';
|
||||
import { formatMetric } from 'plugins/monitoring/lib/format_number';
|
||||
import { EuiMonitoringTable } from 'plugins/monitoring/components/table';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
class ListingUI extends PureComponent {
|
||||
getColumns() {
|
||||
const { kbnUrl, scope } = this.props.angular;
|
||||
|
||||
return [
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.beats.instances.nameTitle', { defaultMessage: 'Name' }),
|
||||
field: 'name',
|
||||
render: (name, beat) => (
|
||||
<EuiLink
|
||||
onClick={() => {
|
||||
scope.$evalAsync(() => {
|
||||
kbnUrl.changePath(`/beats/beat/${beat.uuid}`);
|
||||
});
|
||||
}}
|
||||
data-test-subj={`beatLink-${name}`}
|
||||
>
|
||||
{name}
|
||||
</EuiLink>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.beats.instances.typeTitle', { defaultMessage: 'Type' }),
|
||||
field: 'type',
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.beats.instances.outputEnabledTitle', { defaultMessage: 'Output Enabled' }),
|
||||
field: 'output'
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.beats.instances.totalEventsRateTitle', { defaultMessage: 'Total Events Rate' }),
|
||||
field: 'total_events_rate',
|
||||
render: value => formatMetric(value, '', '/s')
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.beats.instances.bytesSentRateTitle', { defaultMessage: 'Bytes Sent Rate' }),
|
||||
field: 'bytes_sent_rate',
|
||||
render: value => formatMetric(value, 'byte', '/s')
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.beats.instances.outputErrorsTitle', { defaultMessage: 'Output Errors' }),
|
||||
field: 'errors',
|
||||
render: value => formatMetric(value, '0')
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.beats.instances.allocatedMemoryTitle', { defaultMessage: 'Allocated Memory' }),
|
||||
field: 'memory',
|
||||
render: value => formatMetric(value, 'byte')
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.beats.instances.versionTitle', { defaultMessage: 'Version' }),
|
||||
field: 'version',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
stats,
|
||||
data,
|
||||
sorting,
|
||||
pagination,
|
||||
onTableChange
|
||||
} = this.props;
|
||||
|
||||
|
||||
const types = uniq(data.map(item => item.type)).map(type => {
|
||||
return { value: type };
|
||||
});
|
||||
|
||||
const versions = uniq(data.map(item => item.version)).map(version => {
|
||||
return { value: version };
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<Stats stats={stats} />
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiMonitoringTable
|
||||
className="beatsTable"
|
||||
rows={data}
|
||||
columns={this.getColumns()}
|
||||
sorting={sorting}
|
||||
pagination={pagination}
|
||||
search={{
|
||||
box: {
|
||||
incremental: true,
|
||||
placeholder: i18n.translate('xpack.monitoring.beats.filterBeatsPlaceholder', { defaultMessage: 'Filter Beats...' }),
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
type: 'field_value_selection',
|
||||
field: 'type',
|
||||
name: 'Type',
|
||||
options: types,
|
||||
multiSelect: 'or',
|
||||
},
|
||||
{
|
||||
type: 'field_value_selection',
|
||||
field: 'version',
|
||||
name: 'Version',
|
||||
options: versions,
|
||||
multiSelect: 'or',
|
||||
}
|
||||
]
|
||||
}}
|
||||
onTableChange={onTableChange}
|
||||
/>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const Listing = injectI18n(ListingUI);
|
|
@ -3,260 +3,256 @@
|
|||
exports[`Overview that overview page renders normally 1`] = `
|
||||
<EuiPage
|
||||
restrictWidth={false}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "white",
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiPageBody
|
||||
restrictWidth={false}
|
||||
>
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={true}
|
||||
<EuiPageContent
|
||||
panelPaddingSize="l"
|
||||
>
|
||||
<EuiFlexItem
|
||||
<Stats
|
||||
stats={Array []}
|
||||
/>
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
component="div"
|
||||
grow={true}
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={true}
|
||||
>
|
||||
<EuiPanel
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<EuiTitle
|
||||
size="s"
|
||||
textTransform="none"
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
defaultMessage="Active Beats in Last Day"
|
||||
id="xpack.monitoring.beats.overview.activeBeatsInLastDayTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<InjectIntl(LatestActiveUi)
|
||||
latestActive={
|
||||
Array [
|
||||
Object {
|
||||
"count": 5,
|
||||
"range": "last1m",
|
||||
},
|
||||
Object {
|
||||
"count": 5,
|
||||
"range": "last5m",
|
||||
},
|
||||
Object {
|
||||
"count": 5,
|
||||
"range": "last20m",
|
||||
},
|
||||
Object {
|
||||
"count": 6,
|
||||
"range": "last1h",
|
||||
},
|
||||
Object {
|
||||
"count": 10,
|
||||
"range": "last1d",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
>
|
||||
<EuiPanel
|
||||
<EuiTitle
|
||||
size="s"
|
||||
textTransform="none"
|
||||
>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
defaultMessage="Active Beats in Last Day"
|
||||
id="xpack.monitoring.beats.overview.activeBeatsInLastDayTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<InjectIntl(LatestActiveUi)
|
||||
latestActive={
|
||||
Array [
|
||||
Object {
|
||||
"count": 5,
|
||||
"range": "last1m",
|
||||
},
|
||||
Object {
|
||||
"count": 5,
|
||||
"range": "last5m",
|
||||
},
|
||||
Object {
|
||||
"count": 5,
|
||||
"range": "last20m",
|
||||
},
|
||||
Object {
|
||||
"count": 6,
|
||||
"range": "last1h",
|
||||
},
|
||||
Object {
|
||||
"count": 10,
|
||||
"range": "last1d",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<EuiTitle
|
||||
size="s"
|
||||
textTransform="none"
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
defaultMessage="Top 5 Beat Types in Last Day"
|
||||
id="xpack.monitoring.beats.overview.top5BeatTypesInLastDayTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<LatestTypes
|
||||
latestTypes={
|
||||
Array [
|
||||
Object {
|
||||
"count": 4,
|
||||
"type": "Packetbeat",
|
||||
},
|
||||
Object {
|
||||
"count": 4,
|
||||
"type": "Metricbeat",
|
||||
},
|
||||
Object {
|
||||
"count": 2,
|
||||
"type": "Heartbeat",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
>
|
||||
<EuiPanel
|
||||
<EuiTitle
|
||||
size="s"
|
||||
textTransform="none"
|
||||
>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
defaultMessage="Top 5 Beat Types in Last Day"
|
||||
id="xpack.monitoring.beats.overview.top5BeatTypesInLastDayTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<LatestTypes
|
||||
latestTypes={
|
||||
Array [
|
||||
Object {
|
||||
"count": 4,
|
||||
"type": "Packetbeat",
|
||||
},
|
||||
Object {
|
||||
"count": 4,
|
||||
"type": "Metricbeat",
|
||||
},
|
||||
Object {
|
||||
"count": 2,
|
||||
"type": "Heartbeat",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<EuiTitle
|
||||
size="s"
|
||||
textTransform="none"
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
defaultMessage="Top 5 Versions in Last Day"
|
||||
id="xpack.monitoring.beats.overview.top5VersionsInLastDayTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<LatestVersions
|
||||
latestVersions={
|
||||
Array [
|
||||
Object {
|
||||
"count": 8,
|
||||
"version": "6.3.1",
|
||||
},
|
||||
Object {
|
||||
"count": 2,
|
||||
"version": "6.3.0",
|
||||
},
|
||||
]
|
||||
<EuiTitle
|
||||
size="s"
|
||||
textTransform="none"
|
||||
>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
defaultMessage="Top 5 Versions in Last Day"
|
||||
id="xpack.monitoring.beats.overview.top5VersionsInLastDayTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<LatestVersions
|
||||
latestVersions={
|
||||
Array [
|
||||
Object {
|
||||
"count": 8,
|
||||
"version": "6.3.1",
|
||||
},
|
||||
Object {
|
||||
"count": 2,
|
||||
"version": "6.3.0",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={true}
|
||||
>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="0"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<Stats
|
||||
stats={Array []}
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={true}
|
||||
>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="0"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="1"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="1"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="2"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="2"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="3"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="3"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
`;
|
||||
|
@ -264,121 +260,117 @@ exports[`Overview that overview page renders normally 1`] = `
|
|||
exports[`Overview that overview page shows a message if there is no beats data 1`] = `
|
||||
<EuiPage
|
||||
restrictWidth={false}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "white",
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiPageBody
|
||||
restrictWidth={false}
|
||||
>
|
||||
<EuiCallOut
|
||||
color="primary"
|
||||
data-test-subj="noRecentActivityMessage"
|
||||
iconType="gear"
|
||||
size="m"
|
||||
title="Hi there! This area is where your latest Beats activity would show up, but you don't seem to have any activity within the last day."
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<Stats
|
||||
stats={Array []}
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={true}
|
||||
<EuiPageContent
|
||||
panelPaddingSize="l"
|
||||
>
|
||||
<EuiFlexItem
|
||||
<Stats
|
||||
stats={Array []}
|
||||
/>
|
||||
<EuiCallOut
|
||||
color="primary"
|
||||
data-test-subj="noRecentActivityMessage"
|
||||
iconType="gear"
|
||||
size="m"
|
||||
title="Hi there! This area is where your latest Beats activity would show up, but you don't seem to have any activity within the last day."
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
component="div"
|
||||
grow={true}
|
||||
key="0"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
}
|
||||
}
|
||||
direction="row"
|
||||
gutterSize="l"
|
||||
justifyContent="flexStart"
|
||||
responsive={true}
|
||||
wrap={true}
|
||||
>
|
||||
<EuiPanel
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="1"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
key="0"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="2"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="1"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="3"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="2"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
key="3"
|
||||
style={
|
||||
Object {
|
||||
"minWidth": "45%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="m"
|
||||
>
|
||||
<InjectIntl(MonitoringTimeseriesContainerUI)
|
||||
series={1}
|
||||
/>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
`;
|
||||
|
|
|
@ -10,7 +10,6 @@ import { LatestVersions } from './latest_versions';
|
|||
import { LatestTypes } from './latest_types';
|
||||
import { Stats } from '../';
|
||||
import { MonitoringTimeseriesContainer } from '../../chart';
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
import {
|
||||
EuiCallOut,
|
||||
EuiTitle,
|
||||
|
@ -19,8 +18,10 @@ import {
|
|||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPageBody,
|
||||
EuiPanel
|
||||
EuiPanel,
|
||||
EuiPageContent
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
function renderLatestActive(latestActive, latestTypes, latestVersions, intl) {
|
||||
if (latestTypes && latestTypes.length > 0) {
|
||||
|
@ -78,6 +79,7 @@ function renderLatestActive(latestActive, latestTypes, latestVersions, intl) {
|
|||
defaultMessage: `Hi there! This area is where your latest Beats activity would show up, but you don't seem to have any activity within the last day.`
|
||||
});
|
||||
|
||||
|
||||
return (
|
||||
<EuiCallOut
|
||||
title={calloutMsg}
|
||||
|
@ -87,7 +89,7 @@ function renderLatestActive(latestActive, latestTypes, latestVersions, intl) {
|
|||
);
|
||||
}
|
||||
|
||||
function BeatsOverviewUi({
|
||||
export function BeatsOverviewUI({
|
||||
latestActive,
|
||||
latestTypes,
|
||||
latestVersions,
|
||||
|
@ -115,18 +117,19 @@ function BeatsOverviewUi({
|
|||
));
|
||||
|
||||
return (
|
||||
<EuiPage style={{ backgroundColor: 'white' }}>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
{renderLatestActive(latestActive, latestTypes, latestVersions, intl)}
|
||||
<EuiSpacer size="s"/>
|
||||
<Stats stats={stats} />
|
||||
<EuiSpacer size="s"/>
|
||||
<EuiFlexGroup wrap>
|
||||
{charts}
|
||||
</EuiFlexGroup>
|
||||
<EuiPageContent>
|
||||
<Stats stats={stats} />
|
||||
{renderLatestActive(latestActive, latestTypes, latestVersions, intl)}
|
||||
<EuiSpacer size="s"/>
|
||||
<EuiFlexGroup wrap>
|
||||
{charts}
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
|
||||
export const BeatsOverview = injectI18n(BeatsOverviewUi);
|
||||
export const BeatsOverview = injectI18n(BeatsOverviewUI);
|
||||
|
|
|
@ -4,80 +4,51 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import React from 'react';
|
||||
import { formatMetric } from 'plugins/monitoring/lib/format_number';
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { SummaryStatus } from '../summary_status';
|
||||
|
||||
export function Stats({ stats }) {
|
||||
const types = stats.types.map(({ type, count }, index) => {
|
||||
return (
|
||||
<EuiFlexItem
|
||||
key={`type-${index}`}
|
||||
data-test-subj="typeCount"
|
||||
data-test-type-count={type + ':' + count}
|
||||
grow={false}
|
||||
>
|
||||
<EuiFlexGroup responsive={false} gutterSize="xs" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
{type ? type + ': ' : null}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<strong>{formatMetric(count, 'int_commas')}</strong>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
const {
|
||||
total,
|
||||
types,
|
||||
stats: {
|
||||
bytesSent,
|
||||
totalEvents,
|
||||
}
|
||||
} = stats;
|
||||
|
||||
const metrics = [
|
||||
{
|
||||
label: 'Total Beats',
|
||||
value: formatMetric(total, 'int_commas'),
|
||||
'data-test-subj': 'totalBeats'
|
||||
},
|
||||
];
|
||||
|
||||
metrics.push(...types.map(({ type, count }) => ({
|
||||
label: type,
|
||||
value: formatMetric(count, 'int_commas'),
|
||||
'data-test-subj': 'typeCount',
|
||||
'data-test-type-count': `${type}:${count}`
|
||||
})));
|
||||
|
||||
metrics.push({
|
||||
label: 'Total Events',
|
||||
value: formatMetric(totalEvents, '0.[0]a'),
|
||||
'data-test-subj': 'totalEvents'
|
||||
});
|
||||
|
||||
metrics.push({
|
||||
label: 'Bytes Sent',
|
||||
value: formatMetric(bytesSent, 'byte'),
|
||||
'data-test-subj': 'bytesSent'
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="monSummaryStatus" role="status" data-test-subj="beatsSummaryStatus">
|
||||
|
||||
<EuiFlexGroup responsive={false} gutterSize="xs" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.beats.overview.totalBeatsLabel"
|
||||
defaultMessage="Total Beats"
|
||||
/>
|
||||
:
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<strong data-test-subj="totalBeats">
|
||||
{formatMetric(get(stats, 'total'), 'int_commas')}
|
||||
</strong>
|
||||
</EuiFlexItem>
|
||||
|
||||
{types}
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.beats.overview.totalEventsLabel"
|
||||
defaultMessage="Total Events"
|
||||
/>
|
||||
:
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<strong data-test-subj="totalEvents">
|
||||
{formatMetric(get(stats, 'stats.totalEvents'), '0.[0]a')}
|
||||
</strong>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.beats.overview.bytesSentLabel"
|
||||
defaultMessage="Bytes Sent"
|
||||
/>
|
||||
:
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<strong data-test-subj="bytesSent">
|
||||
{formatMetric(get(stats, 'stats.bytesSent'), 'byte')}
|
||||
</strong>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
</div>
|
||||
<SummaryStatus
|
||||
metrics={metrics}
|
||||
data-test-subj="beatsSummaryStatus"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ function StatusUI({ stat, formattedLeader, oldestStat, intl }) {
|
|||
defaultMessage: 'Follower Index',
|
||||
}),
|
||||
value: followerIndex,
|
||||
dataTestSubj: 'followerIndex'
|
||||
'data-test-subj': 'followerIndex'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -37,7 +37,7 @@ function StatusUI({ stat, formattedLeader, oldestStat, intl }) {
|
|||
defaultMessage: 'Shard Id',
|
||||
}),
|
||||
value: shardId,
|
||||
dataTestSubj: 'shardId'
|
||||
'data-test-subj': 'shardId'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -45,7 +45,7 @@ function StatusUI({ stat, formattedLeader, oldestStat, intl }) {
|
|||
defaultMessage: 'Leader Index',
|
||||
}),
|
||||
value: formattedLeader,
|
||||
dataTestSubj: 'leaderIndex'
|
||||
'data-test-subj': 'leaderIndex'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -53,7 +53,7 @@ function StatusUI({ stat, formattedLeader, oldestStat, intl }) {
|
|||
defaultMessage: 'Ops Synced',
|
||||
}),
|
||||
value: formatMetric(operationsReceived - oldestOperationsReceived, 'int_commas'),
|
||||
dataTestSubj: 'operationsReceived'
|
||||
'data-test-subj': 'operationsReceived'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -61,7 +61,7 @@ function StatusUI({ stat, formattedLeader, oldestStat, intl }) {
|
|||
defaultMessage: 'Failed Fetches',
|
||||
}),
|
||||
value: formatMetric(failedFetches - oldestFailedFetches, 'int_commas'),
|
||||
dataTestSubj: 'failedFetches'
|
||||
'data-test-subj': 'failedFetches'
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ function ClusterStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Nodes',
|
||||
}),
|
||||
value: nodesCount,
|
||||
dataTestSubj: 'nodesCount'
|
||||
'data-test-subj': 'nodesCount'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -38,7 +38,7 @@ function ClusterStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Indices',
|
||||
}),
|
||||
value: indicesCount,
|
||||
dataTestSubj: 'indicesCount'
|
||||
'data-test-subj': 'indicesCount'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -46,7 +46,7 @@ function ClusterStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Memory',
|
||||
}),
|
||||
value: formatMetric(memUsed, 'byte') + ' / ' + formatMetric(memMax, 'byte'),
|
||||
dataTestSubj: 'memory'
|
||||
'data-test-subj': 'memory'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -54,7 +54,7 @@ function ClusterStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Total Shards',
|
||||
}),
|
||||
value: totalShards,
|
||||
dataTestSubj: 'totalShards'
|
||||
'data-test-subj': 'totalShards'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -62,7 +62,7 @@ function ClusterStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Unassigned Shards',
|
||||
}),
|
||||
value: unassignedShards,
|
||||
dataTestSubj: 'unassignedShards'
|
||||
'data-test-subj': 'unassignedShards'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -70,7 +70,7 @@ function ClusterStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Documents',
|
||||
}),
|
||||
value: formatMetric(documentCount, 'int_commas'),
|
||||
dataTestSubj: 'documentCount'
|
||||
'data-test-subj': 'documentCount'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -78,7 +78,7 @@ function ClusterStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Data',
|
||||
}),
|
||||
value: formatMetric(dataSize, 'byte'),
|
||||
dataTestSubj: 'dataSize'
|
||||
'data-test-subj': 'dataSize'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiPage,
|
||||
EuiPageContent,
|
||||
EuiPageBody,
|
||||
EuiSpacer,
|
||||
EuiFlexGrid,
|
||||
EuiFlexItem,
|
||||
} from '@elastic/eui';
|
||||
import { IndexDetailStatus } from '../index_detail_status';
|
||||
import { MonitoringTimeseriesContainer } from '../../chart';
|
||||
|
||||
export const AdvancedIndex = ({
|
||||
indexSummary,
|
||||
metrics,
|
||||
...props
|
||||
}) => {
|
||||
const metricsToShow = [
|
||||
metrics.index_1,
|
||||
metrics.index_2,
|
||||
metrics.index_3,
|
||||
metrics.index_4,
|
||||
metrics.index_total,
|
||||
metrics.index_time,
|
||||
metrics.index_refresh,
|
||||
metrics.index_throttling,
|
||||
metrics.index_disk,
|
||||
metrics.index_segment_count,
|
||||
metrics.index_latency,
|
||||
];
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<IndexDetailStatus stats={indexSummary} />
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiFlexGrid columns={2} gutterSize="none">
|
||||
{metricsToShow.map((metric, index) => (
|
||||
<EuiFlexItem key={index} style={{ width: '50%' }}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={metric}
|
||||
{...props}
|
||||
/>
|
||||
<EuiSpacer size="m"/>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGrid>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiPage,
|
||||
EuiPageContent,
|
||||
EuiPageBody,
|
||||
EuiSpacer,
|
||||
EuiFlexGrid,
|
||||
EuiFlexItem,
|
||||
} from '@elastic/eui';
|
||||
import { IndexDetailStatus } from '../index_detail_status';
|
||||
import { MonitoringTimeseriesContainer } from '../../chart';
|
||||
import { ShardAllocation } from '../shard_allocation/shard_allocation';
|
||||
|
||||
export const Index = ({
|
||||
indexSummary,
|
||||
metrics,
|
||||
scope,
|
||||
kbnUrl,
|
||||
...props
|
||||
}) => {
|
||||
const metricsToShow = [
|
||||
metrics.index_mem,
|
||||
metrics.index_size,
|
||||
metrics.index_search_request_rate,
|
||||
metrics.index_request_rate,
|
||||
metrics.index_segment_count,
|
||||
metrics.index_document_count,
|
||||
];
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<IndexDetailStatus stats={indexSummary} />
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiFlexGrid columns={2} gutterSize="none">
|
||||
{metricsToShow.map((metric, index) => (
|
||||
<EuiFlexItem key={index} style={{ width: '50%' }}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={metric}
|
||||
{...props}
|
||||
/>
|
||||
<EuiSpacer size="m"/>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGrid>
|
||||
<EuiSpacer size="m"/>
|
||||
<ShardAllocation scope={scope} kbnUrl={kbnUrl} type="index" />
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
};
|
|
@ -26,7 +26,7 @@ function IndexDetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Total',
|
||||
}),
|
||||
value: formatMetric(dataSize.total, '0.0 b'),
|
||||
dataTestSubj: 'dataSize'
|
||||
'data-test-subj': 'dataSize'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -34,7 +34,7 @@ function IndexDetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Primaries',
|
||||
}),
|
||||
value: formatMetric(dataSize.primaries, '0.0 b'),
|
||||
dataTestSubj: 'dataSizePrimaries'
|
||||
'data-test-subj': 'dataSizePrimaries'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -42,7 +42,7 @@ function IndexDetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Documents',
|
||||
}),
|
||||
value: formatMetric(documentCount, '0.[0]a'),
|
||||
dataTestSubj: 'documentCount'
|
||||
'data-test-subj': 'documentCount'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -50,7 +50,7 @@ function IndexDetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Total Shards',
|
||||
}),
|
||||
value: formatMetric(totalShards, 'int_commas'),
|
||||
dataTestSubj: 'totalShards'
|
||||
'data-test-subj': 'totalShards'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -58,7 +58,7 @@ function IndexDetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Unassigned Shards',
|
||||
}),
|
||||
value: formatMetric(unassignedShards, 'int_commas'),
|
||||
dataTestSubj: 'unassignedShards'
|
||||
'data-test-subj': 'unassignedShards'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -4,133 +4,119 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
import { capitalize, get } from 'lodash';
|
||||
import { SORT_ASCENDING, SORT_DESCENDING } from '../../../../common/constants';
|
||||
import React from 'react';
|
||||
import { capitalize } from 'lodash';
|
||||
import { LARGE_FLOAT, LARGE_BYTES, LARGE_ABBREVIATED } from '../../../../common/formatting';
|
||||
import { formatMetric } from '../../../lib/format_number';
|
||||
import { ElasticsearchStatusIcon } from '../status_icon';
|
||||
import { ClusterStatus } from '../cluster_status';
|
||||
import { MonitoringTable } from '../../table';
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { KuiTableRowCell, KuiTableRow } from '@kbn/ui-framework/components';
|
||||
import { SystemIndicesCheckbox } from './system_indices_checkbox';
|
||||
import { EuiMonitoringTable } from '../../table';
|
||||
import {
|
||||
EuiLink,
|
||||
EuiPage,
|
||||
EuiPageContent,
|
||||
EuiPageBody,
|
||||
EuiSwitch,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
const filterFields = ['name', 'status'];
|
||||
const columns = [
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.indices.nameTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.indices.nameTitle', {
|
||||
defaultMessage: 'Name',
|
||||
}),
|
||||
sortKey: 'name',
|
||||
secondarySortOrder: SORT_ASCENDING
|
||||
field: 'name',
|
||||
width: '350px',
|
||||
sortable: true,
|
||||
render: (value) => (
|
||||
<div data-test-subj="name">
|
||||
<EuiLink
|
||||
href={`#/elasticsearch/indices/${value}`}
|
||||
data-test-subj={`indexLink-${value}`}
|
||||
>
|
||||
{value}
|
||||
</EuiLink>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.indices.statusTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.indices.statusTitle', {
|
||||
defaultMessage: 'Status',
|
||||
}),
|
||||
sortKey: 'status_sort',
|
||||
sortOrder: SORT_DESCENDING // default sort: red, then yellow, then green
|
||||
field: 'status',
|
||||
sortable: true,
|
||||
render: (value) => (
|
||||
<div title={`Index status: ${value}`}>
|
||||
<ElasticsearchStatusIcon status={value} />
|
||||
{capitalize(value)}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.indices.documentCountTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.indices.documentCountTitle', {
|
||||
defaultMessage: 'Document Count',
|
||||
}),
|
||||
sortKey: 'doc_count'
|
||||
field: 'doc_count',
|
||||
sortable: true,
|
||||
render: value => (
|
||||
<div data-test-subj="documentCount">
|
||||
{formatMetric(value, LARGE_ABBREVIATED)}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.indices.dataTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.indices.dataTitle', {
|
||||
defaultMessage: 'Data',
|
||||
}),
|
||||
sortKey: 'data_size'
|
||||
field: 'data_size',
|
||||
sortable: true,
|
||||
render: value => (
|
||||
<div data-test-subj="dataSize">
|
||||
{formatMetric(value, LARGE_BYTES)}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.indices.indexRateTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.indices.indexRateTitle', {
|
||||
defaultMessage: 'Index Rate',
|
||||
}),
|
||||
sortKey: 'index_rate'
|
||||
field: 'index_rate',
|
||||
sortable: true,
|
||||
render: value => (
|
||||
<div data-test-subj="indexRate">
|
||||
{formatMetric(value, LARGE_FLOAT, '/s')}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.indices.searchRateTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.indices.searchRateTitle', {
|
||||
defaultMessage: 'Search Rate',
|
||||
}),
|
||||
sortKey: 'search_rate'
|
||||
field: 'search_rate',
|
||||
sortable: true,
|
||||
render: value => (
|
||||
<div data-test-subj="searchRate">
|
||||
{formatMetric(value, LARGE_FLOAT, '/s')}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.indices.unassignedShardsTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.indices.unassignedShardsTitle', {
|
||||
defaultMessage: 'Unassigned Shards',
|
||||
}),
|
||||
sortKey: 'unassigned_shards'
|
||||
field: 'unassigned_shards',
|
||||
sortable: true,
|
||||
render: value => (
|
||||
<div data-test-subj="unassignedShards">
|
||||
{formatMetric(value, '0')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
const IndexRow = injectI18n(({ status, ...props }) => (
|
||||
<KuiTableRow>
|
||||
<KuiTableRowCell data-test-subj="name">
|
||||
<EuiLink
|
||||
href={`#/elasticsearch/indices/${props.name}`}
|
||||
data-test-subj={`indexLink-${props.name}`}
|
||||
>
|
||||
{props.name}
|
||||
</EuiLink>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
<div
|
||||
title={props.intl.formatMessage({
|
||||
id: 'xpack.monitoring.elasticsearch.indices.indexStatusTitle',
|
||||
defaultMessage: 'Index status: {status}' }, {
|
||||
status
|
||||
})}
|
||||
>
|
||||
<ElasticsearchStatusIcon status={status} />
|
||||
{capitalize(status)}
|
||||
</div>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell data-test-subj="documentCount">
|
||||
{formatMetric(get(props, 'doc_count'), LARGE_ABBREVIATED)}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell data-test-subj="dataSize">
|
||||
{formatMetric(get(props, 'data_size'), LARGE_BYTES)}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell data-test-subj="indexRate">
|
||||
{formatMetric(get(props, 'index_rate'), LARGE_FLOAT, '/s')}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell data-test-subj="searchRate">
|
||||
{formatMetric(get(props, 'search_rate'), LARGE_FLOAT, '/s')}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell data-test-subj="unassignedShards">
|
||||
{formatMetric(get(props, 'unassigned_shards'), '0')}
|
||||
</KuiTableRowCell>
|
||||
</KuiTableRow>
|
||||
));
|
||||
|
||||
const getNoDataMessage = filterText => {
|
||||
const howToShowSystemIndicesDescription = (
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.elasticsearch.indices.howToShowSystemIndicesDescription"
|
||||
defaultMessage="If you are looking for system indices (e.g., .kibana), try checking ‘Show system indices’."
|
||||
/>
|
||||
);
|
||||
if (filterText) {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.elasticsearch.indices.noFilteredIndicesDescription"
|
||||
defaultMessage="There are no indices that match your selection with the filter [{filterText}].
|
||||
Try changing the filter or the time range selection."
|
||||
values={{
|
||||
filterText: filterText.trim()
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
{howToShowSystemIndicesDescription}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const getNoDataMessage = () => {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
|
@ -140,46 +126,64 @@ const getNoDataMessage = filterText => {
|
|||
/>
|
||||
</p>
|
||||
<p>
|
||||
{howToShowSystemIndicesDescription}
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.elasticsearch.indices.howToShowSystemIndicesDescription"
|
||||
defaultMessage="If you are looking for system indices (e.g., .kibana), try checking ‘Show system indices’."
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderToolBarSection = ({ showSystemIndices, toggleShowSystemIndices, ...props }) => (
|
||||
<SystemIndicesCheckbox
|
||||
showSystemIndices={showSystemIndices}
|
||||
toggleShowSystemIndices={toggleShowSystemIndices}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
function ElasticsearchIndicesUI({ clusterStatus, indices, intl, ...props }) {
|
||||
const ElasticsearchIndicesUI = ({
|
||||
clusterStatus,
|
||||
indices,
|
||||
intl,
|
||||
sorting,
|
||||
pagination,
|
||||
onTableChange,
|
||||
toggleShowSystemIndices,
|
||||
showSystemIndices,
|
||||
}) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<ClusterStatus stats={clusterStatus} />
|
||||
<MonitoringTable
|
||||
className="elasticsearchIndicesTable"
|
||||
rows={indices}
|
||||
pageIndex={props.pageIndex}
|
||||
filterText={props.filterText}
|
||||
sortKey={props.sortKey}
|
||||
sortOrder={props.sortOrder}
|
||||
onNewState={props.onNewState}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'xpack.monitoring.elasticsearch.indices.monitoringTablePlaceholder',
|
||||
defaultMessage: 'Filter Indices…',
|
||||
})}
|
||||
filterFields={filterFields}
|
||||
renderToolBarSections={renderToolBarSection}
|
||||
columns={columns}
|
||||
rowComponent={IndexRow}
|
||||
getNoDataMessage={getNoDataMessage}
|
||||
showSystemIndices={props.showSystemIndices}
|
||||
toggleShowSystemIndices={props.toggleShowSystemIndices}
|
||||
/>
|
||||
</Fragment>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<ClusterStatus stats={clusterStatus} />
|
||||
<EuiSpacer size="xs"/>
|
||||
<EuiSwitch
|
||||
label={(
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.elasticsearch.indices.systemIndicesLabel"
|
||||
defaultMessage="System indices"
|
||||
/>
|
||||
)}
|
||||
checked={showSystemIndices}
|
||||
onChange={e => toggleShowSystemIndices(e.target.checked)}
|
||||
/>
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiMonitoringTable
|
||||
className="elasticsearchIndicesTable"
|
||||
rows={indices}
|
||||
columns={columns}
|
||||
sorting={sorting}
|
||||
pagination={pagination}
|
||||
message={getNoDataMessage()}
|
||||
search={{
|
||||
box: {
|
||||
incremental: true,
|
||||
placeholder: intl.formatMessage({
|
||||
id: 'xpack.monitoring.elasticsearch.indices.monitoringTablePlaceholder',
|
||||
defaultMessage: 'Filter Indices…',
|
||||
})
|
||||
},
|
||||
}}
|
||||
onTableChange={onTableChange}
|
||||
/>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const ElasticsearchIndices = injectI18n(ElasticsearchIndicesUI);
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiPage,
|
||||
EuiPageContent,
|
||||
EuiPageBody,
|
||||
EuiSpacer,
|
||||
EuiFlexGrid,
|
||||
EuiFlexItem,
|
||||
} from '@elastic/eui';
|
||||
import { NodeDetailStatus } from '../node_detail_status';
|
||||
import { MonitoringTimeseriesContainer } from '../../chart';
|
||||
|
||||
export const AdvancedNode = ({
|
||||
nodeSummary,
|
||||
metrics,
|
||||
...props
|
||||
}) => {
|
||||
const metricsToShow = [
|
||||
metrics.node_gc,
|
||||
metrics.node_gc_time,
|
||||
metrics.node_jvm_mem,
|
||||
metrics.node_cpu_utilization,
|
||||
metrics.node_index_1,
|
||||
metrics.node_index_2,
|
||||
metrics.node_index_3,
|
||||
metrics.node_index_4,
|
||||
metrics.node_index_time,
|
||||
metrics.node_request_total,
|
||||
metrics.node_index_threads,
|
||||
metrics.node_read_threads,
|
||||
metrics.node_cgroup_cpu,
|
||||
metrics.node_cgroup_stats,
|
||||
metrics.node_latency,
|
||||
];
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<NodeDetailStatus stats={nodeSummary} />
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiFlexGrid columns={2} gutterSize="none">
|
||||
{metricsToShow.map((metric, index) => (
|
||||
<EuiFlexItem key={index} style={{ width: '50%' }}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={metric}
|
||||
{...props}
|
||||
/>
|
||||
<EuiSpacer size="m"/>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGrid>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiPage,
|
||||
EuiPageContent,
|
||||
EuiPageBody,
|
||||
EuiSpacer,
|
||||
EuiFlexGrid,
|
||||
EuiFlexItem,
|
||||
} from '@elastic/eui';
|
||||
import { NodeDetailStatus } from '../node_detail_status';
|
||||
import { MonitoringTimeseriesContainer } from '../../chart';
|
||||
import { ShardAllocation } from '../shard_allocation/shard_allocation';
|
||||
|
||||
export const Node = ({
|
||||
nodeSummary,
|
||||
metrics,
|
||||
scope,
|
||||
kbnUrl,
|
||||
...props
|
||||
}) => {
|
||||
const metricsToShow = [
|
||||
metrics.node_jvm_mem,
|
||||
metrics.node_mem,
|
||||
metrics.node_cpu_metric,
|
||||
metrics.node_load_average,
|
||||
metrics.node_latency,
|
||||
metrics.node_segment_count,
|
||||
];
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<NodeDetailStatus stats={nodeSummary} />
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiFlexGrid columns={2} gutterSize="none">
|
||||
{metricsToShow.map((metric, index) => (
|
||||
<EuiFlexItem key={index} style={{ width: '50%' }}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={metric}
|
||||
{...props}
|
||||
/>
|
||||
<EuiSpacer size="m"/>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGrid>
|
||||
<EuiSpacer size="m"/>
|
||||
<ShardAllocation scope={scope} kbnUrl={kbnUrl}/>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
};
|
|
@ -26,8 +26,12 @@ function NodeDetailStatusUI({ stats, intl }) {
|
|||
|
||||
const metrics = [
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'xpack.monitoring.elasticsearch.nodeDetailStatus.transportAddress',
|
||||
defaultMessage: 'Transport Address',
|
||||
}),
|
||||
value: transportAddress,
|
||||
dataTestSubj: 'transportAddress'
|
||||
'data-test-subj': 'transportAddress'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -36,7 +40,7 @@ function NodeDetailStatusUI({ stats, intl }) {
|
|||
javaVirtualMachine: 'JVM'
|
||||
}),
|
||||
value: formatMetric(usedHeap, '0,0.[00]', '%', { prependSpace: false }),
|
||||
dataTestSubj: 'jvmHeap'
|
||||
'data-test-subj': 'jvmHeap'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -44,7 +48,7 @@ function NodeDetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Free Disk Space',
|
||||
}),
|
||||
value: formatMetric(freeSpace, '0.0 b'),
|
||||
dataTestSubj: 'freeDiskSpace'
|
||||
'data-test-subj': 'freeDiskSpace'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -52,7 +56,7 @@ function NodeDetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Documents',
|
||||
}),
|
||||
value: formatMetric(documents, '0.[0]a'),
|
||||
dataTestSubj: 'documentCount'
|
||||
'data-test-subj': 'documentCount'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -60,7 +64,7 @@ function NodeDetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Data',
|
||||
}),
|
||||
value: formatMetric(dataSize, '0.0 b'),
|
||||
dataTestSubj: 'dataSize'
|
||||
'data-test-subj': 'dataSize'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -68,7 +72,7 @@ function NodeDetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Indices',
|
||||
}),
|
||||
value: formatMetric(indexCount, 'int_commas'),
|
||||
dataTestSubj: 'indicesCount'
|
||||
'data-test-subj': 'indicesCount'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -76,7 +80,7 @@ function NodeDetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Shards',
|
||||
}),
|
||||
value: formatMetric(totalShards, 'int_commas'),
|
||||
dataTestSubj: 'shardsCount'
|
||||
'data-test-subj': 'shardsCount'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -84,7 +88,7 @@ function NodeDetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Type',
|
||||
}),
|
||||
value: nodeTypeLabel,
|
||||
dataTestSubj: 'nodeType'
|
||||
'data-test-subj': 'nodeType'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -1,75 +1,103 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Node Listing Metric Cell should format N/A as the metric for an offline node 1`] = `
|
||||
<td
|
||||
class="kuiTableRowCell"
|
||||
<div
|
||||
class="monTableCell__number monTableCell__offline"
|
||||
>
|
||||
<div
|
||||
class="kuiTableRowCell__liner"
|
||||
>
|
||||
<div
|
||||
class="monTableCell__number monTableCell__offline"
|
||||
>
|
||||
N/A
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
N/A
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Node Listing Metric Cell should format a non-percentage metric 1`] = `
|
||||
<td
|
||||
class="kuiTableRowCell"
|
||||
<div
|
||||
class="euiStat euiStat--leftAligned"
|
||||
>
|
||||
<div
|
||||
class="kuiTableRowCell__liner"
|
||||
class="euiText euiText--small euiStat__description"
|
||||
>
|
||||
<p />
|
||||
</div>
|
||||
<p
|
||||
class="euiTitle euiTitle--large euiStat__title"
|
||||
/>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
intl="[object Object]"
|
||||
>
|
||||
<div
|
||||
class="monTableCell__metricCellMetric"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
206.3 GB
|
||||
<h4
|
||||
class="euiTitle euiTitle--medium"
|
||||
>
|
||||
206.3 GB
|
||||
<span
|
||||
class="fa fa-long-arrow-down"
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<span
|
||||
class="monTableCell__metricCellSlopeArrow fa fa-long-arrow-down"
|
||||
/>
|
||||
<div
|
||||
class="monTableCell__metricCellMixMax"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="euiText euiText--extraSmall"
|
||||
>
|
||||
206.5 GB max
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="euiText euiText--extraSmall"
|
||||
>
|
||||
206.3 GB min
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<p />
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Node Listing Metric Cell should format a percentage metric 1`] = `
|
||||
<td
|
||||
class="kuiTableRowCell"
|
||||
<div
|
||||
class="euiStat euiStat--leftAligned"
|
||||
>
|
||||
<div
|
||||
class="kuiTableRowCell__liner"
|
||||
class="euiText euiText--small euiStat__description"
|
||||
>
|
||||
<p />
|
||||
</div>
|
||||
<p
|
||||
class="euiTitle euiTitle--large euiStat__title"
|
||||
/>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
intl="[object Object]"
|
||||
>
|
||||
<div
|
||||
class="monTableCell__metricCellMetric"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
0%
|
||||
<h4
|
||||
class="euiTitle euiTitle--medium"
|
||||
>
|
||||
0%
|
||||
<span
|
||||
class="fa fa-long-arrow-down"
|
||||
/>
|
||||
</h4>
|
||||
</div>
|
||||
<span
|
||||
class="monTableCell__metricCellSlopeArrow fa fa-long-arrow-down"
|
||||
/>
|
||||
<div
|
||||
class="monTableCell__metricCellMixMax"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="euiText euiText--extraSmall"
|
||||
>
|
||||
2% max
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="euiText euiText--extraSmall"
|
||||
>
|
||||
0% min
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<p />
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -7,19 +7,13 @@
|
|||
import React from 'react';
|
||||
import { get } from 'lodash';
|
||||
import { formatMetric } from '../../../lib/format_number';
|
||||
import { KuiTableRowCell } from '@kbn/ui-framework/components';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { EuiStat, EuiText, EuiTitle, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
|
||||
function OfflineCell() {
|
||||
return (
|
||||
<KuiTableRowCell>
|
||||
<div className="monTableCell__number monTableCell__offline">
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.elasticsearch.nodes.noDataInCellLabel"
|
||||
defaultMessage="N/A"
|
||||
/>
|
||||
</div>
|
||||
</KuiTableRowCell>
|
||||
<div className="monTableCell__number monTableCell__offline">
|
||||
N/A
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -43,20 +37,30 @@ function MetricCell({ isOnline, metric = {}, isPercent, ...props }) {
|
|||
const format = get(metric, 'metric.format');
|
||||
|
||||
return (
|
||||
<KuiTableRowCell>
|
||||
<div className="monTableCell__metricCellMetric" data-test-subj={props['data-test-subj']}>
|
||||
{ metricVal(lastVal, format, isPercent) }
|
||||
</div>
|
||||
<span className={`monTableCell__metricCellSlopeArrow fa fa-long-arrow-${getSlopeArrow(slope)}`} />
|
||||
<div className="monTableCell__metricCellMixMax">
|
||||
<div>
|
||||
{ metricVal(maxVal, format, isPercent) + ' max' }
|
||||
</div>
|
||||
<div>
|
||||
{ metricVal(minVal, format, isPercent) + ' min' }
|
||||
</div>
|
||||
</div>
|
||||
</KuiTableRowCell>
|
||||
<EuiStat
|
||||
description=""
|
||||
title={(
|
||||
<EuiFlexGroup alignItems="center" {...props}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="m">
|
||||
<h4>
|
||||
{ metricVal(lastVal, format, isPercent) }
|
||||
|
||||
<span className={`fa fa-long-arrow-${getSlopeArrow(slope)}`} />
|
||||
</h4>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="xs">
|
||||
{ metricVal(maxVal, format, isPercent) + ' max' }
|
||||
</EuiText>
|
||||
<EuiText size="xs">
|
||||
{ metricVal(minVal, format, isPercent) + ' min' }
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,227 +4,228 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
import { get } from 'lodash';
|
||||
import { SORT_ASCENDING } from '../../../../common/constants';
|
||||
import React from 'react';
|
||||
import { NodeStatusIcon } from '../node';
|
||||
import { extractIp } from '../../../lib/extract_ip'; // TODO this is only used for elasticsearch nodes summary / node detail, so it should be moved to components/elasticsearch/nodes/lib
|
||||
import { ClusterStatus } from '../cluster_status';
|
||||
import { MonitoringTable } from '../../table';
|
||||
import { EuiMonitoringTable } from '../../table';
|
||||
import { MetricCell, OfflineCell } from './cells';
|
||||
import { EuiLink, EuiToolTip } from '@elastic/eui';
|
||||
import { KuiTableRowCell, KuiTableRow } from '@kbn/ui-framework/components';
|
||||
import {
|
||||
EuiLink,
|
||||
EuiToolTip,
|
||||
EuiSpacer,
|
||||
EuiPage,
|
||||
EuiPageContent,
|
||||
EuiPageBody,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
const filterFields = ['name'];
|
||||
const getColumns = showCgroupMetricsElasticsearch => {
|
||||
const cols = [];
|
||||
cols.push({
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.nodes.nameColumnTitle', {
|
||||
defaultMessage: 'Name',
|
||||
}),
|
||||
sortKey: 'name',
|
||||
sortOrder: SORT_ASCENDING
|
||||
});
|
||||
cols.push({
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.nodes.statusColumnTitle', {
|
||||
defaultMessage: 'Status',
|
||||
}),
|
||||
sortKey: 'isOnline'
|
||||
});
|
||||
|
||||
const cpuUsageColumnTitle = i18n.translate('xpack.monitoring.elasticsearch.nodes.cpuUsageColumnTitle', {
|
||||
defaultMessage: 'CPU Usage',
|
||||
});
|
||||
|
||||
cols.push({
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.nodes.nameColumnTitle', {
|
||||
defaultMessage: 'Name',
|
||||
}),
|
||||
field: 'name',
|
||||
sortable: true,
|
||||
render: (value, node) => (
|
||||
<div>
|
||||
<div className="monTableCell__name">
|
||||
<EuiToolTip
|
||||
position="bottom"
|
||||
content={node.nodeTypeLabel}
|
||||
>
|
||||
<span className={`fa ${node.nodeTypeClass}`} />
|
||||
</EuiToolTip>
|
||||
|
||||
<span data-test-subj="name">
|
||||
<EuiLink
|
||||
href={`#/elasticsearch/nodes/${node.resolver}`}
|
||||
data-test-subj={`nodeLink-${node.resolver}`}
|
||||
>
|
||||
{value}
|
||||
</EuiLink>
|
||||
</span>
|
||||
</div>
|
||||
<div className="monTableCell__transportAddress">
|
||||
{extractIp(node.transport_address)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
});
|
||||
|
||||
cols.push({
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.nodes.statusColumnTitle', {
|
||||
defaultMessage: 'Status',
|
||||
}),
|
||||
field: 'isOnline',
|
||||
sortable: true,
|
||||
render: value => {
|
||||
const status = value ? 'Online' : 'Offline';
|
||||
return (
|
||||
<div className="monTableCell__status">
|
||||
<NodeStatusIcon
|
||||
isOnline={value}
|
||||
status={status}
|
||||
/>{' '}
|
||||
{status}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (showCgroupMetricsElasticsearch) {
|
||||
cols.push({
|
||||
title: cpuUsageColumnTitle,
|
||||
sortKey: 'node_cgroup_quota'
|
||||
name: cpuUsageColumnTitle,
|
||||
field: 'node_cgroup_quota',
|
||||
sortable: true,
|
||||
render: (value, node) => (
|
||||
<MetricCell
|
||||
isOnline={node.isOnline}
|
||||
metric={value}
|
||||
isPercent={true}
|
||||
data-test-subj="cpuQuota"
|
||||
/>
|
||||
)
|
||||
});
|
||||
|
||||
cols.push({
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.nodes.cpuThrottlingColumnTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.nodes.cpuThrottlingColumnTitle', {
|
||||
defaultMessage: 'CPU Throttling',
|
||||
}),
|
||||
sortKey: 'node_cgroup_throttled'
|
||||
field: 'node_cgroup_throttled',
|
||||
sortable: true,
|
||||
render: (value, node) => (
|
||||
<MetricCell
|
||||
isOnline={node.isOnline}
|
||||
metric={value}
|
||||
isPercent={false}
|
||||
data-test-subj="cpuThrottled"
|
||||
/>
|
||||
)
|
||||
});
|
||||
} else {
|
||||
cols.push({
|
||||
title: cpuUsageColumnTitle,
|
||||
sortKey: 'node_cpu_utilization'
|
||||
name: cpuUsageColumnTitle,
|
||||
field: 'node_cpu_utilization',
|
||||
sortable: true,
|
||||
render: (value, node) => (
|
||||
<MetricCell
|
||||
isOnline={node.isOnline}
|
||||
metric={value}
|
||||
isPercent={true}
|
||||
data-test-subj="cpuUsage"
|
||||
/>
|
||||
)
|
||||
});
|
||||
|
||||
cols.push({
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.nodes.loadAverageColumnTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.nodes.loadAverageColumnTitle', {
|
||||
defaultMessage: 'Load Average',
|
||||
}),
|
||||
sortKey: 'node_load_average'
|
||||
field: 'node_load_average',
|
||||
sortable: true,
|
||||
render: (value, node) => (
|
||||
<MetricCell
|
||||
isOnline={node.isOnline}
|
||||
metric={value}
|
||||
isPercent={false}
|
||||
data-test-subj="loadAverage"
|
||||
/>
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
cols.push({
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.nodes.jvmMemoryColumnTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.nodes.jvmMemoryColumnTitle', {
|
||||
defaultMessage: '{javaVirtualMachine} Memory',
|
||||
values: {
|
||||
javaVirtualMachine: 'JVM'
|
||||
}
|
||||
}),
|
||||
sortKey: 'node_jvm_mem_percent'
|
||||
field: 'node_jvm_mem_percent',
|
||||
sortable: true,
|
||||
render: (value, node) => (
|
||||
<MetricCell
|
||||
isOnline={node.isOnline}
|
||||
metric={value}
|
||||
isPercent={true}
|
||||
data-test-subj="jvmMemory"
|
||||
/>
|
||||
)
|
||||
});
|
||||
|
||||
cols.push({
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.nodes.diskFreeSpaceColumnTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.nodes.diskFreeSpaceColumnTitle', {
|
||||
defaultMessage: 'Disk Free Space',
|
||||
}),
|
||||
sortKey: 'node_free_space'
|
||||
field: 'node_free_space',
|
||||
sortable: true,
|
||||
width: '300px',
|
||||
render: (value, node) => (
|
||||
<MetricCell
|
||||
isOnline={node.isOnline}
|
||||
metric={value}
|
||||
isPercent={false}
|
||||
data-test-subj="diskFreeSpace"
|
||||
/>
|
||||
)
|
||||
});
|
||||
|
||||
cols.push({
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.nodes.shardsColumnTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.nodes.shardsColumnTitle', {
|
||||
defaultMessage: 'Shards',
|
||||
}),
|
||||
sortKey: 'shardCount'
|
||||
field: 'shardCount',
|
||||
sortable: true,
|
||||
render: (value, node) => {
|
||||
return node.isOnline ? (
|
||||
<div className="monTableCell__number" data-test-subj="shards">
|
||||
{value}
|
||||
</div>
|
||||
) : <OfflineCell/>;
|
||||
}
|
||||
});
|
||||
|
||||
return cols;
|
||||
};
|
||||
|
||||
const nodeRowFactory = showCgroupMetricsElasticsearch => {
|
||||
return class NodeRow extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
isOnline() {
|
||||
return this.props.isOnline === true;
|
||||
}
|
||||
|
||||
getCpuComponents() {
|
||||
const isOnline = this.isOnline();
|
||||
if (showCgroupMetricsElasticsearch) {
|
||||
return [
|
||||
<MetricCell
|
||||
key="cpuCol1"
|
||||
isOnline={isOnline}
|
||||
metric={get(this.props, 'node_cgroup_quota')}
|
||||
isPercent={true}
|
||||
data-test-subj="cpuQuota"
|
||||
/>,
|
||||
<MetricCell
|
||||
key="cpuCol2"
|
||||
isOnline={isOnline}
|
||||
metric={get(this.props, 'node_cgroup_throttled')}
|
||||
isPercent={false}
|
||||
data-test-subj="cpuThrottled"
|
||||
/>
|
||||
];
|
||||
}
|
||||
return [
|
||||
<MetricCell
|
||||
key="cpuCol1"
|
||||
isOnline={isOnline}
|
||||
metric={get(this.props, 'node_cpu_utilization')}
|
||||
isPercent={true}
|
||||
data-test-subj="cpuUsage"
|
||||
/>,
|
||||
<MetricCell
|
||||
key="cpuCol2"
|
||||
isOnline={isOnline}
|
||||
metric={get(this.props, 'node_load_average')}
|
||||
isPercent={false}
|
||||
data-test-subj="loadAverage"
|
||||
/>
|
||||
];
|
||||
}
|
||||
|
||||
getShardCount() {
|
||||
if (this.isOnline()) {
|
||||
return (
|
||||
<KuiTableRowCell>
|
||||
<div className="monTableCell__number" data-test-subj="shards">
|
||||
{get(this.props, 'shardCount')}
|
||||
</div>
|
||||
</KuiTableRowCell>
|
||||
);
|
||||
}
|
||||
return <OfflineCell />;
|
||||
}
|
||||
|
||||
render() {
|
||||
const isOnline = this.isOnline();
|
||||
const status = this.props.isOnline ? 'Online' : 'Offline';
|
||||
|
||||
return (
|
||||
<KuiTableRow>
|
||||
<KuiTableRowCell>
|
||||
<div className="monTableCell__name">
|
||||
<EuiToolTip
|
||||
position="bottom"
|
||||
content={this.props.nodeTypeLabel}
|
||||
>
|
||||
<span className={`fa ${this.props.nodeTypeClass}`} />
|
||||
</EuiToolTip>
|
||||
|
||||
<span data-test-subj="name">
|
||||
<EuiLink
|
||||
href={`#/elasticsearch/nodes/${this.props.resolver}`}
|
||||
data-test-subj={`nodeLink-${this.props.resolver}`}
|
||||
>
|
||||
{this.props.name}
|
||||
</EuiLink>
|
||||
</span>
|
||||
</div>
|
||||
<div className="monTableCell__transportAddress">
|
||||
{extractIp(this.props.transport_address)}
|
||||
</div>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
<div className="monTableCell__status">
|
||||
<NodeStatusIcon
|
||||
isOnline={this.props.isOnline}
|
||||
status={status}
|
||||
/>{' '}
|
||||
{status}
|
||||
</div>
|
||||
</KuiTableRowCell>
|
||||
{this.getCpuComponents()}
|
||||
<MetricCell
|
||||
isOnline={isOnline}
|
||||
metric={get(this.props, 'node_jvm_mem_percent')}
|
||||
isPercent={true}
|
||||
data-test-subj="jvmMemory"
|
||||
/>
|
||||
<MetricCell
|
||||
isOnline={isOnline}
|
||||
metric={get(this.props, 'node_free_space')}
|
||||
isPercent={false}
|
||||
data-test-subj="diskFreeSpace"
|
||||
/>
|
||||
{this.getShardCount()}
|
||||
</KuiTableRow>
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function ElasticsearchNodesUI({ clusterStatus, nodes, showCgroupMetricsElasticsearch, intl, ...props }) {
|
||||
const columns = getColumns(showCgroupMetricsElasticsearch);
|
||||
const { sorting, pagination, onTableChange } = props;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<ClusterStatus stats={clusterStatus} />
|
||||
|
||||
<MonitoringTable
|
||||
className="elasticsearchNodesTable"
|
||||
rows={nodes}
|
||||
pageIndex={props.pageIndex}
|
||||
filterText={props.filterText}
|
||||
sortKey={props.sortKey}
|
||||
sortOrder={props.sortOrder}
|
||||
onNewState={props.onNewState}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'xpack.monitoring.elasticsearch.nodes.monitoringTablePlaceholder',
|
||||
defaultMessage: 'Filter Nodes…',
|
||||
})}
|
||||
filterFields={filterFields}
|
||||
columns={columns}
|
||||
rowComponent={nodeRowFactory(showCgroupMetricsElasticsearch)}
|
||||
/>
|
||||
</Fragment>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<ClusterStatus stats={clusterStatus} />
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiMonitoringTable
|
||||
className="elasticsearchNodesTable"
|
||||
rows={nodes}
|
||||
columns={columns}
|
||||
sorting={sorting}
|
||||
pagination={pagination}
|
||||
search={{
|
||||
box: {
|
||||
incremental: true,
|
||||
placeholder: intl.formatMessage({
|
||||
id: 'xpack.monitoring.elasticsearch.nodes.monitoringTablePlaceholder',
|
||||
defaultMessage: 'Filter Nodes…',
|
||||
}),
|
||||
},
|
||||
}}
|
||||
onTableChange={onTableChange}
|
||||
/>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
import { ClusterStatus } from '../cluster_status';
|
||||
import { ShardActivity } from '../shard_activity';
|
||||
import { MonitoringTimeseriesContainer } from '../../chart';
|
||||
import { EuiPage, EuiFlexGrid, EuiFlexItem, EuiSpacer, EuiPageBody } from '@elastic/eui';
|
||||
import { EuiPage, EuiFlexGrid, EuiFlexItem, EuiSpacer, EuiPageBody, EuiPageContent } from '@elastic/eui';
|
||||
|
||||
export function ElasticsearchOverview({
|
||||
clusterStatus,
|
||||
|
@ -24,10 +24,11 @@ export function ElasticsearchOverview({
|
|||
];
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<ClusterStatus stats={clusterStatus} />
|
||||
<EuiPage style={{ backgroundColor: 'white' }}>
|
||||
<EuiPageBody>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<ClusterStatus stats={clusterStatus} />
|
||||
<EuiSpacer/>
|
||||
<EuiFlexGrid columns={2} gutterSize="none">
|
||||
{metricsToShow.map((metric, index) => (
|
||||
<EuiFlexItem key={index} style={{ width: '50%' }}>
|
||||
|
@ -40,8 +41,8 @@ export function ElasticsearchOverview({
|
|||
))}
|
||||
</EuiFlexGrid>
|
||||
<ShardActivity data={shardActivity} {...props} />
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</Fragment>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { Snapshot } from './snapshot';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
@ -13,7 +13,7 @@ export const RecoveryIndex = (props) => {
|
|||
const { name, shard, relocationType } = props;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div>
|
||||
<EuiLink href={`#/elasticsearch/indices/${name}`}>{name}</EuiLink><br />
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.elasticsearch.shardActivity.recoveryIndex.shardDescription"
|
||||
|
@ -32,6 +32,6 @@ export const RecoveryIndex = (props) => {
|
|||
<div>
|
||||
<Snapshot {...props} />
|
||||
</div>
|
||||
</Fragment>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,13 +6,7 @@
|
|||
|
||||
import React, { Fragment } from 'react';
|
||||
import { EuiText, EuiTitle, EuiLink, EuiSpacer, EuiSwitch } from '@elastic/eui';
|
||||
import {
|
||||
KuiTableRowCell,
|
||||
KuiTableRow,
|
||||
KuiToolBarSection,
|
||||
KuiToolBarText
|
||||
} from '@kbn/ui-framework/components';
|
||||
import { MonitoringTable } from 'plugins/monitoring/components/table';
|
||||
import { EuiMonitoringTable } from 'plugins/monitoring/components/table';
|
||||
import { RecoveryIndex } from './recovery_index';
|
||||
import { TotalTime } from './total_time';
|
||||
import { SourceDestination } from './source_destination';
|
||||
|
@ -23,89 +17,55 @@ import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
|||
|
||||
const columns = [
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.kibana.shardActivity.indexTitle', {
|
||||
name: i18n.translate('xpack.monitoring.kibana.shardActivity.indexTitle', {
|
||||
defaultMessage: 'Index'
|
||||
}),
|
||||
sortKey: null
|
||||
field: 'name',
|
||||
render: (_name, shard) => <RecoveryIndex {...shard} />
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.kibana.shardActivity.stageTitle', {
|
||||
name: i18n.translate('xpack.monitoring.kibana.shardActivity.stageTitle', {
|
||||
defaultMessage: 'Stage'
|
||||
}),
|
||||
sortKey: null
|
||||
field: 'stage'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.kibana.shardActivity.totalTimeTitle', {
|
||||
name: i18n.translate('xpack.monitoring.kibana.shardActivity.totalTimeTitle', {
|
||||
defaultMessage: 'Total Time'
|
||||
}),
|
||||
sortKey: null
|
||||
field: null,
|
||||
render: shard => <TotalTime {...shard} />
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.kibana.shardActivity.sourceDestinationTitle', {
|
||||
name: i18n.translate('xpack.monitoring.kibana.shardActivity.sourceDestinationTitle', {
|
||||
defaultMessage: 'Source / Destination'
|
||||
}),
|
||||
sortKey: null
|
||||
field: null,
|
||||
render: shard => <SourceDestination {...shard} />
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.kibana.shardActivity.filesTitle', {
|
||||
name: i18n.translate('xpack.monitoring.kibana.shardActivity.filesTitle', {
|
||||
defaultMessage: 'Files'
|
||||
}),
|
||||
sortKey: null
|
||||
field: null,
|
||||
render: shard => <FilesProgress {...shard} />
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.kibana.shardActivity.bytesTitle', {
|
||||
name: i18n.translate('xpack.monitoring.kibana.shardActivity.bytesTitle', {
|
||||
defaultMessage: 'Bytes'
|
||||
}),
|
||||
sortKey: null
|
||||
field: null,
|
||||
render: shard => <BytesProgress {...shard} />
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.kibana.shardActivity.translogTitle', {
|
||||
name: i18n.translate('xpack.monitoring.kibana.shardActivity.translogTitle', {
|
||||
defaultMessage: 'Translog'
|
||||
}),
|
||||
sortKey: null
|
||||
field: null,
|
||||
render: shard => <TranslogProgress {...shard} />
|
||||
}
|
||||
];
|
||||
const ActivityRow = props => (
|
||||
<KuiTableRow>
|
||||
<KuiTableRowCell>
|
||||
<RecoveryIndex {...props} />
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>{props.stage}</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
<TotalTime {...props} />
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
<SourceDestination {...props} />
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
<FilesProgress {...props} />
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
<BytesProgress {...props} />
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
<TranslogProgress {...props} />
|
||||
</KuiTableRowCell>
|
||||
</KuiTableRow>
|
||||
);
|
||||
|
||||
const ToggleCompletedSwitch = ({ toggleHistory, showHistory }) => (
|
||||
<KuiToolBarSection>
|
||||
<KuiToolBarText>
|
||||
<EuiSwitch
|
||||
id="monitoring_completed_recoveries"
|
||||
label={(
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.elasticsearch.shardActivity.completedRecoveriesLabel"
|
||||
defaultMessage="Completed recoveries"
|
||||
/>
|
||||
)}
|
||||
onChange={toggleHistory}
|
||||
checked={showHistory}
|
||||
/>
|
||||
</KuiToolBarText>
|
||||
</KuiToolBarSection>
|
||||
);
|
||||
|
||||
class ShardActivityUI extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -146,18 +106,20 @@ class ShardActivityUI extends React.Component {
|
|||
|
||||
render() {
|
||||
// data prop is an array of table row data, or null (which triggers no data message)
|
||||
const { data: rawData } = this.props;
|
||||
const {
|
||||
data: rawData,
|
||||
sorting,
|
||||
pagination,
|
||||
onTableChange,
|
||||
toggleShardActivityHistory,
|
||||
showShardActivityHistory
|
||||
} = this.props;
|
||||
|
||||
if (rawData === null) {
|
||||
return null;
|
||||
}
|
||||
const rows = rawData.map(parseProps);
|
||||
|
||||
const renderToolBarSection = props => (
|
||||
<ToggleCompletedSwitch
|
||||
toggleHistory={props.toggleShardActivityHistory}
|
||||
showHistory={props.showShardActivityHistory}
|
||||
/>
|
||||
);
|
||||
const rows = rawData.map(parseProps);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
@ -172,15 +134,27 @@ class ShardActivityUI extends React.Component {
|
|||
</EuiTitle>
|
||||
</EuiText>
|
||||
<EuiSpacer />
|
||||
<MonitoringTable
|
||||
<EuiSwitch
|
||||
id="monitoring_completed_recoveries"
|
||||
label={(
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.elasticsearch.shardActivity.completedRecoveriesLabel"
|
||||
defaultMessage="Completed recoveries"
|
||||
/>
|
||||
)}
|
||||
onChange={toggleShardActivityHistory}
|
||||
checked={showShardActivityHistory}
|
||||
/>
|
||||
<EuiSpacer/>
|
||||
<EuiMonitoringTable
|
||||
className="esShardActivityTable"
|
||||
rows={rows}
|
||||
renderToolBarSections={renderToolBarSection}
|
||||
columns={columns}
|
||||
rowComponent={ActivityRow}
|
||||
getNoDataMessage={this.getNoDataMessage}
|
||||
alwaysShowPageControls={true}
|
||||
{...this.props}
|
||||
message={this.getNoDataMessage()}
|
||||
sorting={sorting}
|
||||
search={false}
|
||||
pagination={pagination}
|
||||
onTableChange={onTableChange}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
|
|
|
@ -80,44 +80,18 @@ monitoring-shard-allocation {
|
|||
.shard {
|
||||
align-self: center;
|
||||
padding: 5px 7px;
|
||||
background-color: $euiColorPrimary;
|
||||
font: 10px sans-serif;
|
||||
border-left: 1px solid $euiColorEmptyShade;
|
||||
position: relative;
|
||||
color: $euiColorGhost;
|
||||
|
||||
.shard-tooltip {
|
||||
padding: 5px;
|
||||
bottom: 25px;
|
||||
left: 0;
|
||||
background-color: $euiColorLightShade;
|
||||
position: absolute;
|
||||
color: $euiColorDarkShade;
|
||||
border: 1px solid $euiColorLightShade;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&.replica {
|
||||
background-color: tintOrShade($euiColorPrimary, 15%, 15%);
|
||||
}
|
||||
|
||||
&.unassigned {
|
||||
background-color: $euiColorMediumShade !important;
|
||||
color: $euiColorFullShade;
|
||||
}
|
||||
|
||||
&.emergency {
|
||||
background-color: $euiColorDanger !important;
|
||||
color: $euiColorFullShade;
|
||||
}
|
||||
|
||||
&.relocating {
|
||||
background-color: $euiColorVis3;
|
||||
}
|
||||
|
||||
&.initializing {
|
||||
background-color: tintOrShade($euiColorVis3, 15%, 15%);
|
||||
}
|
||||
}
|
||||
|
||||
.legend {
|
|
@ -9,8 +9,8 @@
|
|||
import { get, sortBy } from 'lodash';
|
||||
import React from 'react';
|
||||
import { Shard } from './shard';
|
||||
import { calculateClass } from '../lib/calculateClass';
|
||||
import { generateQueryAndLink } from '../lib/generateQueryAndLink';
|
||||
import { calculateClass } from '../lib/calculate_class';
|
||||
import { generateQueryAndLink } from '../lib/generate_query_and_link';
|
||||
import {
|
||||
EuiKeyboardAccessible,
|
||||
} from '@elastic/eui';
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
|
||||
import React from 'react';
|
||||
import { TableHead } from './tableHead';
|
||||
import { TableBody } from './tableBody';
|
||||
import { TableHead } from './table_head';
|
||||
import { TableBody } from './table_body';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export class ClusterView extends React.Component {
|
||||
|
@ -24,7 +24,7 @@ export class ClusterView extends React.Component {
|
|||
this.state = {
|
||||
labels: props.scope.labels || [],
|
||||
showing: props.scope.showing || [],
|
||||
shardStats: props.shardStats,
|
||||
shardStats: props.scope.pageData.shardStats,
|
||||
showSystemIndices: props.showSystemIndices,
|
||||
toggleShowSystemIndices: props.toggleShowSystemIndices,
|
||||
angularChangeUrl: (url) => {
|
||||
|
@ -45,7 +45,7 @@ export class ClusterView extends React.Component {
|
|||
|
||||
componentWillMount() {
|
||||
this.props.scope.$watch('showing', this.setShowing);
|
||||
this.props.scope.$watch('shardStats', this.setShardStats);
|
||||
this.props.scope.$watch(() => this.props.scope.pageData.shardStats, this.setShardStats);
|
||||
}
|
||||
|
||||
hasUnassigned = () => {
|
|
@ -7,9 +7,32 @@
|
|||
|
||||
|
||||
import React from 'react';
|
||||
import { calculateClass } from '../lib/calculateClass';
|
||||
import { calculateClass } from '../lib/calculate_class';
|
||||
import { vents } from '../lib/vents';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiTextColor } from '@elastic/eui';
|
||||
|
||||
function getColor(classes) {
|
||||
return classes.split(' ').reduce((color, cls) => {
|
||||
if (color) {
|
||||
return color;
|
||||
}
|
||||
|
||||
switch (cls) {
|
||||
case 'primary':
|
||||
return 'ghost';
|
||||
case 'replica':
|
||||
return 'secondary';
|
||||
case 'relocation':
|
||||
return 'accent';
|
||||
case 'initializing':
|
||||
return 'default';
|
||||
case 'emergency':
|
||||
case 'unassigned':
|
||||
return 'danger';
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
export class Shard extends React.Component {
|
||||
static displayName = i18n.translate('xpack.monitoring.elasticsearch.shardAllocation.shardDisplayName', {
|
||||
|
@ -71,6 +94,7 @@ export class Shard extends React.Component {
|
|||
}
|
||||
|
||||
const classes = calculateClass(shard);
|
||||
const color = getColor(classes);
|
||||
const classification = classes + ' ' + shard.shard;
|
||||
|
||||
// data attrs for automated testing verification
|
||||
|
@ -83,7 +107,9 @@ export class Shard extends React.Component {
|
|||
data-shard-classification={classification}
|
||||
data-test-subj="shardIcon"
|
||||
>
|
||||
{tooltip}{shard.shard}
|
||||
<EuiTextColor color={color}>
|
||||
{tooltip}{shard.shard}
|
||||
</EuiTextColor>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { ShardAllocation } from './shard_allocation';
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiTitle, EuiBadge, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ClusterView } from './components/cluster_view';
|
||||
|
||||
export const ShardAllocation = ({
|
||||
scope,
|
||||
kbnUrl,
|
||||
type,
|
||||
shardStats,
|
||||
}) => {
|
||||
const types = [
|
||||
{
|
||||
label: i18n.translate('xpack.monitoring.elasticsearch.shardAllocation.primaryLabel', {
|
||||
defaultMessage: 'Primary'
|
||||
}),
|
||||
color: 'primary'
|
||||
},
|
||||
{
|
||||
label: i18n.translate('xpack.monitoring.elasticsearch.shardAllocation.replicaLabel', {
|
||||
defaultMessage: 'Replica'
|
||||
}),
|
||||
color: 'secondary'
|
||||
},
|
||||
{
|
||||
label: i18n.translate('xpack.monitoring.elasticsearch.shardAllocation.relocatingLabel', {
|
||||
defaultMessage: 'Relocating'
|
||||
}),
|
||||
color: 'accent'
|
||||
},
|
||||
{
|
||||
label: i18n.translate('xpack.monitoring.elasticsearch.shardAllocation.initializingLabel', {
|
||||
defaultMessage: 'Initializing'
|
||||
}),
|
||||
color: 'default'
|
||||
},
|
||||
{
|
||||
label: i18n.translate('xpack.monitoring.elasticsearch.shardAllocation.unassignedPrimaryLabel', {
|
||||
defaultMessage: 'Unassigned Primary'
|
||||
}),
|
||||
color: 'danger'
|
||||
},
|
||||
{
|
||||
label: i18n.translate('xpack.monitoring.elasticsearch.shardAllocation.unassignedReplicaLabel', {
|
||||
defaultMessage: 'Unassigned Replica'
|
||||
}),
|
||||
color: 'warning'
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="monCluster">
|
||||
<EuiTitle>
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.elasticsearch.shardAllocation.shardLegendTitle"
|
||||
defaultMessage="Shard Legend"
|
||||
/>
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="xs"/>
|
||||
<EuiFlexGroup wrap responsive={false} gutterSize="xs">
|
||||
{
|
||||
types.map(type => (
|
||||
<EuiFlexItem grow={false} key={type.label}>
|
||||
<EuiBadge color={type.color}>
|
||||
{type.label}
|
||||
</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
))
|
||||
}
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s"/>
|
||||
<ClusterView
|
||||
scope={scope}
|
||||
shardStats={shardStats}
|
||||
kbnUrl={kbnUrl}
|
||||
showSystemIndices={scope.showSystemIndices}
|
||||
toggleShowSystemIndices={scope.toggleShowSystemIndices}
|
||||
type={type}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
|
||||
import _ from 'lodash';
|
||||
import { hasPrimaryChildren } from '../lib/hasPrimaryChildren';
|
||||
import { hasPrimaryChildren } from '../lib/has_primary_children';
|
||||
import { decorateShards } from '../lib/decorate_shards';
|
||||
|
||||
export function nodesByIndices() {
|
|
@ -28,7 +28,7 @@ function ClusterStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Instances'
|
||||
}),
|
||||
value: instances,
|
||||
dataTestSubj: 'instances'
|
||||
'data-test-subj': 'instances'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -36,7 +36,7 @@ function ClusterStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Memory'
|
||||
}),
|
||||
value: formatMetric(memSize, 'byte') + ' / ' + formatMetric(memLimit, 'byte'),
|
||||
dataTestSubj: 'memory'
|
||||
'data-test-subj': 'memory'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -44,7 +44,7 @@ function ClusterStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Requests'
|
||||
}),
|
||||
value: requests,
|
||||
dataTestSubj: 'requests'
|
||||
'data-test-subj': 'requests'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -52,7 +52,7 @@ function ClusterStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Connections'
|
||||
}),
|
||||
value: connections,
|
||||
dataTestSubj: 'connections'
|
||||
'data-test-subj': 'connections'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -60,7 +60,7 @@ function ClusterStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Max. Response Time'
|
||||
}),
|
||||
value: formatMetric(maxResponseTime, '0', 'ms'),
|
||||
dataTestSubj: 'maxResponseTime'
|
||||
'data-test-subj': 'maxResponseTime'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ function DetailStatusUI({ stats, intl }) {
|
|||
const metrics = [
|
||||
{
|
||||
value: transportAddress,
|
||||
dataTestSubj: 'transportAddress'
|
||||
'data-test-subj': 'transportAddress'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -30,7 +30,7 @@ function DetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'OS Free Memory'
|
||||
}),
|
||||
value: formatMetric(osFreeMemory, 'byte'),
|
||||
dataTestSubj: 'osFreeMemory'
|
||||
'data-test-subj': 'osFreeMemory'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -38,7 +38,7 @@ function DetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Version'
|
||||
}),
|
||||
value: version,
|
||||
dataTestSubj: 'version'
|
||||
'data-test-subj': 'version'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
|
@ -46,7 +46,7 @@ function DetailStatusUI({ stats, intl }) {
|
|||
defaultMessage: 'Uptime'
|
||||
}),
|
||||
value: formatMetric(uptime, 'time_since'),
|
||||
dataTestSubj: 'uptime'
|
||||
'data-test-subj': 'uptime'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -24,28 +24,28 @@ function ClusterStatusUi({ stats, intl }) {
|
|||
id: 'xpack.monitoring.logstash.clusterStatus.nodesLabel', defaultMessage: 'Nodes'
|
||||
}),
|
||||
value: nodeCount,
|
||||
dataTestSubj: 'node_count'
|
||||
'data-test-subj': 'node_count'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'xpack.monitoring.logstash.clusterStatus.memoryLabel', defaultMessage: 'Memory'
|
||||
}),
|
||||
value: formatMetric(avgMemoryUsed, 'byte') + ' / ' + formatMetric(avgMemory, 'byte'),
|
||||
dataTestSubj: 'memory_used'
|
||||
'data-test-subj': 'memory_used'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'xpack.monitoring.logstash.clusterStatus.eventsReceivedLabel', defaultMessage: 'Events Received'
|
||||
}),
|
||||
value: formatMetric(eventsInTotal, '0.[0]a'),
|
||||
dataTestSubj: 'events_in_total'
|
||||
'data-test-subj': 'events_in_total'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'xpack.monitoring.logstash.clusterStatus.eventsEmittedLabel', defaultMessage: 'Events Emitted'
|
||||
}),
|
||||
value: formatMetric(eventsOutTotal, '0.[0]a'),
|
||||
dataTestSubj: 'events_out_total'
|
||||
'data-test-subj': 'events_out_total'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -23,42 +23,42 @@ function DetailStatusUi({ stats, intl }) {
|
|||
const firstMetrics = [
|
||||
{
|
||||
value: httpAddress,
|
||||
dataTestSubj: 'httpAddress'
|
||||
'data-test-subj': 'httpAddress'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'xpack.monitoring.logstash.detailStatus.eventsReceivedLabel', defaultMessage: 'Events Received'
|
||||
}),
|
||||
value: formatMetric(events.in, '0.[0]a'),
|
||||
dataTestSubj: 'eventsIn'
|
||||
'data-test-subj': 'eventsIn'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'xpack.monitoring.logstash.detailStatus.eventsEmittedLabel', defaultMessage: 'Events Emitted'
|
||||
}),
|
||||
value: formatMetric(events.out, '0.[0]a'),
|
||||
dataTestSubj: 'eventsOut'
|
||||
'data-test-subj': 'eventsOut'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'xpack.monitoring.logstash.detailStatus.configReloadsLabel', defaultMessage: 'Config Reloads'
|
||||
}),
|
||||
value: reloads.successes,
|
||||
dataTestSubj: 'numReloads'
|
||||
'data-test-subj': 'numReloads'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'xpack.monitoring.logstash.detailStatus.pipelineWorkersLabel', defaultMessage: 'Pipeline Workers'
|
||||
}),
|
||||
value: pipeline.workers,
|
||||
dataTestSubj: 'pipelineWorkers'
|
||||
'data-test-subj': 'pipelineWorkers'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'xpack.monitoring.logstash.detailStatus.batchSizeLabel', defaultMessage: 'Batch Size'
|
||||
}),
|
||||
value: pipeline.batch_size,
|
||||
dataTestSubj: 'pipelineBatchSize'
|
||||
'data-test-subj': 'pipelineBatchSize'
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -68,14 +68,14 @@ function DetailStatusUi({ stats, intl }) {
|
|||
id: 'xpack.monitoring.logstash.detailStatus.versionLabel', defaultMessage: 'Version'
|
||||
}),
|
||||
value: version,
|
||||
dataTestSubj: 'version'
|
||||
'data-test-subj': 'version'
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'xpack.monitoring.logstash.detailStatus.uptimeLabel', defaultMessage: 'Uptime'
|
||||
}),
|
||||
value: formatMetric(uptime, 'time_since'),
|
||||
dataTestSubj: 'uptime'
|
||||
'data-test-subj': 'uptime'
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -87,7 +87,7 @@ function DetailStatusUi({ stats, intl }) {
|
|||
id: 'xpack.monitoring.logstash.detailStatus.queueTypeLabel', defaultMessage: 'Queue Type'
|
||||
}),
|
||||
value: queueType,
|
||||
dataTestSubj: 'queueType'
|
||||
'data-test-subj': 'queueType'
|
||||
});
|
||||
}
|
||||
metrics.push(...lastMetrics);
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { Listing } from './listing';
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { EuiPage, EuiLink, EuiPageBody, EuiPageContent, EuiSpacer } from '@elastic/eui';
|
||||
import { formatPercentageUsage, formatNumber } from '../../../lib/format_number';
|
||||
import { ClusterStatus } from '..//cluster_status';
|
||||
import { EuiMonitoringTable } from '../../table';
|
||||
import { injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
class ListingUI extends PureComponent {
|
||||
getColumns() {
|
||||
const { kbnUrl, scope } = this.props.angular;
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'Name',
|
||||
field: 'logstash.name',
|
||||
render: (name, node) => (
|
||||
<div>
|
||||
<div>
|
||||
<EuiLink
|
||||
onClick={() => {
|
||||
scope.$evalAsync(() => {
|
||||
kbnUrl.changePath(`/logstash/node/${node.logstash.uuid}`);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</EuiLink>
|
||||
</div>
|
||||
<div>
|
||||
{node.logstash.http_address}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'CPU Usage',
|
||||
field: 'process.cpu.percent',
|
||||
render: value => formatPercentageUsage(value, 100)
|
||||
},
|
||||
{
|
||||
name: 'Load Average',
|
||||
field: 'os.cpu.load_average.1m',
|
||||
render: value => formatNumber(value, '0.00')
|
||||
},
|
||||
{
|
||||
name: 'JVM Heap Used',
|
||||
field: 'jvm.mem.heap_used_percent',
|
||||
render: value => formatPercentageUsage(value, 100)
|
||||
},
|
||||
{
|
||||
name: 'Events Ingested',
|
||||
field: 'events.out',
|
||||
render: value => formatNumber(value, '0.[0]a')
|
||||
},
|
||||
{
|
||||
name: 'Config Reloads',
|
||||
render: node => (
|
||||
<div>
|
||||
<div>{ node.reloads.successes } successes</div>
|
||||
<div>{ node.reloads.failures } failures</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'Version',
|
||||
field: 'logstash.version',
|
||||
render: value => formatNumber(value)
|
||||
}
|
||||
];
|
||||
}
|
||||
render() {
|
||||
const { data, stats, sorting, pagination, onTableChange, intl } = this.props;
|
||||
const columns = this.getColumns();
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<ClusterStatus stats={stats} />
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiMonitoringTable
|
||||
className="logstashNodesTable"
|
||||
rows={data}
|
||||
columns={columns}
|
||||
sorting={{
|
||||
...sorting,
|
||||
sort: {
|
||||
...sorting.sort,
|
||||
field: 'logstash.name'
|
||||
}
|
||||
}}
|
||||
pagination={pagination}
|
||||
search={{
|
||||
box: {
|
||||
incremental: true,
|
||||
placeholder: intl.formatMessage({
|
||||
id: 'xpack.monitoring.logstash.filterNodesPlaceholder',
|
||||
defaultMessage: 'Filter Nodes…'
|
||||
})
|
||||
},
|
||||
}}
|
||||
onTableChange={onTableChange}
|
||||
/>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const Listing = injectI18n(ListingUI);
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { Node } from './node';
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { EuiPage, EuiPageBody, EuiPageContent, EuiSpacer, EuiFlexGrid, EuiFlexItem } from '@elastic/eui';
|
||||
import { DetailStatus } from '../detail_status';
|
||||
import { MonitoringTimeseriesContainer } from '../../chart';
|
||||
|
||||
export class Node extends PureComponent {
|
||||
render() {
|
||||
const { stats, metrics, ...rest } = this.props;
|
||||
|
||||
const metricsToShow = [
|
||||
metrics.logstash_events_input_rate,
|
||||
metrics.logstash_jvm_usage,
|
||||
metrics.logstash_events_output_rate,
|
||||
metrics.logstash_node_cpu_metric,
|
||||
metrics.logstash_events_latency,
|
||||
metrics.logstash_os_load,
|
||||
];
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<DetailStatus stats={stats}/>
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiFlexGrid columns={2} gutterSize="none">
|
||||
{metricsToShow.map((metric, index) => (
|
||||
<EuiFlexItem key={index} style={{ width: '50%' }}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={metric}
|
||||
{...rest}
|
||||
/>
|
||||
<EuiSpacer size="m"/>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGrid>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { Overview } from './overview';
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { EuiPage, EuiPageBody, EuiPageContent, EuiSpacer, EuiFlexGrid, EuiFlexItem } from '@elastic/eui';
|
||||
import { ClusterStatus } from '../cluster_status';
|
||||
import { MonitoringTimeseriesContainer } from '../../chart';
|
||||
|
||||
export class Overview extends PureComponent {
|
||||
render() {
|
||||
const { stats, metrics } = this.props;
|
||||
const metricsToShow = [
|
||||
metrics.logstash_cluster_events_input_rate,
|
||||
metrics.logstash_cluster_events_output_rate,
|
||||
metrics.logstash_cluster_events_latency
|
||||
];
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<ClusterStatus stats={stats} />
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiFlexGrid columns={2} gutterSize="none">
|
||||
{metricsToShow.map((metric, index) => (
|
||||
<EuiFlexItem key={index} style={{ width: '50%' }}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={metric}
|
||||
/>
|
||||
<EuiSpacer size="m"/>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGrid>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { PipelineListing } from './pipeline_listing';
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import moment from 'moment';
|
||||
import { partialRight } from 'lodash';
|
||||
import { EuiPage, EuiLink, EuiPageBody, EuiPageContent, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { formatMetric } from '../../../lib/format_number';
|
||||
import { ClusterStatus } from '../cluster_status';
|
||||
import { Sparkline } from 'plugins/monitoring/components/sparkline';
|
||||
import { EuiMonitoringTable } from '../../table';
|
||||
import { injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
class PipelineListingUI extends Component {
|
||||
tooltipXValueFormatter(xValue) {
|
||||
return moment(xValue).format(this.props.dateFormat);
|
||||
}
|
||||
|
||||
tooltipYValueFormatter(yValue, format, units) {
|
||||
return formatMetric(yValue, format, units);
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
const { onBrush } = this.props;
|
||||
const { kbnUrl, scope } = this.props.angular;
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'ID',
|
||||
field: 'id',
|
||||
sortable: true,
|
||||
render: (id) => (
|
||||
<EuiLink
|
||||
data-test-subj="id"
|
||||
onClick={() => {
|
||||
scope.$evalAsync(() => {
|
||||
kbnUrl.changePath(`/logstash/pipelines/${id}`);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{id}
|
||||
</EuiLink>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'Events Emitted Rate',
|
||||
field: 'latestThroughput',
|
||||
sortable: true,
|
||||
render: (value, pipeline) => {
|
||||
const throughput = pipeline.metrics.throughput;
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
alignItems="center"
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<Sparkline
|
||||
series={throughput.data}
|
||||
onBrush={onBrush}
|
||||
tooltip={{
|
||||
xValueFormatter: value => this.tooltipXValueFormatter(value),
|
||||
yValueFormatter: partialRight(this.tooltipYValueFormatter, throughput.metric.format, throughput.metric.units)
|
||||
}}
|
||||
options={{ xaxis: throughput.timeRange }}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
className="monTableCell__number"
|
||||
data-test-subj="eventsEmittedRate"
|
||||
>
|
||||
{ formatMetric(value, '0.[0]a', throughput.metric.units) }
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Number of Nodes',
|
||||
field: 'latestNodesCount',
|
||||
sortable: true,
|
||||
render: (value, pipeline) => {
|
||||
const nodesCount = pipeline.metrics.nodesCount;
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
alignItems="center"
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<Sparkline
|
||||
series={nodesCount.data}
|
||||
onBrush={onBrush}
|
||||
tooltip={{
|
||||
xValueFormatter: this.tooltipXValueFormatter,
|
||||
yValueFormatter: partialRight(this.tooltipYValueFormatter, nodesCount.metric.format, nodesCount.metric.units)
|
||||
}}
|
||||
options={{ xaxis: nodesCount.timeRange }}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
className="monTableCell__number"
|
||||
data-test-subj="nodeCount"
|
||||
>
|
||||
{ formatMetric(value, '0a') }
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
renderStats() {
|
||||
if (this.props.statusComponent) {
|
||||
const Component = this.props.statusComponent;
|
||||
return (
|
||||
<Component stats={this.props.stats}/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ClusterStatus stats={this.props.stats}/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
data,
|
||||
sorting,
|
||||
pagination,
|
||||
onTableChange,
|
||||
upgradeMessage,
|
||||
className,
|
||||
intl
|
||||
} = this.props;
|
||||
|
||||
const columns = this.getColumns();
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
{this.renderStats()}
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiMonitoringTable
|
||||
className={className || 'logstashNodesTable'}
|
||||
rows={data}
|
||||
columns={columns}
|
||||
sorting={{
|
||||
...sorting,
|
||||
sort: {
|
||||
...sorting.sort,
|
||||
field: 'id'
|
||||
}
|
||||
}}
|
||||
message={upgradeMessage}
|
||||
pagination={pagination}
|
||||
search={{
|
||||
box: {
|
||||
incremental: true,
|
||||
placeholder: intl.formatMessage({
|
||||
id: 'xpack.monitoring.logstash.filterPipelinesPlaceholder',
|
||||
defaultMessage: 'Filter Pipelines…'
|
||||
})
|
||||
},
|
||||
}}
|
||||
onTableChange={onTableChange}
|
||||
/>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const PipelineListing = injectI18n(PipelineListingUI);
|
|
@ -2,249 +2,226 @@
|
|||
|
||||
exports[`Summary Status Component should allow label to be optional 1`] = `
|
||||
<div
|
||||
class="monSummaryStatus"
|
||||
role="status"
|
||||
intl="[object Object]"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
class="euiStat euiStat--leftAligned"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
class="euiText euiText--small euiStat__description"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
data-test-subj="transportAddress"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
/>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<strong>
|
||||
127.0.0.1:9300
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
data-test-subj="documentCount"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
Documents:
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<strong>
|
||||
24.8k
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
Status:
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="euiTitle euiTitle--small euiStat__title"
|
||||
>
|
||||
Status:
|
||||
<span
|
||||
class="kuiStatusText"
|
||||
>
|
||||
<img
|
||||
alt="Status: yellow"
|
||||
data-test-subj="statusIcon"
|
||||
src="../plugins/monitoring/icons/health-yellow.svg"
|
||||
/>
|
||||
</span>
|
||||
Yellow
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
data-test-subj="transportAddress"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
class="euiStat euiStat--leftAligned"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
class="euiText euiText--small euiStat__description"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero eui-textNoWrap"
|
||||
>
|
||||
Status:
|
||||
<span
|
||||
class="kuiStatusText"
|
||||
>
|
||||
<img
|
||||
alt="Status: yellow"
|
||||
data-test-subj="statusIcon"
|
||||
src="../plugins/monitoring/icons/health-yellow.svg"
|
||||
/>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
Yellow
|
||||
</div>
|
||||
<p />
|
||||
</div>
|
||||
<p
|
||||
class="euiTitle euiTitle--small euiStat__title"
|
||||
>
|
||||
127.0.0.1:9300
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
data-test-subj="documentCount"
|
||||
>
|
||||
<div
|
||||
class="euiStat euiStat--leftAligned"
|
||||
>
|
||||
<div
|
||||
class="euiText euiText--small euiStat__description"
|
||||
>
|
||||
<p>
|
||||
Documents:
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="euiTitle euiTitle--small euiStat__title"
|
||||
>
|
||||
24.8k
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr
|
||||
class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginLarge"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Summary Status Component should allow status to be optional 1`] = `
|
||||
<div
|
||||
class="monSummaryStatus"
|
||||
role="status"
|
||||
intl="[object Object]"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
data-test-subj="freeDiskSpace"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
class="euiStat euiStat--leftAligned"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
class="euiText euiText--small euiStat__description"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
data-test-subj="freeDiskSpace"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
Free Disk Space:
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<strong>
|
||||
173.9 GB
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
data-test-subj="documentCount"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
Documents:
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<strong>
|
||||
24.8k
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
Free Disk Space:
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="euiTitle euiTitle--small euiStat__title"
|
||||
>
|
||||
173.9 GB
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
data-test-subj="documentCount"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
/>
|
||||
class="euiStat euiStat--leftAligned"
|
||||
>
|
||||
<div
|
||||
class="euiText euiText--small euiStat__description"
|
||||
>
|
||||
<p>
|
||||
Documents:
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="euiTitle euiTitle--small euiStat__title"
|
||||
>
|
||||
24.8k
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr
|
||||
class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginLarge"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Summary Status Component should render metrics in a summary bar 1`] = `
|
||||
<div
|
||||
class="monSummaryStatus"
|
||||
role="status"
|
||||
intl="[object Object]"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
class="euiStat euiStat--leftAligned"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
class="euiText euiText--small euiStat__description"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
data-test-subj="freeDiskSpace"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
Free Disk Space:
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<strong>
|
||||
173.9 GB
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
data-test-subj="documentCount"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
Documents:
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<strong>
|
||||
24.8k
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
Status:
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="euiTitle euiTitle--small euiStat__title"
|
||||
>
|
||||
Status:
|
||||
<span
|
||||
class="kuiStatusText"
|
||||
>
|
||||
<img
|
||||
alt="Status: green"
|
||||
data-test-subj="statusIcon"
|
||||
src="../plugins/monitoring/icons/health-green.svg"
|
||||
/>
|
||||
</span>
|
||||
Green
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
data-test-subj="freeDiskSpace"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
class="euiStat euiStat--leftAligned"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||
class="euiText euiText--small euiStat__description"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero eui-textNoWrap"
|
||||
>
|
||||
Status:
|
||||
<span
|
||||
class="kuiStatusText"
|
||||
>
|
||||
<img
|
||||
alt="Status: green"
|
||||
data-test-subj="statusIcon"
|
||||
src="../plugins/monitoring/icons/health-green.svg"
|
||||
/>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
Green
|
||||
</div>
|
||||
<p>
|
||||
Free Disk Space:
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="euiTitle euiTitle--small euiStat__title"
|
||||
>
|
||||
173.9 GB
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
data-test-subj="documentCount"
|
||||
>
|
||||
<div
|
||||
class="euiStat euiStat--leftAligned"
|
||||
>
|
||||
<div
|
||||
class="euiText euiText--small euiStat__description"
|
||||
>
|
||||
<p>
|
||||
Documents:
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="euiTitle euiTitle--small euiStat__title"
|
||||
>
|
||||
24.8k
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr
|
||||
class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginLarge"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -7,47 +7,29 @@
|
|||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { isEmpty, capitalize } from 'lodash';
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiStat, EuiHorizontalRule } from '@elastic/eui';
|
||||
import { StatusIcon } from '../status_icon/index.js';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const wrapChild = ({ label, value, dataTestSubj }, index) => (
|
||||
const wrapChild = ({ label, value, ...props }, index) => (
|
||||
<EuiFlexItem
|
||||
key={`summary-status-item-${index}`}
|
||||
grow={false}
|
||||
data-test-subj={dataTestSubj}
|
||||
{...props}
|
||||
>
|
||||
<EuiFlexGroup responsive={false} gutterSize="xs" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
{label ? label + ': ' : null}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<strong>{value}</strong>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiStat
|
||||
title={value}
|
||||
titleSize="s"
|
||||
textAlign="left"
|
||||
description={label ? `${label}:` : ''}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
|
||||
const DefaultIconComponent = ({ status }) => (
|
||||
<Fragment>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.summaryStatus.statusIconTitle"
|
||||
defaultMessage="Status: {statusIcon}"
|
||||
values={{
|
||||
statusIcon: (
|
||||
<StatusIcon
|
||||
type={status.toUpperCase()}
|
||||
label={i18n.translate('xpack.monitoring.summaryStatus.statusIconLabel', {
|
||||
defaultMessage: 'Status: {status}',
|
||||
values: {
|
||||
status
|
||||
}
|
||||
})}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
Status: {(
|
||||
<StatusIcon type={status.toUpperCase()} label={`Status: ${status}`} />
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
|
@ -57,33 +39,34 @@ const StatusIndicator = ({ status, isOnline, IconComponent }) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="xs" alignItems="center">
|
||||
<EuiFlexItem grow={false} className="eui-textNoWrap">
|
||||
<IconComponent status={status} isOnline={isOnline} />{' '}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
{capitalize(status)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexItem
|
||||
key={`summary-status-item-status`}
|
||||
grow={false}
|
||||
>
|
||||
<EuiStat
|
||||
title={(
|
||||
<Fragment>
|
||||
<IconComponent status={status} isOnline={isOnline} />
|
||||
|
||||
{capitalize(status)}
|
||||
</Fragment>
|
||||
)}
|
||||
titleSize="s"
|
||||
textAlign="left"
|
||||
description="Status:"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
export function SummaryStatus({ metrics, status, isOnline, IconComponent = DefaultIconComponent, intl, ...props }) {
|
||||
export function SummaryStatus({ metrics, status, isOnline, IconComponent = DefaultIconComponent, ...props }) {
|
||||
return (
|
||||
<div className="monSummaryStatus" role="status">
|
||||
<div {...props}>
|
||||
<EuiFlexGroup gutterSize="none" alignItems="center" justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup>
|
||||
{metrics.map(wrapChild)}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<StatusIndicator status={status} IconComponent={IconComponent} isOnline={isOnline} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
<div {...props}>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<StatusIndicator status={status} IconComponent={IconComponent} isOnline={isOnline} />
|
||||
{metrics.map(wrapChild)}
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@ describe('Summary Status Component', () => {
|
|||
{
|
||||
label: 'Free Disk Space',
|
||||
value: '173.9 GB',
|
||||
dataTestSubj: 'freeDiskSpace'
|
||||
'data-test-subj': 'freeDiskSpace'
|
||||
},
|
||||
{
|
||||
label: 'Documents',
|
||||
value: '24.8k',
|
||||
dataTestSubj: 'documentCount'
|
||||
'data-test-subj': 'documentCount'
|
||||
},
|
||||
],
|
||||
status: 'green'
|
||||
|
@ -34,12 +34,12 @@ describe('Summary Status Component', () => {
|
|||
metrics: [
|
||||
{
|
||||
value: '127.0.0.1:9300',
|
||||
dataTestSubj: 'transportAddress'
|
||||
'data-test-subj': 'transportAddress'
|
||||
},
|
||||
{
|
||||
label: 'Documents',
|
||||
value: '24.8k',
|
||||
dataTestSubj: 'documentCount'
|
||||
'data-test-subj': 'documentCount'
|
||||
},
|
||||
],
|
||||
status: 'yellow'
|
||||
|
@ -54,12 +54,12 @@ describe('Summary Status Component', () => {
|
|||
{
|
||||
label: 'Free Disk Space',
|
||||
value: '173.9 GB',
|
||||
dataTestSubj: 'freeDiskSpace'
|
||||
'data-test-subj': 'freeDiskSpace'
|
||||
},
|
||||
{
|
||||
label: 'Documents',
|
||||
value: '24.8k',
|
||||
dataTestSubj: 'documentCount'
|
||||
'data-test-subj': 'documentCount'
|
||||
},
|
||||
]
|
||||
};
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiInMemoryTable
|
||||
} from '@elastic/eui';
|
||||
|
||||
export class EuiMonitoringTable extends React.PureComponent {
|
||||
render() {
|
||||
const {
|
||||
rows: items,
|
||||
search = {},
|
||||
columns: _columns,
|
||||
...props
|
||||
} = this.props;
|
||||
|
||||
if (search.box && !search.box['data-test-subj']) {
|
||||
search.box['data-test-subj'] = 'monitoringTableToolBar';
|
||||
}
|
||||
|
||||
const columns = _columns.map(column => {
|
||||
if (!column['data-test-subj']) {
|
||||
column['data-test-subj'] = 'monitoringTableHasData';
|
||||
}
|
||||
return column;
|
||||
});
|
||||
|
||||
return (
|
||||
<div data-test-subj={`${this.props.className}Container`}>
|
||||
<EuiInMemoryTable
|
||||
items={items}
|
||||
search={search}
|
||||
columns={columns}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -5,4 +5,5 @@
|
|||
*/
|
||||
|
||||
export { MonitoringTable } from './table';
|
||||
export { tableStorageGetter, tableStorageSetter } from './storage';
|
||||
export { EuiMonitoringTable } from './eui_table';
|
||||
export { tableStorageGetter, tableStorageSetter, euiTableStorageGetter, euiTableStorageSetter } from './storage';
|
||||
|
|
|
@ -33,3 +33,26 @@ export const tableStorageSetter = keyPrefix => {
|
|||
return localStorageData;
|
||||
};
|
||||
};
|
||||
|
||||
export const euiTableStorageGetter = keyPrefix => {
|
||||
return storage => {
|
||||
const localStorageData = storage.get(STORAGE_KEY) || {};
|
||||
const sort = get(localStorageData, [ keyPrefix, 'sort' ]);
|
||||
const page = get(localStorageData, [ keyPrefix, 'page' ]);
|
||||
|
||||
return { page, sort };
|
||||
};
|
||||
};
|
||||
|
||||
export const euiTableStorageSetter = keyPrefix => {
|
||||
return (storage, { sort, page }) => {
|
||||
const localStorageData = storage.get(STORAGE_KEY) || {};
|
||||
|
||||
set(localStorageData, [ keyPrefix, 'sort' ], sort || undefined); // don`t store empty data
|
||||
set(localStorageData, [ keyPrefix, 'page' ], page || undefined);
|
||||
|
||||
storage.set(STORAGE_KEY, localStorageData);
|
||||
|
||||
return localStorageData;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,173 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { capitalize } from 'lodash';
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { EuiIcon, EuiHealth } from '@elastic/eui';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { KuiTableRowCell, KuiTableRow } from '@kbn/ui-framework/components';
|
||||
import { MonitoringTable } from 'plugins/monitoring/components/table';
|
||||
import { CALCULATE_DURATION_SINCE, SORT_DESCENDING } from '../../../common/constants';
|
||||
import { Tooltip } from 'plugins/monitoring/components/tooltip';
|
||||
import { FormattedAlert } from 'plugins/monitoring/components/alerts/formatted_alert';
|
||||
import { mapSeverity } from 'plugins/monitoring/components/alerts/map_severity';
|
||||
import { formatTimestampToDuration } from '../../../common/format_timestamp_to_duration';
|
||||
import { formatDateTimeLocal } from '../../../common/formatting';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { injectI18n, I18nProvider, FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
const linkToCategories = {
|
||||
'elasticsearch/nodes': i18n.translate('xpack.monitoring.alerts.esNodesCategoryLabel', {
|
||||
defaultMessage: 'Elasticsearch Nodes',
|
||||
}),
|
||||
'elasticsearch/indices': i18n.translate('xpack.monitoring.alerts.esIndicesCategoryLabel', {
|
||||
defaultMessage: 'Elasticsearch Indices',
|
||||
}),
|
||||
'kibana/instances': i18n.translate('xpack.monitoring.alerts.kibanaInstancesCategoryLabel', {
|
||||
defaultMessage: 'Kibana Instances',
|
||||
}),
|
||||
'logstash/instances': i18n.translate('xpack.monitoring.alerts.logstashNodesCategoryLabel', {
|
||||
defaultMessage: 'Logstash Nodes',
|
||||
}),
|
||||
};
|
||||
const filterFields = [ 'message', 'severity_group', 'prefix', 'suffix', 'metadata.link', 'since', 'timestamp', 'update_timestamp' ];
|
||||
const columns = [
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.alerts.statusColumnTitle', {
|
||||
defaultMessage: 'Status',
|
||||
}),
|
||||
sortKey: 'metadata.severity',
|
||||
sortOrder: SORT_DESCENDING
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.alerts.resolvedColumnTitle', {
|
||||
defaultMessage: 'Resolved',
|
||||
}),
|
||||
sortKey: 'resolved_timestamp'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.alerts.messageColumnTitle', {
|
||||
defaultMessage: 'Message',
|
||||
}),
|
||||
sortKey: 'message'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.alerts.categoryColumnTitle', {
|
||||
defaultMessage: 'Category',
|
||||
}),
|
||||
sortKey: 'metadata.link'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.alerts.lastCheckedColumnTitle', {
|
||||
defaultMessage: 'Last Checked',
|
||||
}),
|
||||
sortKey: 'update_timestamp'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.alerts.triggeredColumnTitle', {
|
||||
defaultMessage: 'Triggered',
|
||||
}),
|
||||
sortKey: 'timestamp'
|
||||
},
|
||||
];
|
||||
const alertRowFactory = (scope, kbnUrl) => {
|
||||
return injectI18n(props => {
|
||||
const changeUrl = target => {
|
||||
scope.$evalAsync(() => {
|
||||
kbnUrl.changePath(target);
|
||||
});
|
||||
};
|
||||
const severityIcon = mapSeverity(props.metadata.severity);
|
||||
const resolution = {
|
||||
icon: null,
|
||||
text: props.intl.formatMessage({ id: 'xpack.monitoring.alerts.notResolvedDescription',
|
||||
defaultMessage: 'Not Resolved',
|
||||
})
|
||||
};
|
||||
|
||||
if (props.resolved_timestamp) {
|
||||
resolution.text = props.intl.formatMessage({ id: 'xpack.monitoring.alerts.resolvedAgoDescription',
|
||||
defaultMessage: '{duration} ago',
|
||||
}, { duration: formatTimestampToDuration(props.resolved_timestamp, CALCULATE_DURATION_SINCE) }
|
||||
);
|
||||
} else {
|
||||
resolution.icon = (
|
||||
<EuiIcon
|
||||
type="alert"
|
||||
size="m"
|
||||
aria-label={props.intl.formatMessage({ id: 'xpack.monitoring.alerts.notResolvedAriaLabel', defaultMessage: 'Not Resolved', })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<KuiTableRow>
|
||||
<KuiTableRowCell tabIndex="0">
|
||||
<Tooltip text={severityIcon.title} placement="bottom" trigger="hover">
|
||||
<EuiHealth color={severityIcon.color} data-test-subj="alertIcon" aria-label={severityIcon.title}>
|
||||
{ capitalize(severityIcon.value) }
|
||||
</EuiHealth>
|
||||
</Tooltip>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell tabIndex="0">
|
||||
{ resolution.icon } { resolution.text }
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell tabIndex="0">
|
||||
<FormattedAlert
|
||||
prefix={props.prefix}
|
||||
suffix={props.suffix}
|
||||
message={props.message}
|
||||
metadata={props.metadata}
|
||||
changeUrl={changeUrl}
|
||||
/>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell tabIndex="0">
|
||||
{ linkToCategories[props.metadata.link] ? linkToCategories[props.metadata.link] :
|
||||
props.intl.formatMessage({ id: 'xpack.monitoring.alerts.generalCategoryLabel', defaultMessage: 'General', }) }
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell tabIndex="0">
|
||||
{ formatDateTimeLocal(props.update_timestamp) }
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell tabIndex="0">
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.alerts.triggeredAgoDescription"
|
||||
defaultMessage="{duration} ago"
|
||||
values={{ duration: formatTimestampToDuration(props.timestamp, CALCULATE_DURATION_SINCE) }}
|
||||
/>
|
||||
</KuiTableRowCell>
|
||||
</KuiTableRow>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const uiModule = uiModules.get('monitoring/directives', []);
|
||||
uiModule.directive('monitoringClusterAlertsListing', (kbnUrl, i18n) => {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: { alerts: '=' },
|
||||
link(scope, $el) {
|
||||
const filterAlertsPlaceholder = i18n('xpack.monitoring.alerts.filterAlertsPlaceholder', { defaultMessage: 'Filter Alerts…' });
|
||||
|
||||
scope.$watch('alerts', (alerts = []) => {
|
||||
const alertsTable = (
|
||||
<I18nProvider>
|
||||
<MonitoringTable
|
||||
className="alertsTable"
|
||||
rows={alerts}
|
||||
placeholder={filterAlertsPlaceholder}
|
||||
filterFields={filterFields}
|
||||
columns={columns}
|
||||
rowComponent={alertRowFactory(scope, kbnUrl)}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
render(alertsTable, $el[0]);
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
});
|
|
@ -7,15 +7,12 @@
|
|||
import './main';
|
||||
import './chart';
|
||||
import './sparkline';
|
||||
import './alerts';
|
||||
import './cluster/overview';
|
||||
import './cluster/listing';
|
||||
import './elasticsearch/cluster_status';
|
||||
import './elasticsearch/index_summary';
|
||||
import './elasticsearch/node_summary';
|
||||
import './elasticsearch/ml_job_listing';
|
||||
import './elasticsearch/shard_allocation';
|
||||
import './elasticsearch/shard_allocation/directives/clusterView';
|
||||
import './logstash/cluster_status';
|
||||
import './logstash/listing';
|
||||
import './logstash/node_summary';
|
||||
|
|
|
@ -7,160 +7,40 @@
|
|||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { Stats } from 'plugins/monitoring/components/beats';
|
||||
import { formatMetric } from 'plugins/monitoring/lib/format_number';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
SORT_ASCENDING,
|
||||
SORT_DESCENDING,
|
||||
TABLE_ACTION_UPDATE_FILTER,
|
||||
} from '../../../../common/constants';
|
||||
import {
|
||||
KuiTableRowCell,
|
||||
KuiTableRow
|
||||
} from '@kbn/ui-framework/components';
|
||||
import { MonitoringTable } from 'plugins/monitoring/components/table';
|
||||
|
||||
import {
|
||||
EuiLink,
|
||||
} from '@elastic/eui';
|
||||
|
||||
const filterFields = [ 'name', 'type', 'version', 'output' ];
|
||||
const columns = [
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.beats.instances.nameTitle', { defaultMessage: 'Name' }),
|
||||
sortKey: 'name',
|
||||
sortOrder: SORT_ASCENDING
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.beats.instances.typeTitle', { defaultMessage: 'Type' }),
|
||||
sortKey: 'type'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.beats.instances.outputEnabledTitle', { defaultMessage: 'Output Enabled' }),
|
||||
sortKey: 'output'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.beats.instances.totalEventsRateTitle', { defaultMessage: 'Total Events Rate' }),
|
||||
sortKey: 'total_events_rate',
|
||||
secondarySortOrder: SORT_DESCENDING
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.beats.instances.bytesSentRateTitle', { defaultMessage: 'Bytes Sent Rate' }),
|
||||
sortKey: 'bytes_sent_rate'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.beats.instances.outputErrorsTitle', { defaultMessage: 'Output Errors' }),
|
||||
sortKey: 'errors'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.beats.instances.allocatedMemoryTitle', { defaultMessage: 'Allocated Memory' }),
|
||||
sortKey: 'memory'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.beats.instances.versionTitle', { defaultMessage: 'Version' }),
|
||||
sortKey: 'version'
|
||||
},
|
||||
];
|
||||
const beatRowFactory = (scope, kbnUrl) => {
|
||||
return props => {
|
||||
const goToBeat = uuid => () => {
|
||||
scope.$evalAsync(() => {
|
||||
kbnUrl.changePath(`/beats/beat/${uuid}`);
|
||||
});
|
||||
};
|
||||
const applyFiltering = filterText => () => {
|
||||
props.dispatchTableAction(TABLE_ACTION_UPDATE_FILTER, filterText);
|
||||
};
|
||||
|
||||
return (
|
||||
<KuiTableRow>
|
||||
<KuiTableRowCell>
|
||||
<div className="monTableCell__name">
|
||||
<EuiLink
|
||||
onClick={goToBeat(props.uuid)}
|
||||
data-test-subj={`beatLink-${props.name}`}
|
||||
>
|
||||
{props.name}
|
||||
</EuiLink>
|
||||
</div>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
<EuiLink
|
||||
onClick={applyFiltering(props.type)}
|
||||
>
|
||||
{props.type}
|
||||
</EuiLink>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
{props.output}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
{formatMetric(props.total_events_rate, '', '/s')}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
{formatMetric(props.bytes_sent_rate, 'byte', '/s')}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
{formatMetric(props.errors, '0')}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
{formatMetric(props.memory, 'byte')}
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
<EuiLink
|
||||
onClick={applyFiltering(props.version)}
|
||||
>
|
||||
{props.version}
|
||||
</EuiLink>
|
||||
</KuiTableRowCell>
|
||||
</KuiTableRow>
|
||||
);
|
||||
};
|
||||
};
|
||||
import { Listing } from '../../../components/beats/listing/listing';
|
||||
|
||||
const uiModule = uiModules.get('monitoring/directives', []);
|
||||
uiModule.directive('monitoringBeatsListing', (kbnUrl, i18n) => {
|
||||
uiModule.directive('monitoringBeatsListing', (kbnUrl) => {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
data: '=',
|
||||
pageIndex: '=',
|
||||
filterText: '=',
|
||||
sortKey: '=',
|
||||
sortOrder: '=',
|
||||
onNewState: '=',
|
||||
sorting: '=',
|
||||
pagination: '=paginationSettings',
|
||||
onTableChange: '=',
|
||||
},
|
||||
link(scope, $el) {
|
||||
|
||||
scope.$watch('data', (data = {}) => {
|
||||
const filterBeatsPlaceholder = i18n('xpack.monitoring.beats.filterBeatsPlaceholder', { defaultMessage: 'Filter Beats…' });
|
||||
|
||||
function renderReact(data) {
|
||||
render((
|
||||
<I18nProvider>
|
||||
<div>
|
||||
<Stats stats={data.stats} />
|
||||
<div className="page-row">
|
||||
<MonitoringTable
|
||||
className="beatsTable"
|
||||
rows={data.listing}
|
||||
pageIndex={scope.pageIndex}
|
||||
filterText={scope.filterText}
|
||||
sortKey={scope.sortKey}
|
||||
sortOrder={scope.sortOrder}
|
||||
onNewState={scope.onNewState}
|
||||
placeholder={filterBeatsPlaceholder}
|
||||
filterFields={filterFields}
|
||||
columns={columns}
|
||||
rowComponent={beatRowFactory(scope, kbnUrl)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Listing
|
||||
stats={data.stats}
|
||||
data={data.listing}
|
||||
sorting={scope.sorting}
|
||||
pagination={scope.pagination}
|
||||
onTableChange={scope.onTableChange}
|
||||
angular={{
|
||||
kbnUrl,
|
||||
scope,
|
||||
}}
|
||||
/>
|
||||
</I18nProvider>
|
||||
), $el[0]);
|
||||
}
|
||||
scope.$watch('data', (data = {}) => {
|
||||
renderReact(data);
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -6,408 +6,403 @@
|
|||
|
||||
import React, { Fragment } from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { capitalize, get } from 'lodash';
|
||||
import { capitalize, partial } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import numeral from '@elastic/numeral';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import chrome from 'ui/chrome';
|
||||
import {
|
||||
KuiTableRowCell,
|
||||
KuiTableRow
|
||||
} from '@kbn/ui-framework/components';
|
||||
import {
|
||||
EuiHealth,
|
||||
EuiLink,
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
} from '@elastic/eui';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import { MonitoringTable } from 'plugins/monitoring/components/table';
|
||||
import { EuiMonitoringTable } from 'plugins/monitoring/components/table';
|
||||
import { Tooltip } from 'plugins/monitoring/components/tooltip';
|
||||
import { AlertsIndicator } from 'plugins/monitoring/components/cluster/listing/alerts_indicator';
|
||||
import { SORT_ASCENDING } from '../../../../common/constants';
|
||||
import { I18nProvider, FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const filterFields = [ 'cluster_name', 'status', 'license.type' ];
|
||||
const columns = [
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.cluster.listing.nameColumnTitle', {
|
||||
defaultMessage: 'Name',
|
||||
}),
|
||||
sortKey: 'cluster_name', sortOrder: SORT_ASCENDING
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.cluster.listing.statusColumnTitle', {
|
||||
defaultMessage: 'Status',
|
||||
}),
|
||||
sortKey: 'status'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.cluster.listing.nodesColumnTitle', {
|
||||
defaultMessage: 'Nodes',
|
||||
}),
|
||||
sortKey: 'elasticsearch.cluster_stats.nodes.count.total'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.cluster.listing.indicesColumnTitle', {
|
||||
defaultMessage: 'Indices',
|
||||
}),
|
||||
sortKey: 'elasticsearch.cluster_stats.indices.count'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.cluster.listing.dataColumnTitle', {
|
||||
defaultMessage: 'Data',
|
||||
}),
|
||||
sortKey: 'elasticsearch.cluster_stats.indices.store.size_in_bytes'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.cluster.listing.logstashColumnTitle', {
|
||||
defaultMessage: 'Logstash',
|
||||
}),
|
||||
sortKey: 'logstash.node_count'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.cluster.listing.kibanaColumnTitle', {
|
||||
defaultMessage: 'Kibana',
|
||||
}),
|
||||
sortKey: 'kibana.count'
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.cluster.listing.licenseColumnTitle', {
|
||||
defaultMessage: 'License',
|
||||
}),
|
||||
sortKey: 'license.type'
|
||||
const IsClusterSupported = ({ isSupported, children }) => {
|
||||
return isSupported ? children : '-';
|
||||
};
|
||||
|
||||
/*
|
||||
* This checks if alerts feature is supported via monitoring cluster
|
||||
* license. If the alerts feature is not supported because the prod cluster
|
||||
* license is basic, IsClusterSupported makes the status col hidden
|
||||
* completely
|
||||
*/
|
||||
const IsAlertsSupported = (props) => {
|
||||
const {
|
||||
alertsMeta = { enabled: true },
|
||||
clusterMeta = { enabled: true }
|
||||
} = props.cluster.alerts;
|
||||
if (alertsMeta.enabled && clusterMeta.enabled) {
|
||||
return <span>{ props.children }</span>;
|
||||
}
|
||||
];
|
||||
|
||||
const clusterRowFactory = (scope, globalState, kbnUrl, showLicenseExpiration) => {
|
||||
return class ClusterRow extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
const message = alertsMeta.message || clusterMeta.message;
|
||||
return (
|
||||
<Tooltip
|
||||
text={message}
|
||||
placement="bottom"
|
||||
trigger="hover"
|
||||
>
|
||||
<EuiHealth color="subdued" data-test-subj="alertIcon">
|
||||
N/A
|
||||
</EuiHealth>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
changeCluster() {
|
||||
scope.$evalAsync(() => {
|
||||
globalState.cluster_uuid = this.props.cluster_uuid;
|
||||
globalState.ccs = this.props.ccs;
|
||||
globalState.save();
|
||||
kbnUrl.changePath('/overview');
|
||||
});
|
||||
}
|
||||
|
||||
licenseWarning({ title, text }) {
|
||||
scope.$evalAsync(() => {
|
||||
toastNotifications.addWarning({ title, text, 'data-test-subj': 'monitoringLicenseWarning' });
|
||||
});
|
||||
}
|
||||
|
||||
handleClickIncompatibleLicense() {
|
||||
this.licenseWarning({
|
||||
title: (
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.incompatibleLicense.warningMessageTitle"
|
||||
defaultMessage="You can't view the {clusterName} cluster"
|
||||
values={{ clusterName: '"' + this.props.cluster_name + '"' }}
|
||||
/>
|
||||
),
|
||||
text: (
|
||||
<Fragment>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.incompatibleLicense.noMultiClusterSupportMessage"
|
||||
defaultMessage="The Basic license does not support multi-cluster monitoring."
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.incompatibleLicense.infoMessage"
|
||||
defaultMessage="Need to monitor multiple clusters? {getLicenseInfoLink} to enjoy multi-cluster monitoring."
|
||||
values={{
|
||||
getLicenseInfoLink: (
|
||||
<a href="https://www.elastic.co/subscriptions/xpack" target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.incompatibleLicense.getLicenseLinkLabel"
|
||||
defaultMessage="Get a license with full functionality"
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</Fragment>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
handleClickInvalidLicense() {
|
||||
const licensingPath = `${chrome.getBasePath()}/app/kibana#/management/elasticsearch/license_management/home`;
|
||||
|
||||
this.licenseWarning({
|
||||
title: (
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.invalidLicense.warningMessageTitle"
|
||||
defaultMessage="You can't view the {clusterName} cluster"
|
||||
values={{ clusterName: '"' + this.props.cluster_name + '"' }}
|
||||
/>
|
||||
),
|
||||
text: (
|
||||
<Fragment>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.invalidLicense.invalidInfoMessage"
|
||||
defaultMessage="The license information is invalid."
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.invalidLicense.infoMessage"
|
||||
defaultMessage="Need a license? {getBasicLicenseLink} or {getLicenseInfoLink} to enjoy multi-cluster monitoring."
|
||||
values={{
|
||||
getBasicLicenseLink: (
|
||||
<a href={licensingPath}>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.invalidLicense.getBasicLicenseLinkLabel"
|
||||
defaultMessage="Get a free Basic license"
|
||||
/>
|
||||
</a>
|
||||
),
|
||||
getLicenseInfoLink: (
|
||||
<a href="https://www.elastic.co/subscriptions/xpack" target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.invalidLicense.getLicenseLinkLabel"
|
||||
defaultMessage="get a license with full functionality"
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</Fragment>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
getClusterAction() {
|
||||
if (this.props.isSupported) {
|
||||
return (
|
||||
<EuiLink
|
||||
onClick={this.changeCluster.bind(this)}
|
||||
data-test-subj="clusterLink"
|
||||
>
|
||||
{ this.props.cluster_name }
|
||||
</EuiLink>
|
||||
);
|
||||
}
|
||||
|
||||
// not supported because license is basic/not compatible with multi-cluster
|
||||
if (this.props.license) {
|
||||
return (
|
||||
<EuiLink
|
||||
onClick={this.handleClickIncompatibleLicense.bind(this)}
|
||||
data-test-subj="clusterLink"
|
||||
>
|
||||
{ this.props.cluster_name }
|
||||
</EuiLink>
|
||||
);
|
||||
}
|
||||
|
||||
// not supported because license is invalid
|
||||
return (
|
||||
<EuiLink
|
||||
onClick={this.handleClickInvalidLicense.bind(this)}
|
||||
data-test-subj="clusterLink"
|
||||
>
|
||||
{ this.props.cluster_name }
|
||||
</EuiLink>
|
||||
);
|
||||
}
|
||||
|
||||
getLicenseInfo() {
|
||||
if (this.props.license) {
|
||||
const licenseExpiry = () => {
|
||||
if (this.props.license.expiry_date_in_millis < moment().valueOf()) {
|
||||
// license is expired
|
||||
return (
|
||||
<span className="monTableCell__clusterCellExpired">
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.licenseInfo.expiredLabel"
|
||||
defaultMessage="Expired"
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
// license is fine
|
||||
const getColumns = (
|
||||
showLicenseExpiration,
|
||||
changeCluster,
|
||||
handleClickIncompatibleLicense,
|
||||
handleClickInvalidLicense
|
||||
) => {
|
||||
return [
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.cluster.listing.nameColumnTitle', {
|
||||
defaultMessage: 'Name',
|
||||
}),
|
||||
field: 'cluster_name',
|
||||
sortable: true,
|
||||
render: (value, cluster) => {
|
||||
if (cluster.isSupported) {
|
||||
return (
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.licenseInfo.expiresLabel"
|
||||
defaultMessage="Expires {date}"
|
||||
values={{ date: moment(this.props.license.expiry_date_in_millis).format('D MMM YY') }}
|
||||
/>
|
||||
</span>
|
||||
<EuiLink
|
||||
onClick={() => changeCluster(cluster.cluster_uuid, cluster.ccs)}
|
||||
data-test-subj="clusterLink"
|
||||
>
|
||||
{ value }
|
||||
</EuiLink>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="monTableCell__clusterCellLiscense">
|
||||
{ capitalize(this.props.license.type) }
|
||||
</div>
|
||||
<div className="monTableCell__clusterCellExpiration">
|
||||
{ showLicenseExpiration ? licenseExpiry() : null }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// there is no license!
|
||||
return (
|
||||
<EuiLink
|
||||
onClick={this.handleClickInvalidLicense.bind(this)}
|
||||
>
|
||||
<EuiHealth color="subdued" data-test-subj="alertIcon">
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.licenseInfo.notAvailableDescription"
|
||||
defaultMessage="N/A"
|
||||
/>
|
||||
</EuiHealth>
|
||||
</EuiLink>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const isSupported = this.props.isSupported;
|
||||
const isClusterSupportedFactory = () => {
|
||||
return (props) => {
|
||||
if (isSupported) {
|
||||
return <span>{ props.children }</span>;
|
||||
}
|
||||
return <span>-</span>;
|
||||
};
|
||||
};
|
||||
const IsClusterSupported = isClusterSupportedFactory(isSupported);
|
||||
const classes = [];
|
||||
if (!isSupported) {
|
||||
classes.push('basic');
|
||||
}
|
||||
|
||||
/*
|
||||
* This checks if alerts feature is supported via monitoring cluster
|
||||
* license. If the alerts feature is not supported because the prod cluster
|
||||
* license is basic, IsClusterSupported makes the status col hidden
|
||||
* completely
|
||||
*/
|
||||
const IsAlertsSupported = (props) => {
|
||||
const {
|
||||
alertsMeta = { enabled: true },
|
||||
clusterMeta = { enabled: true }
|
||||
} = props.cluster.alerts;
|
||||
if (alertsMeta.enabled && clusterMeta.enabled) {
|
||||
return <span>{ props.children }</span>;
|
||||
}
|
||||
|
||||
const message = alertsMeta.message || clusterMeta.message;
|
||||
// not supported because license is basic/not compatible with multi-cluster
|
||||
if (cluster.license) {
|
||||
return (
|
||||
<EuiLink
|
||||
onClick={() => handleClickIncompatibleLicense(cluster.cluster_name)}
|
||||
data-test-subj="clusterLink"
|
||||
>
|
||||
{ value }
|
||||
</EuiLink>
|
||||
);
|
||||
}
|
||||
|
||||
// not supported because license is invalid
|
||||
return (
|
||||
<Tooltip
|
||||
text={message}
|
||||
placement="bottom"
|
||||
trigger="hover"
|
||||
<EuiLink
|
||||
onClick={() => handleClickInvalidLicense(cluster.cluster_name)}
|
||||
data-test-subj="clusterLink"
|
||||
>
|
||||
{ value }
|
||||
</EuiLink>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.cluster.listing.statusColumnTitle', {
|
||||
defaultMessage: 'Status',
|
||||
}),
|
||||
field: 'status',
|
||||
'data-test-subj': 'alertsStatus',
|
||||
sortable: true,
|
||||
render: (_status, cluster) => (
|
||||
<IsClusterSupported {...cluster}>
|
||||
<IsAlertsSupported cluster={cluster}>
|
||||
<AlertsIndicator alerts={cluster.alerts} />
|
||||
</IsAlertsSupported>
|
||||
</IsClusterSupported>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.cluster.listing.nodesColumnTitle', {
|
||||
defaultMessage: 'Nodes',
|
||||
}),
|
||||
field: 'elasticsearch.cluster_stats.nodes.count.total',
|
||||
'data-test-subj': 'nodesCount',
|
||||
sortable: true,
|
||||
render: (total, cluster) => (
|
||||
<IsClusterSupported {...cluster}>
|
||||
{ numeral(total).format('0,0') }
|
||||
</IsClusterSupported>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.cluster.listing.indicesColumnTitle', {
|
||||
defaultMessage: 'Indices',
|
||||
}),
|
||||
field: 'elasticsearch.cluster_stats.indices.count',
|
||||
'data-test-subj': 'indicesCount',
|
||||
sortable: true,
|
||||
render: (count, cluster) => (
|
||||
<IsClusterSupported {...cluster}>
|
||||
{ numeral(count).format('0,0') }
|
||||
</IsClusterSupported>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.cluster.listing.dataColumnTitle', {
|
||||
defaultMessage: 'Data',
|
||||
}),
|
||||
field: 'elasticsearch.cluster_stats.indices.store.size_in_bytes',
|
||||
'data-test-subj': 'dataSize',
|
||||
sortable: true,
|
||||
render: (size, cluster) => (
|
||||
<IsClusterSupported {...cluster}>
|
||||
{ numeral(size).format('0,0[.]0 b')}
|
||||
</IsClusterSupported>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.cluster.listing.logstashColumnTitle', {
|
||||
defaultMessage: 'Logstash',
|
||||
}),
|
||||
field: 'logstash.node_count',
|
||||
'data-test-subj': 'logstashCount',
|
||||
sortable: true,
|
||||
render: (count, cluster) => (
|
||||
<IsClusterSupported {...cluster}>
|
||||
{ numeral(count).format('0,0') }
|
||||
</IsClusterSupported>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.cluster.listing.kibanaColumnTitle', {
|
||||
defaultMessage: 'Kibana',
|
||||
}),
|
||||
field: 'kibana.count',
|
||||
'data-test-subj': 'kibanaCount',
|
||||
sortable: true,
|
||||
render: (count, cluster) => (
|
||||
<IsClusterSupported {...cluster}>
|
||||
{ numeral(count).format('0,0') }
|
||||
</IsClusterSupported>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.monitoring.cluster.listing.licenseColumnTitle', {
|
||||
defaultMessage: 'License',
|
||||
}),
|
||||
field: 'license.type',
|
||||
'data-test-subj': 'clusterLicense',
|
||||
sortable: true,
|
||||
render: (licenseType, cluster) => {
|
||||
const license = cluster.license;
|
||||
if (license) {
|
||||
const licenseExpiry = () => {
|
||||
if (license.expiry_date_in_millis < moment().valueOf()) {
|
||||
// license is expired
|
||||
return (
|
||||
<span className="monTableCell__clusterCellExpired">
|
||||
Expired
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
// license is fine
|
||||
return (
|
||||
<span>
|
||||
Expires { moment(license.expiry_date_in_millis).format('D MMM YY') }
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="monTableCell__clusterCellLiscense">
|
||||
{ capitalize(licenseType) }
|
||||
</div>
|
||||
<div className="monTableCell__clusterCellExpiration">
|
||||
{ showLicenseExpiration ? licenseExpiry() : null }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// there is no license!
|
||||
return (
|
||||
<EuiLink
|
||||
onClick={() => handleClickInvalidLicense(cluster.cluster_name)}
|
||||
>
|
||||
<EuiHealth color="subdued" data-test-subj="alertIcon">
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.alerts.notAvailableLabel"
|
||||
defaultMessage="N/A"
|
||||
/>
|
||||
N/A
|
||||
</EuiHealth>
|
||||
</Tooltip>
|
||||
</EuiLink>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<KuiTableRow data-test-subj={`clusterRow_${this.props.cluster_uuid}`}>
|
||||
<KuiTableRowCell>
|
||||
<span className="monTableCell__name">
|
||||
{ this.getClusterAction() }
|
||||
</span>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell data-test-subj="alertsStatus">
|
||||
<IsClusterSupported>
|
||||
<IsAlertsSupported cluster={this.props}>
|
||||
<AlertsIndicator alerts={this.props.alerts} />
|
||||
</IsAlertsSupported>
|
||||
</IsClusterSupported>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell data-test-subj="nodesCount">
|
||||
<IsClusterSupported>
|
||||
{ numeral(get(this.props, 'elasticsearch.cluster_stats.nodes.count.total')).format('0,0') }
|
||||
</IsClusterSupported>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell data-test-subj="indicesCount">
|
||||
<IsClusterSupported>
|
||||
{ numeral(get(this.props, 'elasticsearch.cluster_stats.indices.count')).format('0,0') }
|
||||
</IsClusterSupported>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell data-test-subj="dataSize">
|
||||
<IsClusterSupported>
|
||||
{ numeral(get(this.props, 'elasticsearch.cluster_stats.indices.store.size_in_bytes')).format('0,0[.]0 b') }
|
||||
</IsClusterSupported>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell data-test-subj="logstashCount">
|
||||
<IsClusterSupported>
|
||||
{ numeral(get(this.props, 'logstash.node_count')).format('0,0') }
|
||||
</IsClusterSupported>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell data-test-subj="kibanaCount">
|
||||
<IsClusterSupported>
|
||||
{ numeral(get(this.props, 'kibana.count')).format('0,0') }
|
||||
</IsClusterSupported>
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell data-test-subj="clusterLicense">
|
||||
{ this.getLicenseInfo() }
|
||||
</KuiTableRowCell>
|
||||
</KuiTableRow>
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
const changeCluster = (scope, globalState, kbnUrl, clusterUuid, ccs) => {
|
||||
scope.$evalAsync(() => {
|
||||
globalState.cluster_uuid = clusterUuid;
|
||||
globalState.ccs = ccs;
|
||||
globalState.save();
|
||||
kbnUrl.changePath('/overview');
|
||||
});
|
||||
};
|
||||
|
||||
const licenseWarning = (scope, { title, text }) => {
|
||||
scope.$evalAsync(() => {
|
||||
toastNotifications.addWarning({ title, text, 'data-test-subj': 'monitoringLicenseWarning' });
|
||||
});
|
||||
};
|
||||
|
||||
const handleClickIncompatibleLicense = (scope, clusterName) => {
|
||||
licenseWarning(scope, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.incompatibleLicense.warningMessageTitle"
|
||||
defaultMessage="You can't view the {clusterName} cluster"
|
||||
values={{ clusterName: '"' + clusterName + '"' }}
|
||||
/>
|
||||
),
|
||||
text: (
|
||||
<Fragment>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.incompatibleLicense.noMultiClusterSupportMessage"
|
||||
defaultMessage="The Basic license does not support multi-cluster monitoring."
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.incompatibleLicense.infoMessage"
|
||||
defaultMessage="Need to monitor multiple clusters? {getLicenseInfoLink} to enjoy multi-cluster monitoring."
|
||||
values={{
|
||||
getLicenseInfoLink: (
|
||||
<a href="https://www.elastic.co/subscriptions/xpack" target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.incompatibleLicense.getLicenseLinkLabel"
|
||||
defaultMessage="Get a license with full functionality"
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</Fragment>
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
const handleClickInvalidLicense = (scope, clusterName) => {
|
||||
const licensingPath = `${chrome.getBasePath()}/app/kibana#/management/elasticsearch/license_management/home`;
|
||||
|
||||
licenseWarning(scope, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.invalidLicense.warningMessageTitle"
|
||||
defaultMessage="You can't view the {clusterName} cluster"
|
||||
values={{ clusterName: '"' + clusterName + '"' }}
|
||||
/>
|
||||
),
|
||||
text: (
|
||||
<Fragment>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.invalidLicense.invalidInfoMessage"
|
||||
defaultMessage="The license information is invalid."
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.invalidLicense.infoMessage"
|
||||
defaultMessage="Need a license? {getBasicLicenseLink} or {getLicenseInfoLink} to enjoy multi-cluster monitoring."
|
||||
values={{
|
||||
getBasicLicenseLink: (
|
||||
<a href={licensingPath}>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.invalidLicense.getBasicLicenseLinkLabel"
|
||||
defaultMessage="Get a free Basic license"
|
||||
/>
|
||||
</a>
|
||||
),
|
||||
getLicenseInfoLink: (
|
||||
<a href="https://www.elastic.co/subscriptions/xpack" target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.listing.invalidLicense.getLicenseLinkLabel"
|
||||
defaultMessage="get a license with full functionality"
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</Fragment>
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
const uiModule = uiModules.get('monitoring/directives', []);
|
||||
uiModule.directive('monitoringClusterListing', ($injector, i18n) => {
|
||||
uiModule.directive('monitoringClusterListing', ($injector) => {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
clusters: '=',
|
||||
pageIndex: '=',
|
||||
sorting: '=',
|
||||
filterText: '=',
|
||||
sortKey: '=',
|
||||
sortOrder: '=',
|
||||
onNewState: '=',
|
||||
paginationSettings: '=pagination',
|
||||
onTableChange: '=',
|
||||
},
|
||||
link(scope, $el) {
|
||||
const globalState = $injector.get('globalState');
|
||||
const kbnUrl = $injector.get('kbnUrl');
|
||||
const showLicenseExpiration = $injector.get('showLicenseExpiration');
|
||||
const filterClustersPlaceholder = i18n('xpack.monitoring.cluster.listing.filterClustersPlaceholder',
|
||||
{ defaultMessage: 'Filter Clusters…' }
|
||||
);
|
||||
|
||||
const _changeCluster = partial(changeCluster, scope, globalState, kbnUrl);
|
||||
const _handleClickIncompatibleLicense = partial(handleClickIncompatibleLicense, scope);
|
||||
const _handleClickInvalidLicense = partial(handleClickInvalidLicense, scope);
|
||||
|
||||
const { sorting, pagination, onTableChange } = scope;
|
||||
|
||||
scope.$watch('clusters', (clusters = []) => {
|
||||
const clusterTable = (
|
||||
<I18nProvider>
|
||||
<MonitoringTable
|
||||
className="clusterTable"
|
||||
rows={clusters}
|
||||
pageIndex={scope.pageIndex}
|
||||
filterText={scope.filterText}
|
||||
sortKey={scope.sortKey}
|
||||
sortOrder={scope.sortOrder}
|
||||
onNewState={scope.onNewState}
|
||||
placeholder={filterClustersPlaceholder}
|
||||
filterFields={filterFields}
|
||||
columns={columns}
|
||||
rowComponent={clusterRowFactory(scope, globalState, kbnUrl, showLicenseExpiration)}
|
||||
/>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<EuiMonitoringTable
|
||||
className="clusterTable"
|
||||
rows={clusters}
|
||||
columns={getColumns(
|
||||
showLicenseExpiration,
|
||||
_changeCluster,
|
||||
_handleClickIncompatibleLicense,
|
||||
_handleClickInvalidLicense
|
||||
)}
|
||||
rowProps={item => {
|
||||
return {
|
||||
'data-test-subj': `clusterRow_${item.cluster_uuid}`
|
||||
};
|
||||
}}
|
||||
sorting={{
|
||||
...sorting,
|
||||
sort: {
|
||||
...sorting.sort,
|
||||
field: 'cluster_name'
|
||||
}
|
||||
}}
|
||||
pagination={pagination}
|
||||
search={{
|
||||
box: {
|
||||
incremental: true,
|
||||
placeholder: scope.filterText
|
||||
},
|
||||
}}
|
||||
onTableChange={onTableChange}
|
||||
/>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</I18nProvider>
|
||||
);
|
||||
render(clusterTable, $el[0]);
|
||||
|
|
|
@ -9,150 +9,156 @@ import numeral from '@elastic/numeral';
|
|||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import {
|
||||
KuiTableRowCell,
|
||||
KuiTableRow
|
||||
} from '@kbn/ui-framework/components';
|
||||
import { MonitoringTable } from 'plugins/monitoring/components/table';
|
||||
import { EuiMonitoringTable } from 'plugins/monitoring/components/table';
|
||||
import { MachineLearningJobStatusIcon } from 'plugins/monitoring/components/elasticsearch/ml_job_listing/status_icon';
|
||||
import { SORT_ASCENDING } from '../../../../common/constants';
|
||||
import { LARGE_ABBREVIATED, LARGE_BYTES } from '../../../../common/formatting';
|
||||
import {
|
||||
EuiLink,
|
||||
EuiPage,
|
||||
EuiPageContent,
|
||||
EuiPageBody,
|
||||
} from '@elastic/eui';
|
||||
import { ClusterStatus } from '../../../components/elasticsearch/cluster_status';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { I18nProvider, FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
const filterFields = [ 'job_id', 'state', 'node.name' ];
|
||||
const columns = [
|
||||
const getColumns = (kbnUrl, scope) => ([
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.jobIdTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.jobIdTitle', {
|
||||
defaultMessage: 'Job ID'
|
||||
}),
|
||||
sortKey: 'job_id',
|
||||
sortOrder: SORT_ASCENDING
|
||||
field: 'job_id',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.stateTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.stateTitle', {
|
||||
defaultMessage: 'State'
|
||||
}),
|
||||
sortKey: 'state'
|
||||
field: 'state',
|
||||
sortable: true,
|
||||
render: state => (
|
||||
<div>
|
||||
<MachineLearningJobStatusIcon status={state} />
|
||||
{ capitalize(state) }
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.processedRecordsTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.processedRecordsTitle', {
|
||||
defaultMessage: 'Processed Records'
|
||||
}),
|
||||
sortKey: 'data_counts.processed_record_count'
|
||||
field: 'data_counts.processed_record_count',
|
||||
sortable: true,
|
||||
render: value => (
|
||||
<span>
|
||||
{numeral(value).format(LARGE_ABBREVIATED)}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.modelSizeTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.modelSizeTitle', {
|
||||
defaultMessage: 'Model Size'
|
||||
}),
|
||||
sortKey: 'model_size_stats.model_bytes'
|
||||
field: 'model_size_stats.model_bytes',
|
||||
sortable: true,
|
||||
render: value => (
|
||||
<span>
|
||||
{numeral(value).format(LARGE_BYTES)}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.forecastsTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.forecastsTitle', {
|
||||
defaultMessage: 'Forecasts'
|
||||
}),
|
||||
sortKey: 'forecasts_stats.total'
|
||||
field: 'forecasts_stats.total',
|
||||
sortable: true,
|
||||
render: value => (
|
||||
<span>
|
||||
{numeral(value).format(LARGE_ABBREVIATED)}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.nodeTitle', {
|
||||
name: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.nodeTitle', {
|
||||
defaultMessage: 'Node'
|
||||
}),
|
||||
sortKey: 'node.name'
|
||||
}
|
||||
];
|
||||
const jobRowFactory = (scope, kbnUrl) => {
|
||||
const goToNode = nodeId => {
|
||||
scope.$evalAsync(() => kbnUrl.changePath(`/elasticsearch/nodes/${nodeId}`));
|
||||
};
|
||||
const getNode = node => {
|
||||
if (node) {
|
||||
field: 'node.name',
|
||||
sortable: true,
|
||||
render: (name, node) => {
|
||||
if (node) {
|
||||
return (
|
||||
<EuiLink
|
||||
onClick={() => {
|
||||
scope.$evalAsync(() => kbnUrl.changePath(`/elasticsearch/nodes/${node.id}`));
|
||||
}}
|
||||
>
|
||||
{ name }
|
||||
</EuiLink>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiLink
|
||||
onClick={goToNode.bind(null, node.id)}
|
||||
>
|
||||
{ node.name }
|
||||
</EuiLink>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.elasticsearch.mlJobListing.noDataLabel"
|
||||
defaultMessage="N/A"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.noDataLabel', {
|
||||
defaultMessage: 'N/A'
|
||||
});
|
||||
};
|
||||
|
||||
return function JobRow(props) {
|
||||
return (
|
||||
<KuiTableRow>
|
||||
<KuiTableRowCell>{ props.job_id }</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
<MachineLearningJobStatusIcon status={props.state} />
|
||||
{ capitalize(props.state) }
|
||||
</KuiTableRowCell>
|
||||
<KuiTableRowCell>{ numeral(props.data_counts.processed_record_count).format(LARGE_ABBREVIATED) }</KuiTableRowCell>
|
||||
<KuiTableRowCell>{ numeral(props.model_size_stats.model_bytes).format(LARGE_BYTES) }</KuiTableRowCell>
|
||||
<KuiTableRowCell>{ numeral(props.forecasts_stats.total).format(LARGE_ABBREVIATED) }</KuiTableRowCell>
|
||||
<KuiTableRowCell>
|
||||
{ getNode(props.node) }
|
||||
</KuiTableRowCell>
|
||||
</KuiTableRow>
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
||||
const uiModule = uiModules.get('monitoring/directives', []);
|
||||
uiModule.directive('monitoringMlListing', (kbnUrl, i18n) => {
|
||||
uiModule.directive('monitoringMlListing', kbnUrl => {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
jobs: '=',
|
||||
pageIndex: '=',
|
||||
filterText: '=',
|
||||
sortKey: '=',
|
||||
sortOrder: '=',
|
||||
onNewState: '=',
|
||||
paginationSettings: '=',
|
||||
sorting: '=',
|
||||
onTableChange: '=',
|
||||
status: '=',
|
||||
},
|
||||
link(scope, $el) {
|
||||
const columns = getColumns(kbnUrl, scope);
|
||||
|
||||
const getNoDataMessage = filterText => {
|
||||
if (filterText) {
|
||||
return (
|
||||
i18n('xpack.monitoring.elasticsearch.mlJobListing.noFilteredJobsDescription', {
|
||||
// eslint-disable-next-line max-len
|
||||
defaultMessage: 'There are no Machine Learning Jobs that match the filter [{filterText}] or the time range. Try changing the filter or time range.',
|
||||
values: {
|
||||
filterText: filterText.trim()
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
return i18n('xpack.monitoring.elasticsearch.mlJobListing.noJobsDescription', {
|
||||
defaultMessage: 'There are no Machine Learning Jobs that match your query. Try changing the time range selection.'
|
||||
});
|
||||
};
|
||||
|
||||
const filterJobsPlaceholder = i18n('xpack.monitoring.elasticsearch.mlJobListing.filterJobsPlaceholder', {
|
||||
const filterJobsPlaceholder = i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.filterJobsPlaceholder', {
|
||||
defaultMessage: 'Filter Jobs…'
|
||||
});
|
||||
|
||||
scope.$watch('jobs', (jobs = []) => {
|
||||
const mlTable = (
|
||||
<I18nProvider>
|
||||
<MonitoringTable
|
||||
className="mlJobsTable"
|
||||
rows={jobs}
|
||||
pageIndex={scope.pageIndex}
|
||||
filterText={scope.filterText}
|
||||
sortKey={scope.sortKey}
|
||||
sortOrder={scope.sortOrder}
|
||||
onNewState={scope.onNewState}
|
||||
placeholder={filterJobsPlaceholder}
|
||||
filterFields={filterFields}
|
||||
columns={columns}
|
||||
rowComponent={jobRowFactory(scope, kbnUrl)}
|
||||
getNoDataMessage={getNoDataMessage}
|
||||
/>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<ClusterStatus stats={scope.status} />
|
||||
<EuiMonitoringTable
|
||||
className="mlJobsTable"
|
||||
rows={jobs}
|
||||
columns={columns}
|
||||
sorting={{
|
||||
...scope.sorting,
|
||||
sort: {
|
||||
...scope.sorting.sort,
|
||||
field: 'job_id'
|
||||
}
|
||||
}}
|
||||
pagination={scope.paginationSettings}
|
||||
message={i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.noJobsDescription', {
|
||||
defaultMessage: 'There are no Machine Learning Jobs that match your query. Try changing the time range selection.'
|
||||
})}
|
||||
search={{
|
||||
box: {
|
||||
incremental: true,
|
||||
placeholder: filterJobsPlaceholder
|
||||
},
|
||||
}}
|
||||
onTableChange={scope.onTableChange}
|
||||
/>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</I18nProvider>
|
||||
);
|
||||
render(mlTable, $el[0]);
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { ClusterView } from '../components/clusterView';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
|
||||
const uiModule = uiModules.get('monitoring/directives', []);
|
||||
uiModule.directive('clusterView', kbnUrl => {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
totalCount: '=',
|
||||
filter: '=',
|
||||
showing: '=',
|
||||
labels: '=',
|
||||
shardStats: '=',
|
||||
showSystemIndices: '=',
|
||||
toggleShowSystemIndices: '='
|
||||
},
|
||||
link: function (scope, element) {
|
||||
ReactDOM.render(
|
||||
<I18nProvider>
|
||||
<ClusterView
|
||||
scope={scope}
|
||||
kbnUrl={kbnUrl}
|
||||
showSystemIndices={scope.showSystemIndices}
|
||||
toggleShowSystemIndices={scope.toggleShowSystemIndices}
|
||||
/>
|
||||
</I18nProvider>,
|
||||
element[0]
|
||||
);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,57 +0,0 @@
|
|||
<div class="page-row">
|
||||
<div class="monCluster">
|
||||
<h1
|
||||
class="monClusterTitle"
|
||||
i18n-id="xpack.monitoring.elasticsearch.shardAllocation.shardLegendTitle"
|
||||
i18n-default-message="Shard Legend"
|
||||
></h1>
|
||||
<div class="legend">
|
||||
<span class="shard"> </span>
|
||||
<span
|
||||
class="shard-label"
|
||||
i18n-id="xpack.monitoring.elasticsearch.shardAllocation.primaryLabel"
|
||||
i18n-default-message="Primary"
|
||||
></span>
|
||||
<span class="shard replica"> </span>
|
||||
<span
|
||||
class="shard-label"
|
||||
i18n-id="xpack.monitoring.elasticsearch.shardAllocation.replicaLabel"
|
||||
i18n-default-message="Replica"
|
||||
></span>
|
||||
<span class="shard relocating"> </span>
|
||||
<span
|
||||
class="shard-label"
|
||||
i18n-id="xpack.monitoring.elasticsearch.shardAllocation.relocatingLabel"
|
||||
i18n-default-message="Relocating"
|
||||
></span>
|
||||
<span class="shard initializing"> </span>
|
||||
<span
|
||||
class="shard-label"
|
||||
i18n-id="xpack.monitoring.elasticsearch.shardAllocation.initializingLabel"
|
||||
i18n-default-message="Initializing"
|
||||
></span>
|
||||
<span class="shard emergency" ng-if="isIndexView"> </span>
|
||||
<span
|
||||
class="shard-label"
|
||||
ng-if="isIndexView"
|
||||
i18n-id="xpack.monitoring.elasticsearch.shardAllocation.unassignedPrimaryLabel"
|
||||
i18n-default-message="Unassigned Primary"
|
||||
></span>
|
||||
<span class="shard unassigned replica" ng-if="isIndexView"> </span>
|
||||
<span
|
||||
class="shard-label"
|
||||
ng-if="isIndexView"
|
||||
i18n-id="xpack.monitoring.elasticsearch.shardAllocation.unassignedReplicaLabel"
|
||||
i18n-default-message="Unassigned Replica"
|
||||
></span>
|
||||
</div>
|
||||
<cluster-view
|
||||
shard-stats="shardStats"
|
||||
total-count="totalCount"
|
||||
labels="labels"
|
||||
showing="showing"
|
||||
show-system-indices="showSystemIndices"
|
||||
toggle-show-system-indices="toggleShowSystemIndices"
|
||||
></cluster-view>
|
||||
</div>
|
||||
</div>
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { labels } from './lib/labels';
|
||||
import { indicesByNodes } from './transformers/indices_by_nodes';
|
||||
import { nodesByIndices } from './transformers/nodes_by_indices';
|
||||
import template from './index.html';
|
||||
|
||||
const uiModule = uiModules.get('monitoring/directives', []);
|
||||
uiModule.directive('monitoringShardAllocation', () => {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template,
|
||||
scope: {
|
||||
view: '@',
|
||||
shards: '=',
|
||||
nodes: '=',
|
||||
shardStats: '=',
|
||||
showSystemIndices: '=',
|
||||
toggleShowSystemIndices: '='
|
||||
},
|
||||
link: (scope) => {
|
||||
|
||||
const isIndexView = scope.view === 'index';
|
||||
const transformer = (isIndexView) ? indicesByNodes() : nodesByIndices();
|
||||
|
||||
scope.isIndexView = isIndexView;
|
||||
|
||||
scope.$watch('shards', (shards) => {
|
||||
let view = scope.view;
|
||||
scope.totalCount = shards.length;
|
||||
scope.showing = transformer(shards, scope.nodes);
|
||||
if (isIndexView && shards.some((shard) => shard.state === 'UNASSIGNED')) {
|
||||
view = 'indexWithUnassigned';
|
||||
}
|
||||
scope.labels = labels[view];
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
});
|
|
@ -20,4 +20,4 @@
|
|||
@import 'components/table/index';
|
||||
@import 'components/logstash/pipeline_viewer/views/index';
|
||||
@import 'directives/chart/index';
|
||||
@import 'directives/elasticsearch/shard_allocation/index';
|
||||
@import 'components/elasticsearch/shard_allocation/index';
|
||||
|
|
|
@ -1,28 +1,3 @@
|
|||
<monitoring-main name="alerts">
|
||||
<div class="page-row">
|
||||
<p ng-if="alerts.data.message">
|
||||
{{ alerts.data.message }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page-row">
|
||||
<h3
|
||||
class="kuiScreenReaderOnly"
|
||||
i18n-id="xpack.monitoring.alerts.alertsTitle"
|
||||
i18n-default-message="Alerts"
|
||||
>
|
||||
</h3>
|
||||
<monitoring-cluster-alerts-listing alerts="alerts.data"></monitoring-cluster-alerts-listing>
|
||||
</div>
|
||||
|
||||
<div class="page-row">
|
||||
<p>
|
||||
<a
|
||||
kbn-href="#/overview"
|
||||
i18n-id="xpack.monitoring.alerts.clusterOverviewLinkLabel"
|
||||
i18n-default-message="« Cluster Overview"
|
||||
>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div id="monitoringAlertsApp"></div>
|
||||
</monitoring-main>
|
||||
|
|
|
@ -4,13 +4,18 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { find, get } from 'lodash';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import template from './index.html';
|
||||
import { MonitoringViewBaseController } from 'plugins/monitoring/views';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { Alerts } from '../../components/alerts';
|
||||
import { MonitoringViewBaseEuiTableController } from '../base_eui_table_controller';
|
||||
import { I18nProvider, FormattedMessage } from '@kbn/i18n/react';
|
||||
import { EuiPage, EuiPageBody, EuiPageContent, EuiSpacer, EuiLink } from '@elastic/eui';
|
||||
|
||||
function getPageData($injector) {
|
||||
const globalState = $injector.get('globalState');
|
||||
|
@ -44,10 +49,11 @@ uiRoutes.when('/alerts', {
|
|||
alerts: getPageData
|
||||
},
|
||||
controllerAs: 'alerts',
|
||||
controller: class AlertsView extends MonitoringViewBaseController {
|
||||
controller: class AlertsView extends MonitoringViewBaseEuiTableController {
|
||||
constructor($injector, $scope, i18n) {
|
||||
const $route = $injector.get('$route');
|
||||
const globalState = $injector.get('globalState');
|
||||
const kbnUrl = $injector.get('kbnUrl');
|
||||
|
||||
// breadcrumbs + page title
|
||||
$scope.cluster = find($route.current.locals.clusters, { cluster_uuid: globalState.cluster_uuid });
|
||||
|
@ -60,6 +66,41 @@ uiRoutes.when('/alerts', {
|
|||
});
|
||||
|
||||
this.data = $route.current.locals.alerts;
|
||||
|
||||
const renderReact = data => {
|
||||
const app = data.message
|
||||
? (<p>{data.message}</p>)
|
||||
: (<Alerts
|
||||
alerts={data}
|
||||
angular={{ kbnUrl, scope: $scope }}
|
||||
sorting={this.sorting}
|
||||
pagination={this.pagination}
|
||||
onTableChange={this.onTableChange}
|
||||
/>);
|
||||
|
||||
render(
|
||||
<I18nProvider>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
{app}
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiLink
|
||||
href="#/overview"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.alerts.clusterOverviewLinkLabel"
|
||||
defaultMessage="« Cluster Overview"
|
||||
/>
|
||||
</EuiLink>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</I18nProvider>,
|
||||
document.getElementById('monitoringAlertsApp')
|
||||
);
|
||||
};
|
||||
$scope.$watch(() => this.data, data => renderReact(data));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ import uiRoutes from'ui/routes';
|
|||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import template from './index.html';
|
||||
import { ApmServerInstances } from '../../../components/apm/instances';
|
||||
import { MonitoringViewBaseTableController } from '../../base_table_controller';
|
||||
import { MonitoringViewBaseEuiTableController } from '../..';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
|
||||
uiRoutes.when('/apm/instances', {
|
||||
|
@ -21,7 +21,7 @@ uiRoutes.when('/apm/instances', {
|
|||
return routeInit();
|
||||
},
|
||||
},
|
||||
controller: class extends MonitoringViewBaseTableController {
|
||||
controller: class extends MonitoringViewBaseEuiTableController {
|
||||
constructor($injector, $scope, i18n) {
|
||||
const $route = $injector.get('$route');
|
||||
const globalState = $injector.get('globalState');
|
||||
|
@ -51,22 +51,18 @@ uiRoutes.when('/apm/instances', {
|
|||
|
||||
renderReact(data) {
|
||||
const {
|
||||
pageIndex,
|
||||
filterText,
|
||||
sortKey,
|
||||
sortOrder,
|
||||
onNewState,
|
||||
pagination,
|
||||
sorting,
|
||||
onTableChange,
|
||||
} = this;
|
||||
|
||||
const component = (
|
||||
<I18nProvider>
|
||||
<ApmServerInstances
|
||||
apms={{
|
||||
pageIndex,
|
||||
filterText,
|
||||
sortKey,
|
||||
sortOrder,
|
||||
onNewState,
|
||||
pagination,
|
||||
sorting,
|
||||
onTableChange,
|
||||
data,
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { MonitoringViewBaseController } from './';
|
||||
import { euiTableStorageGetter, euiTableStorageSetter } from 'plugins/monitoring/components/table';
|
||||
import { EUI_SORT_ASCENDING } from '../../common/constants';
|
||||
|
||||
/**
|
||||
* Class to manage common instantiation behaviors in a view controller
|
||||
* And add persistent state to a table:
|
||||
* - page index: in table pagination, which page are we looking at
|
||||
* - filter text: what filter was entered in the table's filter bar
|
||||
* - sortKey: which column field of table data is used for sorting
|
||||
* - sortOrder: is sorting ordered ascending or descending
|
||||
*
|
||||
* This is expected to be extended, and behavior enabled using super();
|
||||
*/
|
||||
export class MonitoringViewBaseEuiTableController extends MonitoringViewBaseController {
|
||||
|
||||
/**
|
||||
* Create a table view controller
|
||||
* - used by parent class:
|
||||
* @param {String} title - Title of the page
|
||||
* @param {Function} getPageData - Function to fetch page data
|
||||
* @param {Service} $injector - Angular dependency injection service
|
||||
* @param {Service} $scope - Angular view data binding service
|
||||
* @param {Boolean} options.enableTimeFilter - Whether to show the time filter
|
||||
* @param {Boolean} options.enableAutoRefresh - Whether to show the auto refresh control
|
||||
* - specific to this class:
|
||||
* @param {String} storageKey - the namespace that will be used to keep the state data in the Monitoring localStorage object
|
||||
*
|
||||
*/
|
||||
constructor(args) {
|
||||
super(args);
|
||||
const { storageKey, $injector } = args;
|
||||
const storage = $injector.get('localStorage');
|
||||
|
||||
const getLocalStorageData = euiTableStorageGetter(storageKey);
|
||||
const setLocalStorageData = euiTableStorageSetter(storageKey);
|
||||
const { page, sort } = getLocalStorageData(storage);
|
||||
|
||||
this.pagination = page || {
|
||||
initialPageSize: 20,
|
||||
pageSizeOptions: [5, 10, 20, 50]
|
||||
};
|
||||
|
||||
this.sorting = sort || {
|
||||
sort: {
|
||||
field: 'name',
|
||||
direction: EUI_SORT_ASCENDING
|
||||
}
|
||||
};
|
||||
|
||||
this.onTableChange = ({ page, sort }) => {
|
||||
setLocalStorageData(storage, {
|
||||
page,
|
||||
sort: {
|
||||
sort
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
|
@ -50,5 +50,4 @@ export class MonitoringViewBaseTableController extends MonitoringViewBaseControl
|
|||
setLocalStorageData(storage, newState);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,18 +1,8 @@
|
|||
<monitoring-main name="listing">
|
||||
<div class="page-row">
|
||||
<h3
|
||||
class="kuiScreenReaderOnly"
|
||||
i18n-id="xpack.monitoring.cluster.listing.clustersTitle"
|
||||
i18n-default-message="Clusters"
|
||||
>
|
||||
</h3>
|
||||
<monitoring-cluster-listing
|
||||
page-index="clusters.pageIndex"
|
||||
filter-text="clusters.filterText"
|
||||
sort-key="clusters.sortKey"
|
||||
sort-order="clusters.sortOrder"
|
||||
on-new-state="clusters.onNewState"
|
||||
clusters="clusters.data"
|
||||
></monitoring-cluster-listing>
|
||||
</div>
|
||||
<monitoring-cluster-listing
|
||||
pagination-settings="clusters.pagination"
|
||||
sorting="clusters.sorting"
|
||||
on-table-change="clusters.onTableChange"
|
||||
clusters="clusters.data"
|
||||
></monitoring-cluster-listing>
|
||||
</monitoring-main>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import { MonitoringViewBaseTableController } from '../../';
|
||||
import { MonitoringViewBaseEuiTableController } from '../../';
|
||||
import template from './index.html';
|
||||
|
||||
const getPageData = $injector => {
|
||||
|
@ -35,7 +35,7 @@ uiRoutes.when('/home', {
|
|||
}
|
||||
},
|
||||
controllerAs: 'clusters',
|
||||
controller: class ClustersList extends MonitoringViewBaseTableController {
|
||||
controller: class ClustersList extends MonitoringViewBaseEuiTableController {
|
||||
|
||||
constructor($injector, $scope) {
|
||||
super({
|
||||
|
|
|
@ -50,7 +50,9 @@ uiRoutes.when('/elasticsearch/ccr/:index/shard/:shardId', {
|
|||
|
||||
this.renderReact = (props) => {
|
||||
super.renderReact(
|
||||
<I18nProvider><CcrShard {...props} /> </I18nProvider>
|
||||
<I18nProvider>
|
||||
<CcrShard {...props} />
|
||||
</I18nProvider>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,21 +4,5 @@
|
|||
resolver="{{ indexName }}"
|
||||
page="advanced"
|
||||
>
|
||||
<monitoring-index-summary summary="pageData.indexSummary"></monitoring-index-summary>
|
||||
|
||||
<div class="page-row">
|
||||
<div class="row" ng-if="pageData.metrics">
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_1"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_2"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_3"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_4"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_total"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_time"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_refresh"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_throttling"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_disk"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_segment_count"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_latency"/></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="monitoringElasticsearchAdvancedIndexApp"></div>
|
||||
</monitoring-main>
|
||||
|
|
|
@ -7,12 +7,17 @@
|
|||
/**
|
||||
* Controller for Advanced Index Detail
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { find } from 'lodash';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import template from './index.html';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { AdvancedIndex } from '../../../../components/elasticsearch/index/advanced';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import moment from 'moment';
|
||||
|
||||
function getPageData($injector) {
|
||||
const globalState = $injector.get('globalState');
|
||||
|
@ -75,5 +80,28 @@ uiRoutes.when('/elasticsearch/indices/:index/advanced', {
|
|||
$executor.start($scope);
|
||||
|
||||
$scope.$on('$destroy', $executor.destroy);
|
||||
|
||||
function onBrush({ xaxis }) {
|
||||
timefilter.setTime({
|
||||
from: moment(xaxis.from),
|
||||
to: moment(xaxis.to),
|
||||
mode: 'absolute',
|
||||
});
|
||||
}
|
||||
|
||||
this.renderReact = () => {
|
||||
render(
|
||||
<I18nProvider>
|
||||
<AdvancedIndex
|
||||
indexSummary={$scope.pageData.indexSummary}
|
||||
metrics={$scope.pageData.metrics}
|
||||
onBrush={onBrush}
|
||||
/>
|
||||
</I18nProvider>,
|
||||
document.getElementById('monitoringElasticsearchAdvancedIndexApp')
|
||||
);
|
||||
};
|
||||
|
||||
$scope.$watch('pageData', this.renderReact);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -5,24 +5,5 @@
|
|||
resolver="{{ indexName }}"
|
||||
page="overview"
|
||||
>
|
||||
<monitoring-index-summary summary="pageData.indexSummary"></monitoring-index-summary>
|
||||
|
||||
<div class="page-row">
|
||||
<div class="row" ng-if="pageData.metrics">
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_mem"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_size"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_search_request_rate"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_request_rate"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_segment_count"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.index_document_count"></monitoring-chart></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<monitoring-shard-allocation
|
||||
id="indexName"
|
||||
view="index"
|
||||
nodes="pageData.nodes"
|
||||
shards="pageData.shards"
|
||||
shard-stats="pageData.shardStats"
|
||||
></monitoring-shard-allocation>
|
||||
<div id="monitoringElasticsearchIndexApp"></div>
|
||||
</monitoring-main>
|
||||
|
|
|
@ -7,12 +7,19 @@
|
|||
/**
|
||||
* Controller for single index detail
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { find } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
||||
import template from './index.html';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { labels } from '../../../components/elasticsearch/shard_allocation/lib/labels';
|
||||
import { indicesByNodes } from '../../../components/elasticsearch/shard_allocation/transformers/indices_by_nodes';
|
||||
import { Index } from '../../../components/elasticsearch/index/index';
|
||||
|
||||
function getPageData($injector) {
|
||||
const $http = $injector.get('$http');
|
||||
|
@ -51,6 +58,7 @@ uiRoutes.when('/elasticsearch/indices/:index', {
|
|||
timefilter.enableAutoRefreshSelector();
|
||||
|
||||
const $route = $injector.get('$route');
|
||||
const kbnUrl = $injector.get('kbnUrl');
|
||||
const globalState = $injector.get('globalState');
|
||||
$scope.cluster = find($route.current.locals.clusters, { cluster_uuid: globalState.cluster_uuid });
|
||||
$scope.pageData = $route.current.locals.pageData;
|
||||
|
@ -75,5 +83,39 @@ uiRoutes.when('/elasticsearch/indices/:index', {
|
|||
$executor.start($scope);
|
||||
|
||||
$scope.$on('$destroy', $executor.destroy);
|
||||
|
||||
function onBrush({ xaxis }) {
|
||||
timefilter.setTime({
|
||||
from: moment(xaxis.from),
|
||||
to: moment(xaxis.to),
|
||||
mode: 'absolute',
|
||||
});
|
||||
}
|
||||
|
||||
const transformer = indicesByNodes();
|
||||
this.renderReact = () => {
|
||||
const shards = $scope.pageData.shards;
|
||||
$scope.totalCount = shards.length;
|
||||
$scope.showing = transformer(shards, $scope.pageData.nodes);
|
||||
if (shards.some((shard) => shard.state === 'UNASSIGNED')) {
|
||||
$scope.labels = labels.indexWithUnassigned;
|
||||
} else {
|
||||
$scope.labels = labels.index;
|
||||
}
|
||||
|
||||
render(
|
||||
<I18nProvider>
|
||||
<Index
|
||||
scope={$scope}
|
||||
kbnUrl={kbnUrl}
|
||||
onBrush={onBrush}
|
||||
{...$scope.pageData}
|
||||
/>
|
||||
</I18nProvider>,
|
||||
document.getElementById('monitoringElasticsearchIndexApp')
|
||||
);
|
||||
};
|
||||
|
||||
$scope.$watch('pageData', this.renderReact);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ import React from 'react';
|
|||
import { find } from 'lodash';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import { MonitoringViewBaseTableController } from '../../';
|
||||
import { MonitoringViewBaseEuiTableController } from '../../';
|
||||
import { ElasticsearchIndices } from '../../../components';
|
||||
import template from './index.html';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
|
@ -22,7 +22,7 @@ uiRoutes.when('/elasticsearch/indices', {
|
|||
}
|
||||
},
|
||||
controllerAs: 'elasticsearchIndices',
|
||||
controller: class ElasticsearchIndicesController extends MonitoringViewBaseTableController {
|
||||
controller: class ElasticsearchIndicesController extends MonitoringViewBaseEuiTableController {
|
||||
constructor($injector, $scope, i18n) {
|
||||
const $route = $injector.get('$route');
|
||||
const globalState = $injector.get('globalState');
|
||||
|
@ -69,6 +69,9 @@ uiRoutes.when('/elasticsearch/indices', {
|
|||
indices={indices}
|
||||
showSystemIndices={showSystemIndices}
|
||||
toggleShowSystemIndices={toggleShowSystemIndices}
|
||||
sorting={this.sorting}
|
||||
pagination={this.pagination}
|
||||
onTableChange={this.onTableChange}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
<monitoring-main product="elasticsearch" name="ml">
|
||||
<monitoring-cluster-status-elasticsearch status="mlJobs.data.clusterStatus"></monitoring-cluster-status-elasticsearch>
|
||||
<div class="page-row">
|
||||
<h3
|
||||
class="kuiScreenReaderOnly"
|
||||
i18n-id="xpack.monitoring.elasticsearch.mlJobsTitle"
|
||||
i18n-default-message="Machine Learning Jobs"
|
||||
></h3>
|
||||
<monitoring-ml-listing
|
||||
page-index="mlJobs.pageIndex"
|
||||
filter-text="mlJobs.filterText"
|
||||
sort-key="mlJobs.sortKey"
|
||||
sort-order="mlJobs.sortOrder"
|
||||
on-new-state="mlJobs.onNewState"
|
||||
jobs="mlJobs.data.rows"
|
||||
></monitoring-ml-listing>
|
||||
</div>
|
||||
<monitoring-ml-listing
|
||||
paginationSettings="mlJobs.pagination"
|
||||
sorting="mlJobs.sorting"
|
||||
on-table-change="mlJobs.onTableChange"
|
||||
jobs="mlJobs.data.rows"
|
||||
status="mlJobs.data.clusterStatus"
|
||||
></monitoring-ml-listing>
|
||||
</monitoring-main>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { find } from 'lodash';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import { MonitoringViewBaseTableController } from '../../';
|
||||
import { MonitoringViewBaseEuiTableController } from '../../';
|
||||
import { getPageData } from './get_page_data';
|
||||
import template from './index.html';
|
||||
|
||||
|
@ -21,7 +21,7 @@ uiRoutes.when('/elasticsearch/ml_jobs', {
|
|||
pageData: getPageData
|
||||
},
|
||||
controllerAs: 'mlJobs',
|
||||
controller: class MlJobsList extends MonitoringViewBaseTableController {
|
||||
controller: class MlJobsList extends MonitoringViewBaseEuiTableController {
|
||||
|
||||
constructor($injector, $scope, i18n) {
|
||||
super({
|
||||
|
|
|
@ -7,25 +7,5 @@
|
|||
tab-icon-class="{{ pageData.nodeSummary.nodeTypeClass }}"
|
||||
tab-icon-class="{{ pageData.nodeSummary.nodeTypeLabel }}"
|
||||
>
|
||||
<monitoring-node-summary node="pageData.nodeSummary"></monitoring-node-summary>
|
||||
|
||||
<div class="page-row">
|
||||
<div class="row" ng-if="pageData.metrics">
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_gc"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_gc_time"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_jvm_mem"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_cpu_utilization"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_index_1"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_index_2"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_index_3"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_index_4"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_index_time"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_request_total"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_index_threads"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_read_threads"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_cgroup_cpu"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_cgroup_stats"/></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_latency"/></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="monitoringElasticsearchAdvancedNodeApp"></div>
|
||||
</monitoring-main>
|
||||
|
|
|
@ -7,12 +7,17 @@
|
|||
/**
|
||||
* Controller for Advanced Node Detail
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { find } from 'lodash';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import template from './index.html';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { AdvancedNode } from '../../../../components/elasticsearch/node/advanced';
|
||||
import moment from 'moment';
|
||||
|
||||
function getPageData($injector) {
|
||||
const $http = $injector.get('$http');
|
||||
|
@ -76,5 +81,28 @@ uiRoutes.when('/elasticsearch/nodes/:node/advanced', {
|
|||
$executor.start($scope);
|
||||
|
||||
$scope.$on('$destroy', $executor.destroy);
|
||||
|
||||
function onBrush({ xaxis }) {
|
||||
timefilter.setTime({
|
||||
from: moment(xaxis.from),
|
||||
to: moment(xaxis.to),
|
||||
mode: 'absolute',
|
||||
});
|
||||
}
|
||||
|
||||
this.renderReact = () => {
|
||||
render(
|
||||
<I18nProvider>
|
||||
<AdvancedNode
|
||||
nodeSummary={$scope.pageData.nodeSummary}
|
||||
metrics={$scope.pageData.metrics}
|
||||
onBrush={onBrush}
|
||||
/>
|
||||
</I18nProvider>,
|
||||
document.getElementById('monitoringElasticsearchAdvancedNodeApp')
|
||||
);
|
||||
};
|
||||
|
||||
$scope.$watch('pageData', this.renderReact);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -7,25 +7,5 @@
|
|||
tab-icon-class="{{ pageData.nodeSummary.nodeTypeClass }}"
|
||||
tab-icon-class="{{ pageData.nodeSummary.nodeTypeLabel }}"
|
||||
>
|
||||
<monitoring-node-summary node="pageData.nodeSummary"></monitoring-node-summary>
|
||||
|
||||
<div class="page-row">
|
||||
<div class="row" ng-if="pageData.metrics">
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_jvm_mem"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_mem"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_cpu_metric"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_load_average"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_latency"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.node_segment_count"></monitoring-chart></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<monitoring-shard-allocation
|
||||
view="node"
|
||||
nodes="pageData.nodes"
|
||||
shards="pageData.shards"
|
||||
shard-stats="pageData.shardStats"
|
||||
show-system-indices="showSystemIndices"
|
||||
toggle-show-system-indices="toggleShowSystemIndices"
|
||||
></monitoring-shard-allocation>
|
||||
<div id="monitoringElasticsearchNodeApp"></div>
|
||||
</monitoring-main>
|
||||
|
|
|
@ -7,12 +7,19 @@
|
|||
/**
|
||||
* Controller for Node Detail
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { find, partial } from 'lodash';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import { getPageData } from './get_page_data';
|
||||
import template from './index.html';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { Node } from '../../../components/elasticsearch/node/node';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { labels } from '../../../components/elasticsearch/shard_allocation/lib/labels';
|
||||
import { nodesByIndices } from '../../../components/elasticsearch/shard_allocation/transformers/nodes_by_indices';
|
||||
import moment from 'moment';
|
||||
|
||||
uiRoutes.when('/elasticsearch/nodes/:node', {
|
||||
template,
|
||||
|
@ -28,6 +35,7 @@ uiRoutes.when('/elasticsearch/nodes/:node', {
|
|||
timefilter.enableAutoRefreshSelector();
|
||||
|
||||
const $route = $injector.get('$route');
|
||||
const kbnUrl = $injector.get('kbnUrl');
|
||||
const globalState = $injector.get('globalState');
|
||||
$scope.cluster = find($route.current.locals.clusters, { cluster_uuid: globalState.cluster_uuid });
|
||||
$scope.pageData = $route.current.locals.pageData;
|
||||
|
@ -65,5 +73,35 @@ uiRoutes.when('/elasticsearch/nodes/:node', {
|
|||
$executor.start($scope);
|
||||
|
||||
$scope.$on('$destroy', $executor.destroy);
|
||||
|
||||
function onBrush({ xaxis }) {
|
||||
timefilter.setTime({
|
||||
from: moment(xaxis.from),
|
||||
to: moment(xaxis.to),
|
||||
mode: 'absolute',
|
||||
});
|
||||
}
|
||||
|
||||
const transformer = nodesByIndices();
|
||||
this.renderReact = () => {
|
||||
const shards = $scope.pageData.shards;
|
||||
$scope.totalCount = shards.length;
|
||||
$scope.showing = transformer(shards, $scope.pageData.nodes);
|
||||
$scope.labels = labels.node;
|
||||
|
||||
render(
|
||||
<I18nProvider>
|
||||
<Node
|
||||
scope={$scope}
|
||||
kbnUrl={kbnUrl}
|
||||
onBrush={onBrush}
|
||||
{...$scope.pageData}
|
||||
/>
|
||||
</I18nProvider>,
|
||||
document.getElementById('monitoringElasticsearchNodeApp')
|
||||
);
|
||||
};
|
||||
|
||||
$scope.$watch('pageData', this.renderReact);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ import { find } from 'lodash';
|
|||
import uiRoutes from 'ui/routes';
|
||||
import template from './index.html';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import { MonitoringViewBaseTableController } from '../../';
|
||||
import { MonitoringViewBaseEuiTableController } from '../../';
|
||||
import { ElasticsearchNodes } from '../../../components';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
|
||||
|
@ -22,7 +22,7 @@ uiRoutes.when('/elasticsearch/nodes', {
|
|||
}
|
||||
},
|
||||
controllerAs: 'elasticsearchNodes',
|
||||
controller: class ElasticsearchNodesController extends MonitoringViewBaseTableController {
|
||||
controller: class ElasticsearchNodesController extends MonitoringViewBaseEuiTableController {
|
||||
constructor($injector, $scope, i18n) {
|
||||
const $route = $injector.get('$route');
|
||||
const globalState = $injector.get('globalState');
|
||||
|
@ -55,6 +55,9 @@ uiRoutes.when('/elasticsearch/nodes', {
|
|||
clusterStatus={clusterStatus}
|
||||
nodes={nodes}
|
||||
showCgroupMetricsElasticsearch={showCgroupMetricsElasticsearch}
|
||||
sorting={this.sorting}
|
||||
pagination={this.pagination}
|
||||
onTableChange={this.onTableChange}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
|
||||
export { MonitoringViewBaseController } from './base_controller';
|
||||
export { MonitoringViewBaseTableController } from './base_table_controller';
|
||||
export { MonitoringViewBaseEuiTableController } from './base_eui_table_controller';
|
||||
|
|
|
@ -4,15 +4,5 @@
|
|||
instance="{{ pageData.kibanaSummary.name }}"
|
||||
data-test-subj="kibanaInstancePage"
|
||||
>
|
||||
<monitoring-kibana-summary kibana="pageData.kibanaSummary"></monitoring-kibana-summary>
|
||||
<div class="page-row">
|
||||
<div class="row" ng-if="pageData.metrics">
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.kibana_requests"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.kibana_response_times"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.kibana_memory"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.kibana_average_concurrent_connections"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.kibana_os_load"></monitoring-chart></div>
|
||||
<div class="col-md-6"><monitoring-chart series="pageData.metrics.kibana_process_delay"></monitoring-chart></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="monitoringKibanaInstanceApp"></div>
|
||||
</monitoring-main>
|
||||
|
|
|
@ -7,12 +7,18 @@
|
|||
/*
|
||||
* Kibana Instance
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { get, find } from 'lodash';
|
||||
import uiRoutes from'ui/routes';
|
||||
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import template from './index.html';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { EuiPage, EuiPageBody, EuiPageContent, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { MonitoringTimeseriesContainer } from '../../../components/chart';
|
||||
import { DetailStatus } from 'plugins/monitoring/components/kibana/detail_status';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
|
||||
function getPageData($injector) {
|
||||
const $http = $injector.get('$http');
|
||||
|
@ -45,7 +51,7 @@ uiRoutes.when('/kibana/instances/:uuid', {
|
|||
},
|
||||
pageData: getPageData
|
||||
},
|
||||
controller($injector, $scope, i18n) {
|
||||
controller($injector, $scope) {
|
||||
timefilter.enableTimeRangeSelector();
|
||||
timefilter.enableAutoRefreshSelector();
|
||||
|
||||
|
@ -55,14 +61,7 @@ uiRoutes.when('/kibana/instances/:uuid', {
|
|||
$scope.pageData = $route.current.locals.pageData;
|
||||
|
||||
const title = $injector.get('title');
|
||||
const routeTitle = i18n('xpack.monitoring.kibana.instance.routeTitle', {
|
||||
defaultMessage: 'Kibana - {kibanaSummaryName}',
|
||||
values: {
|
||||
kibanaSummaryName: get($scope.pageData, 'kibanaSummary.name')
|
||||
}
|
||||
});
|
||||
|
||||
title($scope.cluster, routeTitle);
|
||||
title($scope.cluster, `Kibana - ${get($scope.pageData, 'kibanaSummary.name')}`);
|
||||
|
||||
const $executor = $injector.get('$executor');
|
||||
$executor.register({
|
||||
|
@ -73,5 +72,66 @@ uiRoutes.when('/kibana/instances/:uuid', {
|
|||
$executor.start($scope);
|
||||
|
||||
$scope.$on('$destroy', $executor.destroy);
|
||||
|
||||
$scope.$watch('pageData', renderReact);
|
||||
renderReact();
|
||||
|
||||
function renderReact() {
|
||||
const app = document.getElementById('monitoringKibanaInstanceApp');
|
||||
if (!app) {
|
||||
return;
|
||||
}
|
||||
|
||||
const overviewPage = (
|
||||
<I18nProvider>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<DetailStatus stats={$scope.pageData.kibanaSummary} />
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={true}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={$scope.pageData.metrics.kibana_requests}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={$scope.pageData.metrics.kibana_response_times}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={true}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={$scope.pageData.metrics.kibana_memory}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={$scope.pageData.metrics.kibana_average_concurrent_connections}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={true}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={$scope.pageData.metrics.kibana_os_load}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={$scope.pageData.metrics.kibana_process_delay}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
||||
render(overviewPage, app);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,19 +3,5 @@
|
|||
name="kibanas"
|
||||
data-test-subj="kibanaInstancesPage"
|
||||
>
|
||||
<monitoring-cluster-status-kibana status="kibanas.data.clusterStatus"></monitoring-cluster-status-kibana>
|
||||
<div class="page-row">
|
||||
<h3 class="kuiScreenReaderOnly"
|
||||
i18n-id="xpack.monitoring.kibana.instancesTitle"
|
||||
i18n-default-message="Kibana Instances"
|
||||
></h3>
|
||||
<monitoring-kibana-listing
|
||||
page-index="kibanas.pageIndex"
|
||||
filter-text="kibanas.filterText"
|
||||
sort-key="kibanas.sortKey"
|
||||
sort-order="kibanas.sortOrder"
|
||||
on-new-state="kibanas.onNewState"
|
||||
instances="kibanas.data.kibanas"
|
||||
></monitoring-kibana-listing>
|
||||
</div>
|
||||
<div id="monitoringKibanaInstancesApp"></div>
|
||||
</monitoring-main>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue