diff --git a/x-pack/plugins/monitoring/public/lib/internal_monitoring_toasts.tsx b/x-pack/plugins/monitoring/public/lib/internal_monitoring_toasts.tsx
new file mode 100644
index 000000000000..b6ecb631d005
--- /dev/null
+++ b/x-pack/plugins/monitoring/public/lib/internal_monitoring_toasts.tsx
@@ -0,0 +1,123 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiSpacer, EuiLink } from '@elastic/eui';
+import { Legacy } from '../legacy_shims';
+import { toMountPoint } from '../../../../../src/plugins/kibana_react/public';
+import { isInSetupMode, toggleSetupMode } from './setup_mode';
+
+export interface MonitoringIndicesTypes {
+ legacyIndices: number;
+ metricbeatIndices: number;
+}
+
+const enterSetupModeLabel = () =>
+ i18n.translate('xpack.monitoring.internalMonitoringToast.enterSetupMode', {
+ defaultMessage: 'Enter setup mode',
+ });
+
+const learnMoreLabel = () =>
+ i18n.translate('xpack.monitoring.internalMonitoringToast.learnMoreAction', {
+ defaultMessage: 'Learn more',
+ });
+
+const showIfLegacyOnlyIndices = () => {
+ const { ELASTIC_WEBSITE_URL } = Legacy.shims.docLinks;
+ const toast = Legacy.shims.toastNotifications.addWarning({
+ title: toMountPoint(
+
+ ),
+ text: toMountPoint(
+
+
+ {i18n.translate('xpack.monitoring.internalMonitoringToast.description', {
+ defaultMessage: `It appears you are using "Legacy Collection" for Stack Monitoring.
+ This method of monitoring will no longer be supported in the next major release (8.0.0).
+ Please follow the steps in setup mode to start monitoring with Metricbeat.`,
+ })}
+
+
{
+ Legacy.shims.toastNotifications.remove(toast);
+ toggleSetupMode(true);
+ }}
+ >
+ {enterSetupModeLabel()}
+
+
+
+
+ {learnMoreLabel()}
+
+
+ ),
+ });
+};
+
+const showIfLegacyAndMetricbeatIndices = () => {
+ const { ELASTIC_WEBSITE_URL } = Legacy.shims.docLinks;
+ const toast = Legacy.shims.toastNotifications.addWarning({
+ title: toMountPoint(
+
+ ),
+ text: toMountPoint(
+
+
+ {i18n.translate('xpack.monitoring.internalAndMetricbeatMonitoringToast.description', {
+ defaultMessage: `It appears you are using both Metricbeat and "Legacy Collection" for Stack Monitoring.
+ In 8.0.0, you must use Metricbeat to collect monitoring data.
+ Please follow the steps in setup mode to migrate the rest of the monitoring to Metricbeat.`,
+ })}
+
+
{
+ Legacy.shims.toastNotifications.remove(toast);
+ toggleSetupMode(true);
+ }}
+ >
+ {enterSetupModeLabel()}
+
+
+
+
+ {learnMoreLabel()}
+
+
+ ),
+ });
+};
+
+export const showInternalMonitoringToast = ({
+ legacyIndices,
+ metricbeatIndices,
+}: MonitoringIndicesTypes) => {
+ if (isInSetupMode()) {
+ return;
+ }
+
+ if (legacyIndices && !metricbeatIndices) {
+ showIfLegacyOnlyIndices();
+ } else if (legacyIndices && metricbeatIndices) {
+ showIfLegacyAndMetricbeatIndices();
+ }
+};
diff --git a/x-pack/plugins/monitoring/public/services/clusters.js b/x-pack/plugins/monitoring/public/services/clusters.js
index 5173984dbe86..7f772ac1e1bc 100644
--- a/x-pack/plugins/monitoring/public/services/clusters.js
+++ b/x-pack/plugins/monitoring/public/services/clusters.js
@@ -7,6 +7,7 @@
import { ajaxErrorHandlersProvider } from '../lib/ajax_error_handler';
import { Legacy } from '../legacy_shims';
import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../common/constants';
+import { showInternalMonitoringToast } from '../lib/internal_monitoring_toasts';
import { showSecurityToast } from '../alerts/lib/security_toasts';
function formatClusters(clusters) {
@@ -21,6 +22,7 @@ function formatCluster(cluster) {
}
let once = false;
+let inTransit = false;
export function monitoringClustersProvider($injector) {
return (clusterUuid, ccs, codePaths) => {
@@ -63,19 +65,39 @@ export function monitoringClustersProvider($injector) {
});
}
- if (!once) {
+ function ensureMetricbeatEnabled() {
+ if (Legacy.shims.isCloud) {
+ return Promise.resolve();
+ }
+
+ return $http
+ .get('../api/monitoring/v1/elasticsearch_settings/check/internal_monitoring')
+ .then(({ data }) => {
+ showInternalMonitoringToast({
+ legacyIndices: data.legacy_indices,
+ metricbeatIndices: data.mb_indices,
+ });
+ })
+ .catch((err) => {
+ const Private = $injector.get('Private');
+ const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider);
+ return ajaxErrorHandlers(err);
+ });
+ }
+
+ if (!once && !inTransit) {
+ inTransit = true;
return getClusters().then((clusters) => {
if (clusters.length) {
- return ensureAlertsEnabled()
- .then(({ data }) => {
+ Promise.all([ensureAlertsEnabled(), ensureMetricbeatEnabled()])
+ .then(([{ data }]) => {
showSecurityToast(data);
once = true;
- return clusters;
})
.catch(() => {
// Intentionally swallow the error as this will retry the next page load
- return clusters;
- });
+ })
+ .finally(() => (inTransit = false));
}
return clusters;
});
diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts
new file mode 100644
index 000000000000..4473d824c9e3
--- /dev/null
+++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts
@@ -0,0 +1,85 @@
+/*
+ * 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 { RequestHandlerContext } from 'kibana/server';
+// @ts-ignore
+import { getIndexPatterns } from '../../../../../lib/cluster/get_index_patterns';
+// @ts-ignore
+import { handleError } from '../../../../../lib/errors';
+import { RouteDependencies } from '../../../../../types';
+
+const queryBody = {
+ size: 0,
+ aggs: {
+ types: {
+ terms: {
+ field: '_index',
+ size: 10,
+ },
+ },
+ },
+};
+
+const checkLatestMonitoringIsLegacy = async (context: RequestHandlerContext, index: string) => {
+ const { client: esClient } = context.core.elasticsearch.legacy;
+ const result = await esClient.callAsCurrentUser('search', {
+ index,
+ body: queryBody,
+ });
+
+ const { aggregations } = result;
+ const counts = {
+ legacyIndicesCount: 0,
+ mbIndicesCount: 0,
+ };
+
+ if (!aggregations) {
+ return counts;
+ }
+
+ const {
+ types: { buckets },
+ } = aggregations;
+ counts.mbIndicesCount = buckets.filter(({ key }: { key: string }) => key.includes('-mb-')).length;
+
+ counts.legacyIndicesCount = buckets.length - counts.mbIndicesCount;
+ return counts;
+};
+
+export function internalMonitoringCheckRoute(server: unknown, npRoute: RouteDependencies) {
+ npRoute.router.get(
+ {
+ path: '/api/monitoring/v1/elasticsearch_settings/check/internal_monitoring',
+ validate: false,
+ },
+ async (context, _request, response) => {
+ try {
+ const typeCount = {
+ legacy_indices: 0,
+ mb_indices: 0,
+ };
+
+ const { esIndexPattern, kbnIndexPattern, lsIndexPattern } = getIndexPatterns(server);
+ const indexCounts = await Promise.all([
+ checkLatestMonitoringIsLegacy(context, esIndexPattern),
+ checkLatestMonitoringIsLegacy(context, kbnIndexPattern),
+ checkLatestMonitoringIsLegacy(context, lsIndexPattern),
+ ]);
+
+ indexCounts.forEach((counts) => {
+ typeCount.legacy_indices += counts.legacyIndicesCount;
+ typeCount.mb_indices += counts.mbIndicesCount;
+ });
+
+ return response.ok({
+ body: typeCount,
+ });
+ } catch (err) {
+ throw handleError(err);
+ }
+ }
+ );
+}
diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.js
index d7ef71efc0b5..906057d22186 100644
--- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.js
+++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.js
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
+export { internalMonitoringCheckRoute } from './check/internal_monitoring';
export { clusterSettingsCheckRoute } from './check/cluster';
export { nodesSettingsCheckRoute } from './check/nodes';
export { setCollectionEnabledRoute } from './set/collection_enabled';
diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/ui.js b/x-pack/plugins/monitoring/server/routes/api/v1/ui.js
index de0213ec8468..e8daf5258243 100644
--- a/x-pack/plugins/monitoring/server/routes/api/v1/ui.js
+++ b/x-pack/plugins/monitoring/server/routes/api/v1/ui.js
@@ -20,6 +20,7 @@ export {
ccrShardRoute,
} from './elasticsearch';
export {
+ internalMonitoringCheckRoute,
clusterSettingsCheckRoute,
nodesSettingsCheckRoute,
setCollectionEnabledRoute,
diff --git a/x-pack/test/functional/apps/monitoring/time_filter.js b/x-pack/test/functional/apps/monitoring/time_filter.js
index d7ffdb4a7900..11557d995218 100644
--- a/x-pack/test/functional/apps/monitoring/time_filter.js
+++ b/x-pack/test/functional/apps/monitoring/time_filter.js
@@ -7,6 +7,8 @@
import expect from '@kbn/expect';
import { getLifecycleMethods } from './_get_lifecycle_methods';
+const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
+
export default function ({ getService, getPageObjects }) {
const PageObjects = getPageObjects(['header', 'timePicker']);
const testSubjects = getService('testSubjects');
@@ -35,6 +37,11 @@ export default function ({ getService, getPageObjects }) {
});
it('should send another request when changing the time picker', async () => {
+ /**
+ * TODO: The value should either be removed or lowered after:
+ * https://github.com/elastic/kibana/issues/72997 is resolved
+ */
+ await delay(3000);
await PageObjects.timePicker.setAbsoluteRange(
'Aug 15, 2016 @ 21:00:00.000',
'Aug 16, 2016 @ 00:00:00.000'