mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
support force stop
This commit is contained in:
parent
86c265f1b2
commit
1b1a3b2272
5 changed files with 113 additions and 4 deletions
|
@ -135,10 +135,13 @@ export function trainedModelsApiProvider(httpService: HttpService) {
|
|||
});
|
||||
},
|
||||
|
||||
stopModelAllocation(modelId: string) {
|
||||
stopModelAllocation(modelId: string, options: { force: boolean } = { force: false }) {
|
||||
const force = options?.force;
|
||||
|
||||
return httpService.http<{ acknowledge: boolean }>({
|
||||
path: `${apiBasePath}/trained_models/${modelId}/deployment/_stop`,
|
||||
method: 'POST',
|
||||
query: { force },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { EuiConfirmModal } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import type { OverlayStart } from 'kibana/public';
|
||||
import type { ModelItem } from './models_list';
|
||||
import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface ForceStopModelConfirmDialogProps {
|
||||
model: ModelItem;
|
||||
onCancel: () => void;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
export const ForceStopModelConfirmDialog: FC<ForceStopModelConfirmDialogProps> = ({
|
||||
model,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}) => {
|
||||
return (
|
||||
<EuiConfirmModal
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.trainedModels.modelsList.forceStopDialog.title"
|
||||
defaultMessage="Force stop model {modelId}?"
|
||||
values={{ modelId: model.model_id }}
|
||||
/>
|
||||
}
|
||||
onCancel={onCancel}
|
||||
onConfirm={onConfirm}
|
||||
cancelButtonText={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.trainedModels.modelsList.forceStopDialog.cancelText"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
}
|
||||
confirmButtonText={
|
||||
<FormattedMessage
|
||||
id="xpack.ml.trainedModels.modelsList.forceStopDialog.confirmText"
|
||||
defaultMessage="Stop"
|
||||
/>
|
||||
}
|
||||
buttonColor="danger"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.trainedModels.modelsList.forceStopDialog.confirmText"
|
||||
defaultMessage="Selected model has associated pipelines: "
|
||||
/>
|
||||
<ul>
|
||||
{Object.keys(model.pipelines!)
|
||||
.sort()
|
||||
.map((pipelineName) => {
|
||||
return <li key={pipelineName}>{pipelineName}</li>;
|
||||
})}
|
||||
</ul>
|
||||
</EuiConfirmModal>
|
||||
);
|
||||
};
|
||||
|
||||
export const getUserConfirmationProvider =
|
||||
(overlays: OverlayStart) => async (forceStopModel: ModelItem) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const modalSession = overlays.openModal(
|
||||
toMountPoint(
|
||||
<ForceStopModelConfirmDialog
|
||||
model={forceStopModel}
|
||||
onCancel={() => {
|
||||
modalSession.close();
|
||||
resolve(false);
|
||||
}}
|
||||
onConfirm={() => {
|
||||
modalSession.close();
|
||||
resolve(true);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -53,6 +53,7 @@ import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter';
|
|||
import { FIELD_FORMAT_IDS } from '../../../../../../../src/plugins/field_formats/common';
|
||||
import { useRefresh } from '../../routing/use_refresh';
|
||||
import { DEPLOYMENT_STATE } from '../../../../common/constants/trained_models';
|
||||
import { getUserConfirmationProvider } from './force_stop_dialog';
|
||||
|
||||
type Stats = Omit<TrainedModelStat, 'model_id'>;
|
||||
|
||||
|
@ -80,6 +81,7 @@ export const ModelsList: FC = () => {
|
|||
const {
|
||||
services: {
|
||||
application: { navigateToUrl, capabilities },
|
||||
overlays,
|
||||
},
|
||||
} = useMlKibana();
|
||||
const urlLocator = useMlLocator()!;
|
||||
|
@ -110,6 +112,8 @@ export const ModelsList: FC = () => {
|
|||
{}
|
||||
);
|
||||
|
||||
const getUserConfirmation = useMemo(() => getUserConfirmationProvider(overlays), []);
|
||||
|
||||
const navigateToPath = useNavigateToPath();
|
||||
|
||||
const isBuiltInModel = useCallback(
|
||||
|
@ -418,13 +422,21 @@ export const ModelsList: FC = () => {
|
|||
available: (item) => item.model_type === 'pytorch',
|
||||
enabled: (item) =>
|
||||
!isLoading &&
|
||||
!isPopulatedObject(item.pipelines) &&
|
||||
isPopulatedObject(item.stats?.deployment_stats) &&
|
||||
item.stats?.deployment_stats?.state !== DEPLOYMENT_STATE.STOPPING,
|
||||
onClick: async (item) => {
|
||||
const requireForceStop = isPopulatedObject(item.pipelines);
|
||||
|
||||
if (requireForceStop) {
|
||||
const hasUserApproved = await getUserConfirmation(item);
|
||||
if (!hasUserApproved) return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await trainedModelsApiService.stopModelAllocation(item.model_id);
|
||||
await trainedModelsApiService.stopModelAllocation(item.model_id, {
|
||||
force: requireForceStop,
|
||||
});
|
||||
displaySuccessToast(
|
||||
i18n.translate('xpack.ml.trainedModels.modelsList.stopSuccess', {
|
||||
defaultMessage: 'Deployment for "{modelId}" has been stopped successfully.',
|
||||
|
|
|
@ -51,7 +51,7 @@ describe('check_capabilities', () => {
|
|||
);
|
||||
const { capabilities } = await getCapabilities();
|
||||
const count = Object.keys(capabilities).length;
|
||||
expect(count).toBe(31);
|
||||
expect(count).toBe(32);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -101,6 +101,7 @@ describe('check_capabilities', () => {
|
|||
expect(capabilities.canCreateDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canStartStopDataFrameAnalytics).toBe(false);
|
||||
expect(capabilities.canCreateMlAlerts).toBe(false);
|
||||
expect(capabilities.canViewMlNodes).toBe(false);
|
||||
});
|
||||
|
||||
test('full capabilities', async () => {
|
||||
|
@ -146,6 +147,7 @@ describe('check_capabilities', () => {
|
|||
expect(capabilities.canDeleteDataFrameAnalytics).toBe(true);
|
||||
expect(capabilities.canCreateDataFrameAnalytics).toBe(true);
|
||||
expect(capabilities.canStartStopDataFrameAnalytics).toBe(true);
|
||||
expect(capabilities.canViewMlNodes).toBe(true);
|
||||
});
|
||||
|
||||
test('upgrade in progress with full capabilities', async () => {
|
||||
|
|
|
@ -16,6 +16,7 @@ import { modelsProvider } from '../models/data_frame_analytics';
|
|||
import { TrainedModelConfigResponse } from '../../common/types/trained_models';
|
||||
import { memoryOverviewServiceProvider } from '../models/memory_overview';
|
||||
import { mlLog } from '../lib/log';
|
||||
import { forceQuerySchema } from './schemas/anomaly_detectors_schema';
|
||||
|
||||
export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization) {
|
||||
/**
|
||||
|
@ -262,6 +263,7 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization)
|
|||
path: '/api/ml/trained_models/{modelId}/deployment/_stop',
|
||||
validate: {
|
||||
params: modelIdSchema,
|
||||
query: forceQuerySchema,
|
||||
},
|
||||
options: {
|
||||
tags: ['access:ml:canGetDataFrameAnalytics'],
|
||||
|
@ -272,6 +274,7 @@ export function trainedModelsRoutes({ router, routeGuard }: RouteInitialization)
|
|||
const { modelId } = request.params;
|
||||
const { body } = await mlClient.stopTrainedModelDeployment({
|
||||
model_id: modelId,
|
||||
force: request.query.force ?? false,
|
||||
});
|
||||
return response.ok({
|
||||
body,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue