[Monitoring] Convert Elasticsearch-related server files that read from _source to typescript (#88212)
* A good chunk of server-side ES changes * CCR files * More areas where we just pass down the source to the client * Some more * Fix tests * Fix tests and types
This commit is contained in:
parent
f0f192c654
commit
c9002a25c5
|
@ -4,6 +4,31 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export interface ElasticsearchResponse {
|
||||
hits?: {
|
||||
hits: ElasticsearchResponseHit[];
|
||||
total: {
|
||||
value: number;
|
||||
};
|
||||
};
|
||||
aggregations?: any;
|
||||
}
|
||||
|
||||
export interface ElasticsearchResponseHit {
|
||||
_index: string;
|
||||
_source: ElasticsearchSource;
|
||||
inner_hits?: {
|
||||
[field: string]: {
|
||||
hits?: {
|
||||
hits: ElasticsearchResponseHit[];
|
||||
total: {
|
||||
value: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface ElasticsearchSourceKibanaStats {
|
||||
timestamp?: string;
|
||||
kibana?: {
|
||||
|
@ -34,9 +59,94 @@ export interface ElasticsearchSourceLogstashPipelineVertex {
|
|||
};
|
||||
}
|
||||
|
||||
export interface ElasticsearchSource {
|
||||
export interface ElasticsearchNodeStats {
|
||||
indices?: {
|
||||
docs?: {
|
||||
count?: number;
|
||||
};
|
||||
store?: {
|
||||
size_in_bytes?: number;
|
||||
size?: {
|
||||
bytes?: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
fs?: {
|
||||
total?: {
|
||||
available_in_bytes?: number;
|
||||
total_in_bytes?: number;
|
||||
};
|
||||
summary?: {
|
||||
available?: {
|
||||
bytes?: number;
|
||||
};
|
||||
total?: {
|
||||
bytes?: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
jvm?: {
|
||||
mem?: {
|
||||
heap_used_percent?: number;
|
||||
heap?: {
|
||||
used?: {
|
||||
pct?: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface ElasticsearchLegacySource {
|
||||
timestamp: string;
|
||||
cluster_uuid: string;
|
||||
cluster_stats?: {
|
||||
nodes?: {
|
||||
count?: {
|
||||
total?: number;
|
||||
};
|
||||
jvm?: {
|
||||
max_uptime_in_millis?: number;
|
||||
mem?: {
|
||||
heap_used_in_bytes?: number;
|
||||
heap_max_in_bytes?: number;
|
||||
};
|
||||
};
|
||||
versions?: string[];
|
||||
};
|
||||
indices?: {
|
||||
count?: number;
|
||||
docs?: {
|
||||
count?: number;
|
||||
};
|
||||
shards?: {
|
||||
total?: number;
|
||||
};
|
||||
store?: {
|
||||
size_in_bytes?: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
cluster_state?: {
|
||||
status?: string;
|
||||
nodes?: {
|
||||
[nodeUuid: string]: {};
|
||||
};
|
||||
master_node?: boolean;
|
||||
};
|
||||
source_node?: {
|
||||
id?: string;
|
||||
uuid?: string;
|
||||
attributes?: {};
|
||||
transport_address?: string;
|
||||
name?: string;
|
||||
type?: string;
|
||||
};
|
||||
kibana_stats?: ElasticsearchSourceKibanaStats;
|
||||
license?: {
|
||||
status?: string;
|
||||
type?: string;
|
||||
};
|
||||
logstash_state?: {
|
||||
pipeline?: {
|
||||
representation?: {
|
||||
|
@ -108,4 +218,98 @@ export interface ElasticsearchSource {
|
|||
};
|
||||
};
|
||||
};
|
||||
stack_stats?: {
|
||||
xpack?: {
|
||||
ccr?: {
|
||||
enabled?: boolean;
|
||||
available?: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
job_stats?: {
|
||||
job_id?: number;
|
||||
state?: string;
|
||||
data_counts?: {
|
||||
processed_record_count?: number;
|
||||
};
|
||||
model_size_stats?: {
|
||||
model_bytes?: number;
|
||||
};
|
||||
forecasts_stats?: {
|
||||
total?: number;
|
||||
};
|
||||
node?: {
|
||||
id?: number;
|
||||
name?: string;
|
||||
};
|
||||
};
|
||||
index_stats?: {
|
||||
index?: string;
|
||||
primaries?: {
|
||||
docs?: {
|
||||
count?: number;
|
||||
};
|
||||
store?: {
|
||||
size_in_bytes?: number;
|
||||
};
|
||||
indexing?: {
|
||||
index_total?: number;
|
||||
};
|
||||
};
|
||||
total?: {
|
||||
store?: {
|
||||
size_in_bytes?: number;
|
||||
};
|
||||
search?: {
|
||||
query_total?: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
node_stats?: ElasticsearchNodeStats;
|
||||
service?: {
|
||||
address?: string;
|
||||
};
|
||||
shard?: {
|
||||
index?: string;
|
||||
shard?: string;
|
||||
primary?: boolean;
|
||||
relocating_node?: string;
|
||||
node?: string;
|
||||
};
|
||||
ccr_stats?: {
|
||||
leader_index?: string;
|
||||
follower_index?: string;
|
||||
shard_id?: number;
|
||||
read_exceptions?: Array<{
|
||||
exception?: {
|
||||
type?: string;
|
||||
};
|
||||
}>;
|
||||
time_since_last_read_millis?: number;
|
||||
};
|
||||
index_recovery?: {
|
||||
shards?: ElasticsearchIndexRecoveryShard[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ElasticsearchIndexRecoveryShard {
|
||||
start_time_in_millis: number;
|
||||
stop_time_in_millis: number;
|
||||
}
|
||||
|
||||
export interface ElasticsearchMetricbeatNode {
|
||||
stats?: ElasticsearchNodeStats;
|
||||
}
|
||||
|
||||
export interface ElasticsearchMetricbeatSource {
|
||||
elasticsearch?: {
|
||||
node?: ElasticsearchLegacySource['source_node'] & ElasticsearchMetricbeatNode;
|
||||
};
|
||||
}
|
||||
|
||||
export type ElasticsearchSource = ElasticsearchLegacySource & ElasticsearchMetricbeatSource;
|
||||
|
||||
export interface ElasticsearchModifiedSource extends ElasticsearchSource {
|
||||
ccs?: string;
|
||||
isSupported?: boolean;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
import { createApmQuery } from './create_apm_query';
|
||||
// @ts-ignore
|
||||
import { ApmClusterMetric } from '../metrics';
|
||||
import { LegacyRequest, ElasticsearchResponse } from '../../types';
|
||||
import { LegacyRequest } from '../../types';
|
||||
import { ElasticsearchResponse } from '../../../common/types/es';
|
||||
|
||||
export async function getTimeOfLastEvent({
|
||||
req,
|
||||
|
@ -58,5 +59,5 @@ export async function getTimeOfLastEvent({
|
|||
};
|
||||
|
||||
const response = await callWithRequest(req, 'search', params);
|
||||
return response.hits?.hits.length ? response.hits?.hits[0]._source.timestamp : undefined;
|
||||
return response.hits?.hits.length ? response.hits?.hits[0]?._source.timestamp : undefined;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@ import { getDiffCalculation } from '../beats/_beats_stats';
|
|||
// @ts-ignore
|
||||
import { ApmMetric } from '../metrics';
|
||||
import { getTimeOfLastEvent } from './_get_time_of_last_event';
|
||||
import { LegacyRequest, ElasticsearchResponse } from '../../types';
|
||||
import { LegacyRequest } from '../../types';
|
||||
import { ElasticsearchResponse } from '../../../common/types/es';
|
||||
|
||||
export function handleResponse(response: ElasticsearchResponse, apmUuid: string) {
|
||||
if (!response.hits || response.hits.hits.length === 0) {
|
||||
|
|
|
@ -14,7 +14,8 @@ import { createApmQuery } from './create_apm_query';
|
|||
import { calculateRate } from '../calculate_rate';
|
||||
// @ts-ignore
|
||||
import { getDiffCalculation } from './_apm_stats';
|
||||
import { LegacyRequest, ElasticsearchResponse, ElasticsearchResponseHit } from '../../types';
|
||||
import { LegacyRequest } from '../../types';
|
||||
import { ElasticsearchResponse, ElasticsearchResponseHit } from '../../../common/types/es';
|
||||
|
||||
export function handleResponse(response: ElasticsearchResponse, start: number, end: number) {
|
||||
const initial = { ids: new Set(), beats: [] };
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
*/
|
||||
|
||||
import { upperFirst } from 'lodash';
|
||||
import { LegacyRequest, ElasticsearchResponse } from '../../types';
|
||||
import { LegacyRequest } from '../../types';
|
||||
import { ElasticsearchResponse } from '../../../common/types/es';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../error_missing_required';
|
||||
// @ts-ignore
|
||||
|
|
|
@ -14,7 +14,8 @@ import { createBeatsQuery } from './create_beats_query';
|
|||
import { calculateRate } from '../calculate_rate';
|
||||
// @ts-ignore
|
||||
import { getDiffCalculation } from './_beats_stats';
|
||||
import { ElasticsearchResponse, LegacyRequest } from '../../types';
|
||||
import { LegacyRequest } from '../../types';
|
||||
import { ElasticsearchResponse } from '../../../common/types/es';
|
||||
|
||||
interface Beat {
|
||||
uuid: string | undefined;
|
||||
|
|
|
@ -4,17 +4,18 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { set } from '@elastic/safer-lodash-set';
|
||||
import { get, find } from 'lodash';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../error_missing_required';
|
||||
import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../common/constants';
|
||||
import { ElasticsearchResponse, ElasticsearchModifiedSource } from '../../../common/types/es';
|
||||
import { LegacyRequest } from '../../types';
|
||||
|
||||
async function findSupportedBasicLicenseCluster(
|
||||
req,
|
||||
clusters,
|
||||
kbnIndexPattern,
|
||||
kibanaUuid,
|
||||
serverLog
|
||||
req: LegacyRequest,
|
||||
clusters: ElasticsearchModifiedSource[],
|
||||
kbnIndexPattern: string,
|
||||
kibanaUuid: string,
|
||||
serverLog: (message: string) => void
|
||||
) {
|
||||
checkParam(kbnIndexPattern, 'kbnIndexPattern in cluster/findSupportedBasicLicenseCluster');
|
||||
|
||||
|
@ -25,7 +26,7 @@ async function findSupportedBasicLicenseCluster(
|
|||
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring');
|
||||
const gte = req.payload.timeRange.min;
|
||||
const lte = req.payload.timeRange.max;
|
||||
const kibanaDataResult = await callWithRequest(req, 'search', {
|
||||
const kibanaDataResult: ElasticsearchResponse = (await callWithRequest(req, 'search', {
|
||||
index: kbnIndexPattern,
|
||||
size: 1,
|
||||
ignoreUnavailable: true,
|
||||
|
@ -42,11 +43,13 @@ async function findSupportedBasicLicenseCluster(
|
|||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const supportedClusterUuid = get(kibanaDataResult, 'hits.hits[0]._source.cluster_uuid');
|
||||
const supportedCluster = find(clusters, { cluster_uuid: supportedClusterUuid });
|
||||
// only this basic cluster is supported
|
||||
set(supportedCluster, 'isSupported', true);
|
||||
})) as ElasticsearchResponse;
|
||||
const supportedClusterUuid = kibanaDataResult.hits?.hits[0]?._source.cluster_uuid ?? undefined;
|
||||
for (const cluster of clusters) {
|
||||
if (cluster.cluster_uuid === supportedClusterUuid) {
|
||||
cluster.isSupported = true;
|
||||
}
|
||||
}
|
||||
|
||||
serverLog(
|
||||
`Found basic license admin cluster UUID for Monitoring UI support: ${supportedClusterUuid}.`
|
||||
|
@ -69,12 +72,12 @@ async function findSupportedBasicLicenseCluster(
|
|||
* Non-Basic license clusters and any cluster in a single-cluster environment
|
||||
* are also flagged as supported in this method.
|
||||
*/
|
||||
export function flagSupportedClusters(req, kbnIndexPattern) {
|
||||
export function flagSupportedClusters(req: LegacyRequest, kbnIndexPattern: string) {
|
||||
checkParam(kbnIndexPattern, 'kbnIndexPattern in cluster/flagSupportedClusters');
|
||||
|
||||
const config = req.server.config();
|
||||
const serverLog = (msg) => req.getLogger('supported-clusters').debug(msg);
|
||||
const flagAllSupported = (clusters) => {
|
||||
const serverLog = (message: string) => req.getLogger('supported-clusters').debug(message);
|
||||
const flagAllSupported = (clusters: ElasticsearchModifiedSource[]) => {
|
||||
clusters.forEach((cluster) => {
|
||||
if (cluster.license) {
|
||||
cluster.isSupported = true;
|
||||
|
@ -83,7 +86,7 @@ export function flagSupportedClusters(req, kbnIndexPattern) {
|
|||
return clusters;
|
||||
};
|
||||
|
||||
return async function (clusters) {
|
||||
return async function (clusters: ElasticsearchModifiedSource[]) {
|
||||
// Standalone clusters are automatically supported in the UI so ignore those for
|
||||
// our calculations here
|
||||
let linkedClusterCount = 0;
|
||||
|
@ -110,7 +113,7 @@ export function flagSupportedClusters(req, kbnIndexPattern) {
|
|||
|
||||
// if all linked are basic licenses
|
||||
if (linkedClusterCount === basicLicenseCount) {
|
||||
const kibanaUuid = config.get('server.uuid');
|
||||
const kibanaUuid = config.get('server.uuid') as string;
|
||||
return await findSupportedBasicLicenseCluster(
|
||||
req,
|
||||
clusters,
|
|
@ -4,12 +4,16 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../error_missing_required';
|
||||
// @ts-ignore
|
||||
import { createQuery } from '../create_query';
|
||||
// @ts-ignore
|
||||
import { ElasticsearchMetric } from '../metrics';
|
||||
import { ElasticsearchResponse } from '../../../common/types/es';
|
||||
import { LegacyRequest } from '../../types';
|
||||
|
||||
export function getClusterLicense(req, esIndexPattern, clusterUuid) {
|
||||
export function getClusterLicense(req: LegacyRequest, esIndexPattern: string, clusterUuid: string) {
|
||||
checkParam(esIndexPattern, 'esIndexPattern in getClusterLicense');
|
||||
|
||||
const params = {
|
||||
|
@ -28,7 +32,7 @@ export function getClusterLicense(req, esIndexPattern, clusterUuid) {
|
|||
};
|
||||
|
||||
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring');
|
||||
return callWithRequest(req, 'search', params).then((response) => {
|
||||
return get(response, 'hits.hits[0]._source.license', {});
|
||||
return callWithRequest(req, 'search', params).then((response: ElasticsearchResponse) => {
|
||||
return response.hits?.hits[0]?._source.license;
|
||||
});
|
||||
}
|
|
@ -3,20 +3,20 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { ElasticsearchSource } from '../../../common/types/es';
|
||||
|
||||
/*
|
||||
* @param cluster {Object} clusterStats from getClusterStatus
|
||||
* @param unassignedShards {Object} shardStats from getShardStats
|
||||
* @return top-level cluster summary data
|
||||
*/
|
||||
export function getClusterStatus(cluster, shardStats) {
|
||||
const clusterStats = get(cluster, 'cluster_stats', {});
|
||||
const clusterNodes = get(clusterStats, 'nodes', {});
|
||||
const clusterIndices = get(clusterStats, 'indices', {});
|
||||
export function getClusterStatus(cluster: ElasticsearchSource, shardStats: unknown) {
|
||||
const clusterStats = cluster.cluster_stats ?? {};
|
||||
const clusterNodes = clusterStats.nodes ?? {};
|
||||
const clusterIndices = clusterStats.indices ?? {};
|
||||
|
||||
const clusterTotalShards = get(clusterIndices, 'shards.total', 0);
|
||||
const clusterTotalShards = clusterIndices.shards?.total ?? 0;
|
||||
let unassignedShardsTotal = 0;
|
||||
const unassignedShards = get(shardStats, 'indicesTotals.unassigned');
|
||||
if (unassignedShards !== undefined) {
|
||||
|
@ -26,17 +26,17 @@ export function getClusterStatus(cluster, shardStats) {
|
|||
const totalShards = clusterTotalShards + unassignedShardsTotal;
|
||||
|
||||
return {
|
||||
status: get(cluster, 'cluster_state.status', 'unknown'),
|
||||
status: cluster.cluster_state?.status ?? 'unknown',
|
||||
// index-based stats
|
||||
indicesCount: get(clusterIndices, 'count', 0),
|
||||
documentCount: get(clusterIndices, 'docs.count', 0),
|
||||
dataSize: get(clusterIndices, 'store.size_in_bytes', 0),
|
||||
indicesCount: clusterIndices.count ?? 0,
|
||||
documentCount: clusterIndices.docs?.count ?? 0,
|
||||
dataSize: clusterIndices.store?.size_in_bytes ?? 0,
|
||||
// node-based stats
|
||||
nodesCount: get(clusterNodes, 'count.total', 0),
|
||||
upTime: get(clusterNodes, 'jvm.max_uptime_in_millis', 0),
|
||||
version: get(clusterNodes, 'versions', null),
|
||||
memUsed: get(clusterNodes, 'jvm.mem.heap_used_in_bytes', 0),
|
||||
memMax: get(clusterNodes, 'jvm.mem.heap_max_in_bytes', 0),
|
||||
nodesCount: clusterNodes.count?.total ?? 0,
|
||||
upTime: clusterNodes.jvm?.max_uptime_in_millis ?? 0,
|
||||
version: clusterNodes.versions ?? null,
|
||||
memUsed: clusterNodes.jvm?.mem?.heap_used_in_bytes ?? 0,
|
||||
memMax: clusterNodes.jvm?.mem?.heap_max_in_bytes ?? 0,
|
||||
unassignedShards: unassignedShardsTotal,
|
||||
totalShards,
|
||||
};
|
|
@ -4,8 +4,11 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get, find } from 'lodash';
|
||||
import { find } from 'lodash';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../error_missing_required';
|
||||
import { ElasticsearchResponse, ElasticsearchModifiedSource } from '../../../common/types/es';
|
||||
import { LegacyRequest } from '../../types';
|
||||
|
||||
/**
|
||||
* Augment the {@clusters} with their cluster state's from the {@code response}.
|
||||
|
@ -15,11 +18,14 @@ import { checkParam } from '../error_missing_required';
|
|||
* @param {Array} clusters Array of clusters to be augmented
|
||||
* @return {Array} Always {@code clusters}.
|
||||
*/
|
||||
export function handleResponse(response, clusters) {
|
||||
const hits = get(response, 'hits.hits', []);
|
||||
export function handleResponse(
|
||||
response: ElasticsearchResponse,
|
||||
clusters: ElasticsearchModifiedSource[]
|
||||
) {
|
||||
const hits = response.hits?.hits ?? [];
|
||||
|
||||
hits.forEach((hit) => {
|
||||
const currentCluster = get(hit, '_source', {});
|
||||
const currentCluster = hit._source;
|
||||
|
||||
if (currentCluster) {
|
||||
const cluster = find(clusters, { cluster_uuid: currentCluster.cluster_uuid });
|
||||
|
@ -39,7 +45,11 @@ export function handleResponse(response, clusters) {
|
|||
*
|
||||
* If there is no cluster state available for any cluster, then it will be returned without any cluster state information.
|
||||
*/
|
||||
export function getClustersState(req, esIndexPattern, clusters) {
|
||||
export function getClustersState(
|
||||
req: LegacyRequest,
|
||||
esIndexPattern: string,
|
||||
clusters: ElasticsearchModifiedSource[]
|
||||
) {
|
||||
checkParam(esIndexPattern, 'esIndexPattern in cluster/getClustersHealth');
|
||||
|
||||
const clusterUuids = clusters
|
|
@ -4,12 +4,17 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../error_missing_required';
|
||||
// @ts-ignore
|
||||
import { createQuery } from '../create_query';
|
||||
// @ts-ignore
|
||||
import { ElasticsearchMetric } from '../metrics';
|
||||
// @ts-ignore
|
||||
import { parseCrossClusterPrefix } from '../ccs_utils';
|
||||
import { getClustersState } from './get_clusters_state';
|
||||
import { ElasticsearchResponse, ElasticsearchModifiedSource } from '../../../common/types/es';
|
||||
import { LegacyRequest } from '../../types';
|
||||
|
||||
/**
|
||||
* This will fetch the cluster stats and cluster state as a single object per cluster.
|
||||
|
@ -19,10 +24,10 @@ import { getClustersState } from './get_clusters_state';
|
|||
* @param {String} clusterUuid (optional) If not undefined, getClusters will filter for a single cluster
|
||||
* @return {Promise} A promise containing an array of clusters.
|
||||
*/
|
||||
export function getClustersStats(req, esIndexPattern, clusterUuid) {
|
||||
export function getClustersStats(req: LegacyRequest, esIndexPattern: string, clusterUuid: string) {
|
||||
return (
|
||||
fetchClusterStats(req, esIndexPattern, clusterUuid)
|
||||
.then((response) => handleClusterStats(response, req.server))
|
||||
.then((response) => handleClusterStats(response))
|
||||
// augment older documents (e.g., from 2.x - 5.4) with their cluster_state
|
||||
.then((clusters) => getClustersState(req, esIndexPattern, clusters))
|
||||
);
|
||||
|
@ -36,7 +41,7 @@ export function getClustersStats(req, esIndexPattern, clusterUuid) {
|
|||
* @param {String} clusterUuid (optional) - if not undefined, getClusters filters for a single clusterUuid
|
||||
* @return {Promise} Object representing each cluster.
|
||||
*/
|
||||
function fetchClusterStats(req, esIndexPattern, clusterUuid) {
|
||||
function fetchClusterStats(req: LegacyRequest, esIndexPattern: string, clusterUuid: string) {
|
||||
checkParam(esIndexPattern, 'esIndexPattern in getClusters');
|
||||
|
||||
const config = req.server.config();
|
||||
|
@ -81,15 +86,15 @@ function fetchClusterStats(req, esIndexPattern, clusterUuid) {
|
|||
* @param {Object} response The response from Elasticsearch.
|
||||
* @return {Array} Objects representing each cluster.
|
||||
*/
|
||||
export function handleClusterStats(response) {
|
||||
const hits = get(response, 'hits.hits', []);
|
||||
export function handleClusterStats(response: ElasticsearchResponse) {
|
||||
const hits = response?.hits?.hits ?? [];
|
||||
|
||||
return hits
|
||||
.map((hit) => {
|
||||
const cluster = get(hit, '_source');
|
||||
const cluster = hit._source as ElasticsearchModifiedSource;
|
||||
|
||||
if (cluster) {
|
||||
const indexName = get(hit, '_index', '');
|
||||
const indexName = hit._index;
|
||||
const ccs = parseCrossClusterPrefix(indexName);
|
||||
|
||||
// use CCS whenever we come across it so that we can avoid talking to other monitoring clusters whenever possible
|
|
@ -4,19 +4,24 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import moment from 'moment';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../error_missing_required';
|
||||
// @ts-ignore
|
||||
import { ElasticsearchMetric } from '../metrics';
|
||||
// @ts-ignore
|
||||
import { createQuery } from '../create_query';
|
||||
import { ElasticsearchResponse } from '../../../common/types/es';
|
||||
import { LegacyRequest } from '../../types';
|
||||
|
||||
export function handleResponse(response) {
|
||||
const isEnabled = get(response, 'hits.hits[0]._source.stack_stats.xpack.ccr.enabled');
|
||||
const isAvailable = get(response, 'hits.hits[0]._source.stack_stats.xpack.ccr.available');
|
||||
export function handleResponse(response: ElasticsearchResponse) {
|
||||
const isEnabled = response.hits?.hits[0]?._source.stack_stats?.xpack?.ccr?.enabled ?? undefined;
|
||||
const isAvailable =
|
||||
response.hits?.hits[0]?._source.stack_stats?.xpack?.ccr?.available ?? undefined;
|
||||
return isEnabled && isAvailable;
|
||||
}
|
||||
|
||||
export async function checkCcrEnabled(req, esIndexPattern) {
|
||||
export async function checkCcrEnabled(req: LegacyRequest, esIndexPattern: string) {
|
||||
checkParam(esIndexPattern, 'esIndexPattern in getNodes');
|
||||
|
||||
const start = moment.utc(req.payload.timeRange.min).valueOf();
|
|
@ -5,9 +5,14 @@
|
|||
*/
|
||||
import moment from 'moment';
|
||||
import _ from 'lodash';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../error_missing_required';
|
||||
// @ts-ignore
|
||||
import { createQuery } from '../create_query';
|
||||
// @ts-ignore
|
||||
import { ElasticsearchMetric } from '../metrics';
|
||||
import { ElasticsearchResponse, ElasticsearchIndexRecoveryShard } from '../../../common/types/es';
|
||||
import { LegacyRequest } from '../../types';
|
||||
|
||||
/**
|
||||
* Filter out shard activity that we do not care about.
|
||||
|
@ -20,8 +25,8 @@ import { ElasticsearchMetric } from '../metrics';
|
|||
* @param {Number} startMs Start time in milliseconds of the polling window
|
||||
* @returns {boolean} true to keep
|
||||
*/
|
||||
export function filterOldShardActivity(startMs) {
|
||||
return (activity) => {
|
||||
export function filterOldShardActivity(startMs: number) {
|
||||
return (activity: ElasticsearchIndexRecoveryShard) => {
|
||||
// either it's still going and there is no stop time, or the stop time happened after we started looking for one
|
||||
return !_.isNumber(activity.stop_time_in_millis) || activity.stop_time_in_millis >= startMs;
|
||||
};
|
||||
|
@ -35,9 +40,9 @@ export function filterOldShardActivity(startMs) {
|
|||
* @param {Date} start The start time from the request payload (expected to be of type {@code Date})
|
||||
* @returns {Object[]} An array of shards representing active shard activity from {@code _source.index_recovery.shards}.
|
||||
*/
|
||||
export function handleLastRecoveries(resp, start) {
|
||||
if (resp.hits.hits.length === 1) {
|
||||
const data = _.get(resp.hits.hits[0], '_source.index_recovery.shards', []).filter(
|
||||
export function handleLastRecoveries(resp: ElasticsearchResponse, start: number) {
|
||||
if (resp.hits?.hits.length === 1) {
|
||||
const data = (resp.hits?.hits[0]?._source.index_recovery?.shards ?? []).filter(
|
||||
filterOldShardActivity(moment.utc(start).valueOf())
|
||||
);
|
||||
data.sort((a, b) => b.start_time_in_millis - a.start_time_in_millis);
|
||||
|
@ -47,7 +52,7 @@ export function handleLastRecoveries(resp, start) {
|
|||
return [];
|
||||
}
|
||||
|
||||
export function getLastRecovery(req, esIndexPattern) {
|
||||
export function getLastRecovery(req: LegacyRequest, esIndexPattern: string) {
|
||||
checkParam(esIndexPattern, 'esIndexPattern in elasticsearch/getLastRecovery');
|
||||
|
||||
const start = req.payload.timeRange.min;
|
|
@ -4,22 +4,26 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import Bluebird from 'bluebird';
|
||||
import { includes, get } from 'lodash';
|
||||
import { includes } from 'lodash';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../error_missing_required';
|
||||
// @ts-ignore
|
||||
import { createQuery } from '../create_query';
|
||||
// @ts-ignore
|
||||
import { ElasticsearchMetric } from '../metrics';
|
||||
import { ML_SUPPORTED_LICENSES } from '../../../common/constants';
|
||||
import { ElasticsearchResponse, ElasticsearchSource } from '../../../common/types/es';
|
||||
import { LegacyRequest } from '../../types';
|
||||
|
||||
/*
|
||||
* Get a listing of jobs along with some metric data to use for the listing
|
||||
*/
|
||||
export function handleResponse(response) {
|
||||
const hits = get(response, 'hits.hits', []);
|
||||
return hits.map((hit) => get(hit, '_source.job_stats'));
|
||||
export function handleResponse(response: ElasticsearchResponse) {
|
||||
const hits = response.hits?.hits;
|
||||
return hits?.map((hit) => hit._source.job_stats) ?? [];
|
||||
}
|
||||
|
||||
export function getMlJobs(req, esIndexPattern) {
|
||||
export function getMlJobs(req: LegacyRequest, esIndexPattern: string) {
|
||||
checkParam(esIndexPattern, 'esIndexPattern in getMlJobs');
|
||||
|
||||
const config = req.server.config();
|
||||
|
@ -56,8 +60,12 @@ export function getMlJobs(req, esIndexPattern) {
|
|||
* cardinality isn't guaranteed to be accurate is the issue
|
||||
* but it will be as long as the precision threshold is >= the actual value
|
||||
*/
|
||||
export function getMlJobsForCluster(req, esIndexPattern, cluster) {
|
||||
const license = get(cluster, 'license', {});
|
||||
export function getMlJobsForCluster(
|
||||
req: LegacyRequest,
|
||||
esIndexPattern: string,
|
||||
cluster: ElasticsearchSource
|
||||
) {
|
||||
const license = cluster.license ?? {};
|
||||
|
||||
if (license.status === 'active' && includes(ML_SUPPORTED_LICENSES, license.type)) {
|
||||
// ML is supported
|
||||
|
@ -80,11 +88,11 @@ export function getMlJobsForCluster(req, esIndexPattern, cluster) {
|
|||
|
||||
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring');
|
||||
|
||||
return callWithRequest(req, 'search', params).then((response) => {
|
||||
return get(response, 'aggregations.jobs_count.value', 0);
|
||||
return callWithRequest(req, 'search', params).then((response: ElasticsearchResponse) => {
|
||||
return response.aggregations.jobs_count.value ?? 0;
|
||||
});
|
||||
}
|
||||
|
||||
// ML is not supported
|
||||
return Bluebird.resolve(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
|
@ -5,22 +5,27 @@
|
|||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { checkParam } from '../../error_missing_required';
|
||||
import { createQuery } from '../../create_query';
|
||||
import { ElasticsearchMetric } from '../../metrics';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../../error_missing_required';
|
||||
// @ts-ignore
|
||||
import { createQuery } from '../../create_query';
|
||||
// @ts-ignore
|
||||
import { ElasticsearchMetric } from '../../metrics';
|
||||
import { ElasticsearchResponse } from '../../../../common/types/es';
|
||||
import { LegacyRequest } from '../../../types';
|
||||
|
||||
export function handleResponse(shardStats, indexUuid) {
|
||||
return (response) => {
|
||||
const indexStats = get(response, 'hits.hits[0]._source.index_stats');
|
||||
const primaries = get(indexStats, 'primaries');
|
||||
const total = get(indexStats, 'total');
|
||||
export function handleResponse(shardStats: any, indexUuid: string) {
|
||||
return (response: ElasticsearchResponse) => {
|
||||
const indexStats = response.hits?.hits[0]?._source.index_stats;
|
||||
const primaries = indexStats?.primaries;
|
||||
const total = indexStats?.total;
|
||||
|
||||
const stats = {
|
||||
documents: get(primaries, 'docs.count'),
|
||||
documents: primaries?.docs?.count,
|
||||
dataSize: {
|
||||
primaries: get(primaries, 'store.size_in_bytes'),
|
||||
total: get(total, 'store.size_in_bytes'),
|
||||
primaries: primaries?.store?.size_in_bytes,
|
||||
total: total?.store?.size_in_bytes,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -55,10 +60,15 @@ export function handleResponse(shardStats, indexUuid) {
|
|||
}
|
||||
|
||||
export function getIndexSummary(
|
||||
req,
|
||||
esIndexPattern,
|
||||
shardStats,
|
||||
{ clusterUuid, indexUuid, start, end }
|
||||
req: LegacyRequest,
|
||||
esIndexPattern: string,
|
||||
shardStats: any,
|
||||
{
|
||||
clusterUuid,
|
||||
indexUuid,
|
||||
start,
|
||||
end,
|
||||
}: { clusterUuid: string; indexUuid: string; start: number; end: number }
|
||||
) {
|
||||
checkParam(esIndexPattern, 'esIndexPattern in elasticsearch/getIndexSummary');
|
||||
|
|
@ -5,43 +5,54 @@
|
|||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { checkParam } from '../../error_missing_required';
|
||||
import { ElasticsearchMetric } from '../../metrics';
|
||||
import { createQuery } from '../../create_query';
|
||||
import { calculateRate } from '../../calculate_rate';
|
||||
import { getUnassignedShards } from '../shards';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../../error_missing_required';
|
||||
// @ts-ignore
|
||||
import { ElasticsearchMetric } from '../../metrics';
|
||||
// @ts-ignore
|
||||
import { createQuery } from '../../create_query';
|
||||
// @ts-ignore
|
||||
import { calculateRate } from '../../calculate_rate';
|
||||
// @ts-ignore
|
||||
import { getUnassignedShards } from '../shards';
|
||||
import { ElasticsearchResponse } from '../../../../common/types/es';
|
||||
import { LegacyRequest } from '../../../types';
|
||||
|
||||
export function handleResponse(resp, min, max, shardStats) {
|
||||
export function handleResponse(
|
||||
resp: ElasticsearchResponse,
|
||||
min: number,
|
||||
max: number,
|
||||
shardStats: any
|
||||
) {
|
||||
// map the hits
|
||||
const hits = get(resp, 'hits.hits', []);
|
||||
const hits = resp?.hits?.hits ?? [];
|
||||
return hits.map((hit) => {
|
||||
const stats = get(hit, '_source.index_stats');
|
||||
const earliestStats = get(hit, 'inner_hits.earliest.hits.hits[0]._source.index_stats');
|
||||
const stats = hit._source.index_stats;
|
||||
const earliestStats = hit.inner_hits?.earliest?.hits?.hits[0]?._source.index_stats;
|
||||
|
||||
const rateOptions = {
|
||||
hitTimestamp: get(hit, '_source.timestamp'),
|
||||
earliestHitTimestamp: get(hit, 'inner_hits.earliest.hits.hits[0]._source.timestamp'),
|
||||
hitTimestamp: hit._source.timestamp,
|
||||
earliestHitTimestamp: hit.inner_hits?.earliest?.hits?.hits[0]?._source.timestamp,
|
||||
timeWindowMin: min,
|
||||
timeWindowMax: max,
|
||||
};
|
||||
|
||||
const earliestIndexingHit = get(earliestStats, 'primaries.indexing');
|
||||
const earliestIndexingHit = earliestStats?.primaries?.indexing;
|
||||
const { rate: indexRate } = calculateRate({
|
||||
latestTotal: get(stats, 'primaries.indexing.index_total'),
|
||||
earliestTotal: get(earliestIndexingHit, 'index_total'),
|
||||
latestTotal: stats?.primaries?.indexing?.index_total,
|
||||
earliestTotal: earliestIndexingHit?.index_total,
|
||||
...rateOptions,
|
||||
});
|
||||
|
||||
const earliestSearchHit = get(earliestStats, 'total.search');
|
||||
const earliestSearchHit = earliestStats?.total?.search;
|
||||
const { rate: searchRate } = calculateRate({
|
||||
latestTotal: get(stats, 'total.search.query_total'),
|
||||
earliestTotal: get(earliestSearchHit, 'query_total'),
|
||||
latestTotal: stats?.total?.search?.query_total,
|
||||
earliestTotal: earliestSearchHit?.query_total,
|
||||
...rateOptions,
|
||||
});
|
||||
|
||||
const shardStatsForIndex = get(shardStats, ['indices', stats.index]);
|
||||
|
||||
const shardStatsForIndex = get(shardStats, ['indices', stats?.index ?? '']);
|
||||
let status;
|
||||
let statusSort;
|
||||
let unassignedShards;
|
||||
|
@ -65,10 +76,10 @@ export function handleResponse(resp, min, max, shardStats) {
|
|||
}
|
||||
|
||||
return {
|
||||
name: stats.index,
|
||||
name: stats?.index,
|
||||
status,
|
||||
doc_count: get(stats, 'primaries.docs.count'),
|
||||
data_size: get(stats, 'total.store.size_in_bytes'),
|
||||
doc_count: stats?.primaries?.docs?.count,
|
||||
data_size: stats?.total?.store?.size_in_bytes,
|
||||
index_rate: indexRate,
|
||||
search_rate: searchRate,
|
||||
unassigned_shards: unassignedShards,
|
||||
|
@ -78,9 +89,14 @@ export function handleResponse(resp, min, max, shardStats) {
|
|||
}
|
||||
|
||||
export function buildGetIndicesQuery(
|
||||
esIndexPattern,
|
||||
clusterUuid,
|
||||
{ start, end, size, showSystemIndices = false }
|
||||
esIndexPattern: string,
|
||||
clusterUuid: string,
|
||||
{
|
||||
start,
|
||||
end,
|
||||
size,
|
||||
showSystemIndices = false,
|
||||
}: { start: number; end: number; size: number; showSystemIndices: boolean }
|
||||
) {
|
||||
const filters = [];
|
||||
if (!showSystemIndices) {
|
||||
|
@ -134,7 +150,12 @@ export function buildGetIndicesQuery(
|
|||
};
|
||||
}
|
||||
|
||||
export function getIndices(req, esIndexPattern, showSystemIndices = false, shardStats) {
|
||||
export function getIndices(
|
||||
req: LegacyRequest,
|
||||
esIndexPattern: string,
|
||||
showSystemIndices: boolean = false,
|
||||
shardStats: any
|
||||
) {
|
||||
checkParam(esIndexPattern, 'esIndexPattern in elasticsearch/getIndices');
|
||||
|
||||
const { min: start, max: end } = req.payload.timeRange;
|
||||
|
@ -145,7 +166,7 @@ export function getIndices(req, esIndexPattern, showSystemIndices = false, shard
|
|||
start,
|
||||
end,
|
||||
showSystemIndices,
|
||||
size: config.get('monitoring.ui.max_bucket_size'),
|
||||
size: parseInt(config.get('monitoring.ui.max_bucket_size') || '', 10),
|
||||
});
|
||||
|
||||
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring');
|
|
@ -5,35 +5,49 @@
|
|||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { checkParam } from '../../error_missing_required';
|
||||
import { createQuery } from '../../create_query';
|
||||
import { ElasticsearchMetric } from '../../metrics';
|
||||
import { getDefaultNodeFromId } from './get_default_node_from_id';
|
||||
import { calculateNodeType } from './calculate_node_type';
|
||||
import { getNodeTypeClassLabel } from './get_node_type_class_label';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../../error_missing_required';
|
||||
// @ts-ignore
|
||||
import { createQuery } from '../../create_query';
|
||||
// @ts-ignore
|
||||
import { ElasticsearchMetric } from '../../metrics';
|
||||
// @ts-ignore
|
||||
import { getDefaultNodeFromId } from './get_default_node_from_id';
|
||||
// @ts-ignore
|
||||
import { calculateNodeType } from './calculate_node_type';
|
||||
// @ts-ignore
|
||||
import { getNodeTypeClassLabel } from './get_node_type_class_label';
|
||||
import {
|
||||
ElasticsearchSource,
|
||||
ElasticsearchResponse,
|
||||
ElasticsearchLegacySource,
|
||||
ElasticsearchMetricbeatNode,
|
||||
} from '../../../../common/types/es';
|
||||
import { LegacyRequest } from '../../../types';
|
||||
|
||||
export function handleResponse(clusterState, shardStats, nodeUuid) {
|
||||
return (response) => {
|
||||
export function handleResponse(
|
||||
clusterState: ElasticsearchSource['cluster_state'],
|
||||
shardStats: any,
|
||||
nodeUuid: string
|
||||
) {
|
||||
return (response: ElasticsearchResponse) => {
|
||||
let nodeSummary = {};
|
||||
const nodeStatsHits = get(response, 'hits.hits', []);
|
||||
const nodes = nodeStatsHits.map((hit) =>
|
||||
get(hit, '_source.elasticsearch.node', hit._source.source_node)
|
||||
); // using [0] value because query results are sorted desc per timestamp
|
||||
const nodeStatsHits = response.hits?.hits ?? [];
|
||||
const nodes: Array<
|
||||
ElasticsearchLegacySource['source_node'] | ElasticsearchMetricbeatNode
|
||||
> = nodeStatsHits.map((hit) => hit._source.elasticsearch?.node || hit._source.source_node); // using [0] value because query results are sorted desc per timestamp
|
||||
const node = nodes[0] || getDefaultNodeFromId(nodeUuid);
|
||||
const sourceStats =
|
||||
get(response, 'hits.hits[0]._source.elasticsearch.node.stats') ||
|
||||
get(response, 'hits.hits[0]._source.node_stats');
|
||||
const clusterNode = get(clusterState, ['nodes', nodeUuid]);
|
||||
response.hits?.hits[0]?._source.elasticsearch?.node?.stats ||
|
||||
response.hits?.hits[0]?._source.node_stats;
|
||||
const clusterNode =
|
||||
clusterState && clusterState.nodes ? clusterState.nodes[nodeUuid] : undefined;
|
||||
const stats = {
|
||||
resolver: nodeUuid,
|
||||
node_ids: nodes.map((node) => node.id || node.uuid),
|
||||
node_ids: nodes.map((_node) => node.id || node.uuid),
|
||||
attributes: node.attributes,
|
||||
transport_address: get(
|
||||
response,
|
||||
'hits.hits[0]._source.service.address',
|
||||
node.transport_address
|
||||
),
|
||||
transport_address: response.hits?.hits[0]?._source.service?.address || node.transport_address,
|
||||
name: node.name,
|
||||
type: node.type,
|
||||
};
|
||||
|
@ -48,22 +62,19 @@ export function handleResponse(clusterState, shardStats, nodeUuid) {
|
|||
|
||||
nodeSummary = {
|
||||
type: nodeType,
|
||||
nodeTypeLabel: nodeTypeLabel,
|
||||
nodeTypeClass: nodeTypeClass,
|
||||
nodeTypeLabel,
|
||||
nodeTypeClass,
|
||||
totalShards: _shardStats.shardCount,
|
||||
indexCount: _shardStats.indexCount,
|
||||
documents: get(sourceStats, 'indices.docs.count'),
|
||||
documents: sourceStats?.indices?.docs?.count,
|
||||
dataSize:
|
||||
get(sourceStats, 'indices.store.size_in_bytes') ||
|
||||
get(sourceStats, 'indices.store.size.bytes'),
|
||||
sourceStats?.indices?.store?.size_in_bytes || sourceStats?.indices?.store?.size?.bytes,
|
||||
freeSpace:
|
||||
get(sourceStats, 'fs.total.available_in_bytes') ||
|
||||
get(sourceStats, 'fs.summary.available.bytes'),
|
||||
sourceStats?.fs?.total?.available_in_bytes || sourceStats?.fs?.summary?.available?.bytes,
|
||||
totalSpace:
|
||||
get(sourceStats, 'fs.total.total_in_bytes') || get(sourceStats, 'fs.summary.total.bytes'),
|
||||
sourceStats?.fs?.total?.total_in_bytes || sourceStats?.fs?.summary?.total?.bytes,
|
||||
usedHeap:
|
||||
get(sourceStats, 'jvm.mem.heap_used_percent') ||
|
||||
get(sourceStats, 'jvm.mem.heap.used.pct'),
|
||||
sourceStats?.jvm?.mem?.heap_used_percent || sourceStats?.jvm?.mem?.heap?.used?.pct,
|
||||
status: i18n.translate('xpack.monitoring.es.nodes.onlineStatusLabel', {
|
||||
defaultMessage: 'Online',
|
||||
}),
|
||||
|
@ -89,11 +100,16 @@ export function handleResponse(clusterState, shardStats, nodeUuid) {
|
|||
}
|
||||
|
||||
export function getNodeSummary(
|
||||
req,
|
||||
esIndexPattern,
|
||||
clusterState,
|
||||
shardStats,
|
||||
{ clusterUuid, nodeUuid, start, end }
|
||||
req: LegacyRequest,
|
||||
esIndexPattern: string,
|
||||
clusterState: ElasticsearchSource['cluster_state'],
|
||||
shardStats: any,
|
||||
{
|
||||
clusterUuid,
|
||||
nodeUuid,
|
||||
start,
|
||||
end,
|
||||
}: { clusterUuid: string; nodeUuid: string; start: number; end: number }
|
||||
) {
|
||||
checkParam(esIndexPattern, 'esIndexPattern in elasticsearch/getNodeSummary');
|
||||
|
|
@ -5,13 +5,21 @@
|
|||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../../../error_missing_required';
|
||||
// @ts-ignore
|
||||
import { createQuery } from '../../../create_query';
|
||||
// @ts-ignore
|
||||
import { calculateAuto } from '../../../calculate_auto';
|
||||
// @ts-ignore
|
||||
import { ElasticsearchMetric } from '../../../metrics';
|
||||
// @ts-ignore
|
||||
import { getMetricAggs } from './get_metric_aggs';
|
||||
import { handleResponse } from './handle_response';
|
||||
// @ts-ignore
|
||||
import { LISTING_METRICS_NAMES, LISTING_METRICS_PATHS } from './nodes_listing_metrics';
|
||||
import { LegacyRequest } from '../../../../types';
|
||||
import { ElasticsearchModifiedSource } from '../../../../../common/types/es';
|
||||
|
||||
/* Run an aggregation on node_stats to get stat data for the selected time
|
||||
* range for all the active nodes. Every option is a key to a configuration
|
||||
|
@ -30,7 +38,13 @@ import { LISTING_METRICS_NAMES, LISTING_METRICS_PATHS } from './nodes_listing_me
|
|||
* @param {Object} nodesShardCount: per-node information about shards
|
||||
* @return {Array} node info combined with metrics for each node from handle_response
|
||||
*/
|
||||
export async function getNodes(req, esIndexPattern, pageOfNodes, clusterStats, nodesShardCount) {
|
||||
export async function getNodes(
|
||||
req: LegacyRequest,
|
||||
esIndexPattern: string,
|
||||
pageOfNodes: Array<{ uuid: string }>,
|
||||
clusterStats: ElasticsearchModifiedSource,
|
||||
nodesShardCount: { nodes: { [nodeId: string]: { shardCount: number } } }
|
||||
) {
|
||||
checkParam(esIndexPattern, 'esIndexPattern in getNodes');
|
||||
|
||||
const start = moment.utc(req.payload.timeRange.min).valueOf();
|
||||
|
@ -45,7 +59,7 @@ export async function getNodes(req, esIndexPattern, pageOfNodes, clusterStats, n
|
|||
const min = start;
|
||||
|
||||
const bucketSize = Math.max(
|
||||
config.get('monitoring.ui.min_interval_seconds'),
|
||||
parseInt(config.get('monitoring.ui.min_interval_seconds') as string, 10),
|
||||
calculateAuto(100, duration).asSeconds()
|
||||
);
|
||||
|
|
@ -6,8 +6,11 @@
|
|||
|
||||
import { get } from 'lodash';
|
||||
import { mapNodesInfo } from './map_nodes_info';
|
||||
// @ts-ignore
|
||||
import { mapNodesMetrics } from './map_nodes_metrics';
|
||||
// @ts-ignore
|
||||
import { uncovertMetricNames } from '../../convert_metric_names';
|
||||
import { ElasticsearchResponse, ElasticsearchModifiedSource } from '../../../../../common/types/es';
|
||||
|
||||
/*
|
||||
* Process the response from the get_nodes query
|
||||
|
@ -18,18 +21,18 @@ import { uncovertMetricNames } from '../../convert_metric_names';
|
|||
* @return {Array} node info combined with metrics for each node
|
||||
*/
|
||||
export function handleResponse(
|
||||
response,
|
||||
clusterStats,
|
||||
nodesShardCount,
|
||||
pageOfNodes,
|
||||
response: ElasticsearchResponse,
|
||||
clusterStats: ElasticsearchModifiedSource | undefined,
|
||||
nodesShardCount: { nodes: { [nodeId: string]: { shardCount: number } } } | undefined,
|
||||
pageOfNodes: Array<{ uuid: string }>,
|
||||
timeOptions = {}
|
||||
) {
|
||||
if (!get(response, 'hits.hits')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const nodeHits = get(response, 'hits.hits', []);
|
||||
const nodesInfo = mapNodesInfo(nodeHits, clusterStats, nodesShardCount);
|
||||
const nodeHits = response.hits?.hits ?? [];
|
||||
const nodesInfo: { [key: string]: any } = mapNodesInfo(nodeHits, clusterStats, nodesShardCount);
|
||||
|
||||
/*
|
||||
* Every node bucket is an object with a field for nodeId and fields for
|
||||
|
@ -37,19 +40,29 @@ export function handleResponse(
|
|||
* with a sub-object for all the metrics buckets
|
||||
*/
|
||||
const nodeBuckets = get(response, 'aggregations.nodes.buckets', []);
|
||||
const metricsForNodes = nodeBuckets.reduce((accum, { key: nodeId, by_date: byDate }) => {
|
||||
return {
|
||||
...accum,
|
||||
[nodeId]: uncovertMetricNames(byDate),
|
||||
};
|
||||
}, {});
|
||||
const nodesMetrics = mapNodesMetrics(metricsForNodes, nodesInfo, timeOptions); // summarize the metrics of online nodes
|
||||
const metricsForNodes = nodeBuckets.reduce(
|
||||
(
|
||||
accum: { [nodeId: string]: any },
|
||||
{ key: nodeId, by_date: byDate }: { key: string; by_date: any }
|
||||
) => {
|
||||
return {
|
||||
...accum,
|
||||
[nodeId]: uncovertMetricNames(byDate),
|
||||
};
|
||||
},
|
||||
{}
|
||||
);
|
||||
const nodesMetrics: { [key: string]: any } = mapNodesMetrics(
|
||||
metricsForNodes,
|
||||
nodesInfo,
|
||||
timeOptions
|
||||
); // summarize the metrics of online nodes
|
||||
|
||||
// nodesInfo is the source of truth for the nodeIds, where nodesMetrics will lack metrics for offline nodes
|
||||
const nodes = pageOfNodes.map((node) => ({
|
||||
...node,
|
||||
...nodesInfo[node.uuid],
|
||||
...nodesMetrics[node.uuid],
|
||||
...(nodesInfo && nodesInfo[node.uuid] ? nodesInfo[node.uuid] : {}),
|
||||
...(nodesMetrics && nodesMetrics[node.uuid] ? nodesMetrics[node.uuid] : {}),
|
||||
resolver: node.uuid,
|
||||
}));
|
||||
|
|
@ -1,46 +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 { get, isUndefined } from 'lodash';
|
||||
import { calculateNodeType, getNodeTypeClassLabel } from '../';
|
||||
|
||||
/**
|
||||
* @param {Array} nodeHits: info about each node from the hits in the get_nodes query
|
||||
* @param {Object} clusterStats: cluster stats from cluster state document
|
||||
* @param {Object} nodesShardCount: per-node information about shards
|
||||
* @return {Object} summarized info about each node keyed by nodeId
|
||||
*/
|
||||
export function mapNodesInfo(nodeHits, clusterStats, nodesShardCount) {
|
||||
const clusterState = get(clusterStats, 'cluster_state', { nodes: {} });
|
||||
|
||||
return nodeHits.reduce((prev, node) => {
|
||||
const sourceNode = get(node, '_source.source_node') || get(node, '_source.elasticsearch.node');
|
||||
|
||||
const calculatedNodeType = calculateNodeType(sourceNode, get(clusterState, 'master_node'));
|
||||
const { nodeType, nodeTypeLabel, nodeTypeClass } = getNodeTypeClassLabel(
|
||||
sourceNode,
|
||||
calculatedNodeType
|
||||
);
|
||||
const isOnline = !isUndefined(get(clusterState, ['nodes', sourceNode.uuid || sourceNode.id]));
|
||||
|
||||
return {
|
||||
...prev,
|
||||
[sourceNode.uuid || sourceNode.id]: {
|
||||
name: sourceNode.name,
|
||||
transport_address: sourceNode.transport_address,
|
||||
type: nodeType,
|
||||
isOnline,
|
||||
nodeTypeLabel: nodeTypeLabel,
|
||||
nodeTypeClass: nodeTypeClass,
|
||||
shardCount: get(
|
||||
nodesShardCount,
|
||||
`nodes[${sourceNode.uuid || sourceNode.id}].shardCount`,
|
||||
0
|
||||
),
|
||||
},
|
||||
};
|
||||
}, {});
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 { isUndefined } from 'lodash';
|
||||
// @ts-ignore
|
||||
import { calculateNodeType } from '../calculate_node_type';
|
||||
// @ts-ignore
|
||||
import { getNodeTypeClassLabel } from '../get_node_type_class_label';
|
||||
import {
|
||||
ElasticsearchResponseHit,
|
||||
ElasticsearchModifiedSource,
|
||||
} from '../../../../../common/types/es';
|
||||
|
||||
/**
|
||||
* @param {Array} nodeHits: info about each node from the hits in the get_nodes query
|
||||
* @param {Object} clusterStats: cluster stats from cluster state document
|
||||
* @param {Object} nodesShardCount: per-node information about shards
|
||||
* @return {Object} summarized info about each node keyed by nodeId
|
||||
*/
|
||||
export function mapNodesInfo(
|
||||
nodeHits: ElasticsearchResponseHit[],
|
||||
clusterStats?: ElasticsearchModifiedSource,
|
||||
nodesShardCount?: { nodes: { [nodeId: string]: { shardCount: number } } }
|
||||
) {
|
||||
const clusterState = clusterStats?.cluster_state ?? { nodes: {} };
|
||||
|
||||
return nodeHits.reduce((prev, node) => {
|
||||
const sourceNode = node._source.source_node || node._source.elasticsearch?.node;
|
||||
|
||||
const calculatedNodeType = calculateNodeType(sourceNode, clusterState.master_node);
|
||||
const { nodeType, nodeTypeLabel, nodeTypeClass } = getNodeTypeClassLabel(
|
||||
sourceNode,
|
||||
calculatedNodeType
|
||||
);
|
||||
const uuid = sourceNode?.uuid ?? sourceNode?.id ?? undefined;
|
||||
if (!uuid) {
|
||||
return prev;
|
||||
}
|
||||
const isOnline = !isUndefined(clusterState.nodes ? clusterState.nodes[uuid] : undefined);
|
||||
|
||||
return {
|
||||
...prev,
|
||||
[uuid]: {
|
||||
name: sourceNode?.name,
|
||||
transport_address: sourceNode?.transport_address,
|
||||
type: nodeType,
|
||||
isOnline,
|
||||
nodeTypeLabel,
|
||||
nodeTypeClass,
|
||||
shardCount: nodesShardCount?.nodes[uuid]?.shardCount ?? 0,
|
||||
},
|
||||
};
|
||||
}, {});
|
||||
}
|
|
@ -4,22 +4,26 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
// @ts-ignore
|
||||
import { checkParam } from '../../error_missing_required';
|
||||
// @ts-ignore
|
||||
import { createQuery } from '../../create_query';
|
||||
// @ts-ignore
|
||||
import { ElasticsearchMetric } from '../../metrics';
|
||||
import { ElasticsearchResponse, ElasticsearchLegacySource } from '../../../../common/types/es';
|
||||
import { LegacyRequest } from '../../../types';
|
||||
|
||||
export function handleResponse(response) {
|
||||
const hits = get(response, 'hits.hits');
|
||||
export function handleResponse(response: ElasticsearchResponse) {
|
||||
const hits = response.hits?.hits;
|
||||
if (!hits) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// deduplicate any shards from earlier days with the same cluster state state_uuid
|
||||
const uniqueShards = new Set();
|
||||
const uniqueShards = new Set<string>();
|
||||
|
||||
// map into object with shard and source properties
|
||||
return hits.reduce((shards, hit) => {
|
||||
return hits.reduce((shards: Array<ElasticsearchLegacySource['shard']>, hit) => {
|
||||
const shard = hit._source.shard;
|
||||
|
||||
if (shard) {
|
||||
|
@ -37,9 +41,13 @@ export function handleResponse(response) {
|
|||
}
|
||||
|
||||
export function getShardAllocation(
|
||||
req,
|
||||
esIndexPattern,
|
||||
{ shardFilter, stateUuid, showSystemIndices = false }
|
||||
req: LegacyRequest,
|
||||
esIndexPattern: string,
|
||||
{
|
||||
shardFilter,
|
||||
stateUuid,
|
||||
showSystemIndices = false,
|
||||
}: { shardFilter: any; stateUuid: string; showSystemIndices: boolean }
|
||||
) {
|
||||
checkParam(esIndexPattern, 'esIndexPattern in elasticsearch/getShardAllocation');
|
||||
|
|
@ -9,10 +9,11 @@ import { merge } from 'lodash';
|
|||
import { checkParam } from '../error_missing_required';
|
||||
// @ts-ignore
|
||||
import { calculateAvailability } from '../calculate_availability';
|
||||
import { LegacyRequest, ElasticsearchResponse } from '../../types';
|
||||
import { LegacyRequest } from '../../types';
|
||||
import { ElasticsearchResponse } from '../../../common/types/es';
|
||||
|
||||
export function handleResponse(resp: ElasticsearchResponse) {
|
||||
const source = resp.hits?.hits[0]._source.kibana_stats;
|
||||
const source = resp.hits?.hits[0]?._source.kibana_stats;
|
||||
const kibana = source?.kibana;
|
||||
return merge(kibana, {
|
||||
availability: calculateAvailability(source?.timestamp),
|
||||
|
|
|
@ -9,7 +9,8 @@ import { merge } from 'lodash';
|
|||
import { checkParam } from '../error_missing_required';
|
||||
// @ts-ignore
|
||||
import { calculateAvailability } from '../calculate_availability';
|
||||
import { LegacyRequest, ElasticsearchResponse } from '../../types';
|
||||
import { LegacyRequest } from '../../types';
|
||||
import { ElasticsearchResponse } from '../../../common/types/es';
|
||||
|
||||
export function handleResponse(resp: ElasticsearchResponse) {
|
||||
const source = resp.hits?.hits[0]?._source?.logstash_stats;
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
import { createQuery } from '../create_query';
|
||||
// @ts-ignore
|
||||
import { LogstashMetric } from '../metrics';
|
||||
import { LegacyRequest, ElasticsearchResponse } from '../../types';
|
||||
import { LegacyRequest } from '../../types';
|
||||
import { ElasticsearchResponse } from '../../../common/types/es';
|
||||
|
||||
export async function getPipelineStateDocument(
|
||||
req: LegacyRequest,
|
||||
|
|
|
@ -7,11 +7,15 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import moment from 'moment';
|
||||
import { get, groupBy } from 'lodash';
|
||||
// @ts-ignore
|
||||
import { handleError } from '../../../../lib/errors/handle_error';
|
||||
// @ts-ignore
|
||||
import { prefixIndexPattern } from '../../../../lib/ccs_utils';
|
||||
import { INDEX_PATTERN_ELASTICSEARCH } from '../../../../../common/constants';
|
||||
import { ElasticsearchResponse, ElasticsearchSource } from '../../../../../common/types/es';
|
||||
import { LegacyRequest } from '../../../../types';
|
||||
|
||||
function getBucketScript(max, min) {
|
||||
function getBucketScript(max: string, min: string) {
|
||||
return {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
|
@ -23,7 +27,13 @@ function getBucketScript(max, min) {
|
|||
};
|
||||
}
|
||||
|
||||
function buildRequest(req, config, esIndexPattern) {
|
||||
function buildRequest(
|
||||
req: LegacyRequest,
|
||||
config: {
|
||||
get: (key: string) => string | undefined;
|
||||
},
|
||||
esIndexPattern: string
|
||||
) {
|
||||
const min = moment.utc(req.payload.timeRange.min).valueOf();
|
||||
const max = moment.utc(req.payload.timeRange.max).valueOf();
|
||||
const maxBucketSize = config.get('monitoring.ui.max_bucket_size');
|
||||
|
@ -168,7 +178,12 @@ function buildRequest(req, config, esIndexPattern) {
|
|||
};
|
||||
}
|
||||
|
||||
export function ccrRoute(server) {
|
||||
export function ccrRoute(server: {
|
||||
route: (p: any) => void;
|
||||
config: () => {
|
||||
get: (key: string) => string | undefined;
|
||||
};
|
||||
}) {
|
||||
server.route({
|
||||
method: 'POST',
|
||||
path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/ccr',
|
||||
|
@ -186,14 +201,14 @@ export function ccrRoute(server) {
|
|||
}),
|
||||
},
|
||||
},
|
||||
async handler(req) {
|
||||
async handler(req: LegacyRequest) {
|
||||
const config = server.config();
|
||||
const ccs = req.payload.ccs;
|
||||
const esIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_ELASTICSEARCH, ccs);
|
||||
|
||||
try {
|
||||
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring');
|
||||
const response = await callWithRequest(
|
||||
const response: ElasticsearchResponse = await callWithRequest(
|
||||
req,
|
||||
'search',
|
||||
buildRequest(req, config, esIndexPattern)
|
||||
|
@ -203,50 +218,72 @@ export function ccrRoute(server) {
|
|||
return { data: [] };
|
||||
}
|
||||
|
||||
const fullStats = get(response, 'hits.hits').reduce((accum, hit) => {
|
||||
const innerHits = get(hit, 'inner_hits.by_shard.hits.hits');
|
||||
const innerHitsSource = innerHits.map((innerHit) => get(innerHit, '_source.ccr_stats'));
|
||||
const grouped = groupBy(
|
||||
innerHitsSource,
|
||||
(stat) => `${stat.follower_index}:${stat.shard_id}`
|
||||
);
|
||||
const fullStats: {
|
||||
[key: string]: Array<NonNullable<ElasticsearchSource['ccr_stats']>>;
|
||||
} =
|
||||
response.hits?.hits.reduce((accum, hit) => {
|
||||
const innerHits = hit.inner_hits?.by_shard.hits?.hits ?? [];
|
||||
const innerHitsSource = innerHits.map(
|
||||
(innerHit) =>
|
||||
innerHit._source.ccr_stats as NonNullable<ElasticsearchSource['ccr_stats']>
|
||||
);
|
||||
const grouped = groupBy(
|
||||
innerHitsSource,
|
||||
(stat) => `${stat.follower_index}:${stat.shard_id}`
|
||||
);
|
||||
|
||||
return {
|
||||
...accum,
|
||||
...grouped,
|
||||
};
|
||||
}, {});
|
||||
return {
|
||||
...accum,
|
||||
...grouped,
|
||||
};
|
||||
}, {}) ?? {};
|
||||
|
||||
const buckets = get(response, 'aggregations.by_follower_index.buckets');
|
||||
const data = buckets.reduce((accum, bucket) => {
|
||||
const buckets = response.aggregations.by_follower_index.buckets;
|
||||
const data = buckets.reduce((accum: any, bucket: any) => {
|
||||
const leaderIndex = get(bucket, 'leader_index.buckets[0].key');
|
||||
const remoteCluster = get(
|
||||
bucket,
|
||||
'leader_index.buckets[0].remote_cluster.buckets[0].key'
|
||||
);
|
||||
const follows = remoteCluster ? `${leaderIndex} on ${remoteCluster}` : leaderIndex;
|
||||
const stat = {
|
||||
const stat: {
|
||||
[key: string]: any;
|
||||
shards: Array<{
|
||||
error?: string;
|
||||
opsSynced: number;
|
||||
syncLagTime: number;
|
||||
syncLagOps: number;
|
||||
}>;
|
||||
} = {
|
||||
id: bucket.key,
|
||||
index: bucket.key,
|
||||
follows,
|
||||
shards: [],
|
||||
error: undefined,
|
||||
opsSynced: undefined,
|
||||
syncLagTime: undefined,
|
||||
syncLagOps: undefined,
|
||||
};
|
||||
|
||||
stat.shards = get(bucket, 'by_shard_id.buckets').reduce((accum, shardBucket) => {
|
||||
const fullStat = get(fullStats[`${bucket.key}:${shardBucket.key}`], '[0]', {});
|
||||
const shardStat = {
|
||||
shardId: shardBucket.key,
|
||||
error: fullStat.read_exceptions.length
|
||||
? fullStat.read_exceptions[0].exception.type
|
||||
: null,
|
||||
opsSynced: get(shardBucket, 'ops_synced.value'),
|
||||
syncLagTime: fullStat.time_since_last_read_millis,
|
||||
syncLagOps: get(shardBucket, 'lag_ops.value'),
|
||||
syncLagOpsLeader: get(shardBucket, 'leader_lag_ops.value'),
|
||||
syncLagOpsFollower: get(shardBucket, 'follower_lag_ops.value'),
|
||||
};
|
||||
accum.push(shardStat);
|
||||
return accum;
|
||||
}, []);
|
||||
stat.shards = get(bucket, 'by_shard_id.buckets').reduce(
|
||||
(accum2: any, shardBucket: any) => {
|
||||
const fullStat = fullStats[`${bucket.key}:${shardBucket.key}`][0] ?? {};
|
||||
const shardStat = {
|
||||
shardId: shardBucket.key,
|
||||
error: fullStat.read_exceptions?.length
|
||||
? fullStat.read_exceptions[0].exception?.type
|
||||
: null,
|
||||
opsSynced: get(shardBucket, 'ops_synced.value'),
|
||||
syncLagTime: fullStat.time_since_last_read_millis,
|
||||
syncLagOps: get(shardBucket, 'lag_ops.value'),
|
||||
syncLagOpsLeader: get(shardBucket, 'leader_lag_ops.value'),
|
||||
syncLagOpsFollower: get(shardBucket, 'follower_lag_ops.value'),
|
||||
};
|
||||
accum2.push(shardStat);
|
||||
return accum2;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
stat.error = (stat.shards.find((shard) => shard.error) || {}).error;
|
||||
stat.opsSynced = stat.shards.reduce((sum, { opsSynced }) => sum + opsSynced, 0);
|
|
@ -4,15 +4,19 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
// @ts-ignore
|
||||
import { handleError } from '../../../../lib/errors/handle_error';
|
||||
// @ts-ignore
|
||||
import { prefixIndexPattern } from '../../../../lib/ccs_utils';
|
||||
// @ts-ignore
|
||||
import { getMetrics } from '../../../../lib/details/get_metrics';
|
||||
import { INDEX_PATTERN_ELASTICSEARCH } from '../../../../../common/constants';
|
||||
import { ElasticsearchResponse } from '../../../../../common/types/es';
|
||||
import { LegacyRequest } from '../../../../types';
|
||||
|
||||
function getFormattedLeaderIndex(leaderIndex) {
|
||||
function getFormattedLeaderIndex(leaderIndex: string) {
|
||||
let leader = leaderIndex;
|
||||
if (leader.includes(':')) {
|
||||
const leaderSplit = leader.split(':');
|
||||
|
@ -21,7 +25,7 @@ function getFormattedLeaderIndex(leaderIndex) {
|
|||
return leader;
|
||||
}
|
||||
|
||||
async function getCcrStat(req, esIndexPattern, filters) {
|
||||
async function getCcrStat(req: LegacyRequest, esIndexPattern: string, filters: unknown[]) {
|
||||
const min = moment.utc(req.payload.timeRange.min).valueOf();
|
||||
const max = moment.utc(req.payload.timeRange.max).valueOf();
|
||||
|
||||
|
@ -68,7 +72,7 @@ async function getCcrStat(req, esIndexPattern, filters) {
|
|||
return await callWithRequest(req, 'search', params);
|
||||
}
|
||||
|
||||
export function ccrShardRoute(server) {
|
||||
export function ccrShardRoute(server: { route: (p: any) => void; config: () => {} }) {
|
||||
server.route({
|
||||
method: 'POST',
|
||||
path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/ccr/{index}/shard/{shardId}',
|
||||
|
@ -88,7 +92,7 @@ export function ccrShardRoute(server) {
|
|||
}),
|
||||
},
|
||||
},
|
||||
async handler(req) {
|
||||
async handler(req: LegacyRequest) {
|
||||
const config = server.config();
|
||||
const index = req.params.index;
|
||||
const shardId = req.params.shardId;
|
||||
|
@ -120,7 +124,7 @@ export function ccrShardRoute(server) {
|
|||
];
|
||||
|
||||
try {
|
||||
const [metrics, ccrResponse] = await Promise.all([
|
||||
const [metrics, ccrResponse]: [unknown, ElasticsearchResponse] = await Promise.all([
|
||||
getMetrics(
|
||||
req,
|
||||
esIndexPattern,
|
||||
|
@ -133,18 +137,15 @@ export function ccrShardRoute(server) {
|
|||
getCcrStat(req, esIndexPattern, filters),
|
||||
]);
|
||||
|
||||
const stat = get(ccrResponse, 'hits.hits[0]._source.ccr_stats', {});
|
||||
const oldestStat = get(
|
||||
ccrResponse,
|
||||
'hits.hits[0].inner_hits.oldest.hits.hits[0]._source.ccr_stats',
|
||||
{}
|
||||
);
|
||||
const stat = ccrResponse.hits?.hits[0]?._source.ccr_stats ?? {};
|
||||
const oldestStat =
|
||||
ccrResponse.hits?.hits[0].inner_hits?.oldest.hits?.hits[0]?._source.ccr_stats ?? {};
|
||||
|
||||
return {
|
||||
metrics,
|
||||
stat,
|
||||
formattedLeader: getFormattedLeaderIndex(stat.leader_index),
|
||||
timestamp: get(ccrResponse, 'hits.hits[0]._source.timestamp'),
|
||||
formattedLeader: getFormattedLeaderIndex(stat.leader_index ?? ''),
|
||||
timestamp: ccrResponse.hits?.hits[0]?._source.timestamp,
|
||||
oldestStat,
|
||||
};
|
||||
} catch (err) {
|
|
@ -17,7 +17,6 @@ import { LicensingPluginSetup } from '../../licensing/server';
|
|||
import { PluginSetupContract as FeaturesPluginSetupContract } from '../../features/server';
|
||||
import { EncryptedSavedObjectsPluginSetup } from '../../encrypted_saved_objects/server';
|
||||
import { CloudSetup } from '../../cloud/server';
|
||||
import { ElasticsearchSource } from '../common/types/es';
|
||||
|
||||
export interface MonitoringLicenseService {
|
||||
refresh: () => Promise<any>;
|
||||
|
@ -118,26 +117,3 @@ export interface LegacyServer {
|
|||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface ElasticsearchResponse {
|
||||
hits?: {
|
||||
hits: ElasticsearchResponseHit[];
|
||||
total: {
|
||||
value: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface ElasticsearchResponseHit {
|
||||
_source: ElasticsearchSource;
|
||||
inner_hits?: {
|
||||
[field: string]: {
|
||||
hits?: {
|
||||
hits: ElasticsearchResponseHit[];
|
||||
total: {
|
||||
value: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue