Skip to content

Commit

Permalink
[ML] Lazy ml node UI improvements (#90455) (#90787)
Browse files Browse the repository at this point in the history
* [ML] Lazy ml node UI improvements

* fixing test

* adding awaitingMlNodeAllocation to default datafeed response

* changing datafeed icon when node is not assigned

* updating text

Co-authored-by: Kibana Machine <[email protected]>

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
jgowdyelastic and kibanamachine authored Feb 10, 2021
1 parent 7879757 commit 53b8f45
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 18 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/ml/common/types/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export interface KibanaObjectResponse extends ResultItem {

export interface DatafeedResponse extends ResultItem {
started: boolean;
awaitingMlNodeAllocation?: boolean;
error?: ErrorType;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import React, { Fragment, FC } from 'react';

import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { isCloud } from '../../services/ml_server_info';
import { lazyMlNodesAvailable } from '../../ml_nodes_check';

interface Props {
jobCount: number;
}

export const JobsAwaitingNodeWarning: FC<Props> = ({ jobCount }) => {
if (isCloud() === false || jobCount === 0) {
if (lazyMlNodesAvailable() === false || jobCount === 0) {
return null;
}

Expand All @@ -26,7 +26,7 @@ export const JobsAwaitingNodeWarning: FC<Props> = ({ jobCount }) => {
title={
<FormattedMessage
id="xpack.ml.jobsAwaitingNodeWarning.title"
defaultMessage="Awaiting ML node provisioning"
defaultMessage="Awaiting machine learning node"
/>
}
color="primary"
Expand All @@ -35,7 +35,7 @@ export const JobsAwaitingNodeWarning: FC<Props> = ({ jobCount }) => {
<div>
<FormattedMessage
id="xpack.ml.jobsAwaitingNodeWarning.noMLNodesAvailableDescription"
defaultMessage="There {jobCount, plural, one {is} other {are}} {jobCount, plural, one {# job} other {# jobs}} waiting to be started while ML nodes are being provisioned."
defaultMessage="There {jobCount, plural, one {is} other {are}} {jobCount, plural, one {# job} other {# jobs}} waiting for machine learning nodes to start."
values={{
jobCount,
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,24 @@ import React, { Fragment, FC } from 'react';
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { JobType } from '../../../../common/types/saved_objects';
import { lazyMlNodesAvailable } from '../../ml_nodes_check';

interface Props {
jobType: JobType;
}

export const NewJobAwaitingNodeWarning: FC<Props> = () => {
if (lazyMlNodesAvailable() === false) {
return null;
}

return (
<Fragment>
<EuiCallOut
title={
<FormattedMessage
id="xpack.ml.jobsAwaitingNodeWarning.title"
defaultMessage="Awaiting ML node provisioning"
defaultMessage="Awaiting machine learning node"
/>
}
color="primary"
Expand All @@ -31,7 +36,7 @@ export const NewJobAwaitingNodeWarning: FC<Props> = () => {
<div>
<FormattedMessage
id="xpack.ml.newJobAwaitingNodeWarning.noMLNodesAvailableDescription"
defaultMessage="Job cannot be started straight away, an ML node needs to be started. This will happen automatically."
defaultMessage="There are currently no nodes that can run the job, therefore it will remain in OPENING state until an appropriate node becomes available."
/>
</div>
</EuiCallOut>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { ModuleJobUI } from '../page';
import { SETUP_RESULTS_WIDTH } from './module_jobs';
import { tabColor } from '../../../../../../common/util/group_color_utils';
import { JobOverride } from '../../../../../../common/types/modules';
import { JobOverride, DatafeedResponse } from '../../../../../../common/types/modules';
import { extractErrorMessage } from '../../../../../../common/util/errors';

interface JobItemProps {
Expand Down Expand Up @@ -151,8 +151,8 @@ export const JobItem: FC<JobItemProps> = memo(

<EuiFlexItem grow={false}>
<EuiIcon
type={datafeedResult.started ? 'check' : 'cross'}
color={datafeedResult.started ? 'secondary' : 'danger'}
type={getDatafeedStartedIcon(datafeedResult).type}
color={getDatafeedStartedIcon(datafeedResult).color}
size="m"
aria-label={
setupResult.success
Expand All @@ -172,3 +172,14 @@ export const JobItem: FC<JobItemProps> = memo(
);
}
);

function getDatafeedStartedIcon({
awaitingMlNodeAllocation,
success,
}: DatafeedResponse): { type: string; color: string } {
if (awaitingMlNodeAllocation === true) {
return { type: 'alert', color: 'warning' };
}

return success ? { type: 'check', color: 'secondary' } : { type: 'cross', color: 'danger' };
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { TimeRange } from '../common/components';
import { JobId } from '../../../../../common/types/anomaly_detection_jobs';
import { ML_PAGES } from '../../../../../common/constants/ml_url_generator';
import { TIME_FORMAT } from '../../../../../common/constants/time_format';
import { JobsAwaitingNodeWarning } from '../../../components/jobs_awaiting_node_warning';

export interface ModuleJobUI extends ModuleJob {
datafeedResult?: DatafeedResponse;
Expand Down Expand Up @@ -84,6 +85,7 @@ export const Page: FC<PageProps> = ({ moduleId, existingGroupIds }) => {
const [saveState, setSaveState] = useState<SAVE_STATE>(SAVE_STATE.NOT_SAVED);
const [resultsUrl, setResultsUrl] = useState<string>('');
const [existingGroups, setExistingGroups] = useState(existingGroupIds);
const [jobsAwaitingNodeCount, setJobsAwaitingNodeCount] = useState(0);
// #endregion

const {
Expand Down Expand Up @@ -204,9 +206,19 @@ export const Page: FC<PageProps> = ({ moduleId, existingGroupIds }) => {
});

setResultsUrl(url);
const failedJobsCount = jobsResponse.reduce((count, { success }) => {
return success ? count : count + 1;
}, 0);
const failedJobsCount = jobsResponse.reduce(
(count, { success }) => (success ? count : count + 1),
0
);

const lazyJobsCount = datafeedsResponse.reduce(
(count, { awaitingMlNodeAllocation }) =>
awaitingMlNodeAllocation === true ? count + 1 : count,
0
);

setJobsAwaitingNodeCount(lazyJobsCount);

setSaveState(
failedJobsCount === 0
? SAVE_STATE.SAVED
Expand Down Expand Up @@ -291,6 +303,8 @@ export const Page: FC<PageProps> = ({ moduleId, existingGroupIds }) => {
</>
)}

{jobsAwaitingNodeCount > 0 && <JobsAwaitingNodeWarning jobCount={jobsAwaitingNodeCount} />}

<EuiFlexGroup wrap={true} gutterSize="m">
<EuiFlexItem grow={1}>
<EuiPanel grow={false}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ export function mlNodesAvailable() {
return mlNodeCount !== 0 || lazyMlNodeCount !== 0;
}

export function currentMlNodesAvailable() {
return mlNodeCount !== 0;
}

export function lazyMlNodesAvailable() {
return lazyMlNodeCount !== 0;
}

export function permissionToViewMlNodeCount() {
return userHasPermissionToViewMlNodeCount;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export {
checkMlNodesAvailable,
getMlNodeCount,
mlNodesAvailable,
lazyMlNodesAvailable,
permissionToViewMlNodeCount,
} from './check_ml_nodes';
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
import { JOB_STATE } from '../../../../../common/constants/states';
import { FORECAST_DURATION_MAX_DAYS } from './forecasting_modal';
import { ForecastProgress } from './forecast_progress';
import { mlNodesAvailable } from '../../../ml_nodes_check/check_ml_nodes';
import { currentMlNodesAvailable } from '../../../ml_nodes_check/check_ml_nodes';
import {
checkPermission,
createPermissionFailureMessage,
Expand All @@ -41,7 +41,7 @@ function getRunInputDisabledState(job, isForecastRequested) {
// - No canForecastJob permission
// - Job is not in an OPENED or CLOSED state
// - A new forecast has been requested
if (mlNodesAvailable() === false) {
if (currentMlNodesAvailable() === false) {
return {
isDisabled: true,
isDisabledToolTipText: i18n.translate(
Expand Down
28 changes: 24 additions & 4 deletions x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ export class DataRecognizer {
const startedDatafeed = startResults[df.id];
if (startedDatafeed !== undefined) {
df.started = startedDatafeed.started;
df.awaitingMlNodeAllocation = startedDatafeed.awaitingMlNodeAllocation;
if (startedDatafeed.error !== undefined) {
df.error = startedDatafeed.error;
}
Expand Down Expand Up @@ -749,9 +750,20 @@ export class DataRecognizer {
datafeeds.map(async (datafeed) => {
try {
await this.saveDatafeed(datafeed);
return { id: datafeed.id, success: true, started: false };
return {
id: datafeed.id,
success: true,
started: false,
awaitingMlNodeAllocation: false,
};
} catch ({ body }) {
return { id: datafeed.id, success: false, started: false, error: body };
return {
id: datafeed.id,
success: false,
started: false,
awaitingMlNodeAllocation: false,
error: body,
};
}
})
);
Expand Down Expand Up @@ -811,11 +823,18 @@ export class DataRecognizer {
duration.end = (end as unknown) as string;
}

await this._mlClient.startDatafeed({
const {
body: { started, node },
} = await this._mlClient.startDatafeed<{
started: boolean;
node: string;
}>({
datafeed_id: datafeed.id,
...duration,
});
result.started = true;

result.started = started;
result.awaitingMlNodeAllocation = node?.length === 0;
} catch ({ body }) {
result.started = false;
result.error = body;
Expand Down Expand Up @@ -845,6 +864,7 @@ export class DataRecognizer {
if (d.id === d2.id) {
d.success = d2.success;
d.started = d2.started;
d.awaitingMlNodeAllocation = d2.awaitingMlNodeAllocation;
if (d2.error !== undefined) {
d.error = d2.error;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,7 @@ export default ({ getService }: FtrProviderContext) => {
const expectedRspDatafeeds = sortBy(
testData.expected.jobs.map((job) => {
return {
awaitingMlNodeAllocation: false,
id: `datafeed-${job.jobId}`,
success: true,
started: testData.requestBody.startDatafeed,
Expand Down

0 comments on commit 53b8f45

Please sign in to comment.